bookmark_view.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. const { form, button, div, h2, p, section, input, label, textarea, br, a, ul, li } = require("../server/node_modules/hyperaxe");
  2. const { template, i18n } = require('./main_views');
  3. const moment = require("../server/node_modules/moment");
  4. const { config } = require('../server/SSB_server.js');
  5. const userId = config.keys.id
  6. const renderBookmarkActions = (filter, bookmark) => {
  7. return filter === 'mine' ? div({ class: "bookmark-actions" },
  8. form({ method: "GET", action: `/bookmarks/edit/${encodeURIComponent(bookmark.id)}` },
  9. button({ class: "update-btn", type: "submit" }, i18n.bookmarkUpdateButton)
  10. ),
  11. form({ method: "POST", action: `/bookmarks/delete/${encodeURIComponent(bookmark.id)}` },
  12. button({ class: "delete-btn", type: "submit" }, i18n.bookmarkDeleteButton)
  13. )
  14. ) : null;
  15. };
  16. const renderBookmarkList = (filteredBookmarks, filter) => {
  17. return filteredBookmarks.length > 0
  18. ? filteredBookmarks.map(bookmark =>
  19. div({ class: "bookmark-item" },
  20. renderBookmarkActions(filter, bookmark),
  21. form({ method: "GET", action: `/bookmarks/${encodeURIComponent(bookmark.id)}` },
  22. button({ type: "submit", class: "filter-btn" }, i18n.viewDetails)
  23. ),br,
  24. h2(bookmark.title),
  25. p(bookmark.description),
  26. label(bookmark.url ? a({ href: bookmark.url, target: "_blank", class: "bookmark-url" }, bookmark.url) : null), br,
  27. p(`${i18n.bookmarkCreatedAt}: ${moment(bookmark.createdAt).format('YYYY/MM/DD HH:mm:ss')}`),
  28. p(`${i18n.bookmarkAuthor}: `, a({ href: `/author/${encodeURIComponent(bookmark.author)}` }, bookmark.author)),
  29. bookmark.category?.trim() ? p(`${i18n.bookmarkCategory}: ${bookmark.category}`) : null,
  30. p(`${i18n.bookmarkLastVisit}: ${moment(bookmark.lastVisit).format('YYYY/MM/DD HH:mm:ss') || i18n.noLastVisit}`),
  31. bookmark.tags?.length
  32. ? div(bookmark.tags.map(tag =>
  33. a({ href: `/search?query=%23${encodeURIComponent(tag)}`, class: "tag-link", style: "margin-right: 0.8em; margin-bottom: 0.5em;" }, `#${tag}`)
  34. ))
  35. : null,
  36. div({ class: "voting-buttons" },
  37. ['interesting','necessary','funny','disgusting','sensible','propaganda','adultOnly','boring','confusing','inspiring','spam'].map(category =>
  38. form({ method: "POST", action: `/bookmarks/opinions/${encodeURIComponent(bookmark.id)}/${category}` },
  39. button({ class: "vote-btn" }, `${i18n[`vote${category.charAt(0).toUpperCase() + category.slice(1)}`]} [${bookmark.opinions?.[category] || 0}]`)
  40. )
  41. )
  42. )
  43. )
  44. )
  45. : i18n.nobookmarks;
  46. };
  47. const renderBookmarkForm = (filter, bookmarkId, bookmarkToEdit, tags) => {
  48. return div({ class: "div-center bookmark-form" },
  49. form(
  50. {
  51. action: filter === 'edit'
  52. ? `/bookmarks/update/${encodeURIComponent(bookmarkId)}`
  53. : "/bookmarks/create",
  54. method: "POST"
  55. },
  56. label(i18n.bookmarkUrlLabel), br,
  57. input({ type: "url", name: "url", id: "url", required: true, placeholder: i18n.bookmarkUrlPlaceholder, value: filter === 'edit' ? bookmarkToEdit.url : '' }), br, br,
  58. label(i18n.bookmarkDescriptionLabel),
  59. textarea({ name: "description", id: "description", placeholder: i18n.bookmarkDescriptionPlaceholder, value: filter === 'edit' ? bookmarkToEdit.description : '' }), br, br,
  60. label(i18n.bookmarkTagsLabel),
  61. input({ type: "text", name: "tags", id: "tags", placeholder: i18n.bookmarkTagsPlaceholder, value: filter === 'edit' ? tags.join(', ') : '' }), br, br,
  62. label(i18n.bookmarkCategoryLabel),
  63. input({ type: "text", name: "category", id: "category", placeholder: i18n.bookmarkCategoryPlaceholder, value: filter === 'edit' ? bookmarkToEdit.category : '' }), br, br,
  64. label(i18n.bookmarkLastVisitLabel),
  65. input({ type: "datetime-local", name: "lastVisit", value: filter === 'edit' ? moment(bookmarkToEdit.lastVisit).format('YYYY-MM-DDTHH:mm:ss') : '' }), br, br,
  66. button({ type: "submit" }, filter === 'edit' ? i18n.bookmarkUpdateButton : i18n.bookmarkCreateButton)
  67. )
  68. );
  69. };
  70. exports.bookmarkView = async (bookmarks, filter, bookmarkId) => {
  71. const title = filter === 'mine' ? i18n.bookmarkMineSectionTitle :
  72. filter === 'create' ? i18n.bookmarkCreateSectionTitle :
  73. filter === 'edit' ? i18n.bookmarkUpdateSectionTitle :
  74. filter === 'internal' ? i18n.bookmarkInternalTitle :
  75. filter === 'external' ? i18n.bookmarkExternalTitle :
  76. filter === 'top' ? i18n.bookmarkTopTitle :
  77. filter === 'recent' ? i18n.bookmarkRecentTitle :
  78. i18n.bookmarkAllSectionTitle;
  79. const sectionTitle = title;
  80. const now = Date.now();
  81. let filteredBookmarks = (filter === 'mine')
  82. ? bookmarks.filter(bookmark => String(bookmark.author).trim() === String(userId).trim())
  83. : (filter === 'internal')
  84. ? bookmarks.filter(bookmark => bookmark.tags?.includes('internal'))
  85. : (filter === 'external')
  86. ? bookmarks.filter(bookmark => bookmark.tags?.includes('external'))
  87. : (filter === 'recent')
  88. ? bookmarks.filter(bookmark => new Date(bookmark.createdAt).getTime() >= (now - 24 * 60 * 60 * 1000))
  89. : bookmarks;
  90. if (filter === 'top') {
  91. filteredBookmarks = [...filteredBookmarks].sort((a, b) => {
  92. const sumA = Object.values(a.opinions || {}).reduce((s, n) => s + n, 0);
  93. const sumB = Object.values(b.opinions || {}).reduce((s, n) => s + n, 0);
  94. return sumB - sumA;
  95. });
  96. } else {
  97. filteredBookmarks = [...filteredBookmarks].sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
  98. }
  99. const bookmarkToEdit = bookmarks.find(b => b.id === bookmarkId);
  100. const tags = bookmarkToEdit && Array.isArray(bookmarkToEdit.tags) ? bookmarkToEdit.tags : [];
  101. return template(
  102. title,
  103. section(
  104. div({ class: "tags-header" },
  105. h2(sectionTitle),
  106. p(i18n.bookmarkDescription)
  107. ),
  108. div({ class: "filters" },
  109. form({ method: "GET", action: "/bookmarks" },
  110. button({ type: "submit", name: "filter", value: "all", class: filter === 'all' ? 'filter-btn active' : 'filter-btn' }, i18n.bookmarkFilterAll),
  111. button({ type: "submit", name: "filter", value: "mine", class: filter === 'mine' ? 'filter-btn active' : 'filter-btn' }, i18n.bookmarkFilterMine),
  112. button({ type: "submit", name: "filter", value: "internal", class: filter === 'internal' ? 'filter-btn active' : 'filter-btn' }, i18n.bookmarkFilterInternal),
  113. button({ type: "submit", name: "filter", value: "external", class: filter === 'external' ? 'filter-btn active' : 'filter-btn' }, i18n.bookmarkFilterExternal),
  114. button({ type: "submit", name: "filter", value: "top", class: filter === 'top' ? 'filter-btn active' : 'filter-btn' }, i18n.bookmarkFilterTop),
  115. button({ type: "submit", name: "filter", value: "recent", class: filter === 'recent' ? 'filter-btn active' : 'filter-btn' }, i18n.bookmarkFilterRecent),
  116. button({ type: "submit", name: "filter", value: "create", class: "create-button" }, i18n.bookmarkCreateButton)
  117. )
  118. )
  119. ),
  120. section(
  121. (filter === 'edit' || filter === 'create')
  122. ? renderBookmarkForm(filter, bookmarkId, bookmarkToEdit, tags)
  123. : div({ class: "bookmark-list" }, renderBookmarkList(filteredBookmarks, filter))
  124. )
  125. );
  126. };
  127. exports.singleBookmarkView = async (bookmark, filter) => {
  128. const isAuthor = bookmark.author === userId;
  129. const hasOpinions = Object.keys(bookmark.opinions || {}).length > 0;
  130. return template(
  131. i18n.bookmarkTitle,
  132. section(
  133. div({ class: "filters" },
  134. form({ method: "GET", action: "/bookmarks" },
  135. button({ type: "submit", name: "filter", value: "all", class: filter === 'all' ? 'filter-btn active' : 'filter-btn' }, i18n.bookmarkFilterAll),
  136. button({ type: "submit", name: "filter", value: "mine", class: filter === 'mine' ? 'filter-btn active' : 'filter-btn' }, i18n.bookmarkFilterMine),
  137. button({ type: "submit", name: "filter", value: "internal", class: filter === 'internal' ? 'filter-btn active' : 'filter-btn' }, i18n.bookmarkFilterInternal),
  138. button({ type: "submit", name: "filter", value: "external", class: filter === 'external' ? 'filter-btn active' : 'filter-btn' }, i18n.bookmarkFilterExternal),
  139. button({ type: "submit", name: "filter", value: "top", class: filter === 'top' ? 'filter-btn active' : 'filter-btn' }, i18n.bookmarkFilterTop),
  140. button({ type: "submit", name: "filter", value: "recent", class: filter === 'recent' ? 'filter-btn active' : 'filter-btn' }, i18n.bookmarkFilterRecent),
  141. button({ type: "submit", name: "filter", value: "create", class: "create-button" }, i18n.bookmarkCreateButton)
  142. )
  143. ),
  144. div({ class: "tags-header" },
  145. p(bookmark.description),
  146. label(bookmark.url ? a({ href: bookmark.url, target: "_blank", class: "bookmark-url" }, bookmark.url) : null), br,
  147. p(`${i18n.bookmarkCreatedAt}: ${moment(bookmark.createdAt).format('YYYY/MM/DD HH:mm:ss')}`),
  148. p(`${i18n.bookmarkAuthor}: `, a({ href: `/author/${encodeURIComponent(bookmark.author)}` }, bookmark.author)),
  149. p(`${i18n.bookmarkCategory}: ${bookmark.category || i18n.noCategory}`),
  150. p(`${i18n.bookmarkLastVisitLabel}: ${moment(bookmark.lastVisit).format('YYYY/MM/DD HH:mm:ss') || i18n.noLastVisit}`),
  151. bookmark.tags && bookmark.tags.length
  152. ? div(
  153. bookmark.tags.map(tag =>
  154. a({ href: `/search?query=%23${encodeURIComponent(tag)}`, class: "tag-link", style: "margin-right: 0.8em; margin-bottom: 0.5em;" }, `#${tag}`)
  155. )
  156. )
  157. : null
  158. ),
  159. isAuthor ? div({ class: "bookmark-actions" },
  160. !hasOpinions
  161. ? form({ method: "GET", action: `/bookmarks/edit/${encodeURIComponent(bookmark.id)}` },
  162. button({ class: "update-btn", type: "submit" }, i18n.bookmarkUpdateButton)
  163. )
  164. : null,
  165. form({ method: "POST", action: `/bookmarks/delete/${encodeURIComponent(bookmark.id)}` },
  166. button({ class: "delete-btn", type: "submit" }, i18n.bookmarkDeleteButton)
  167. )
  168. ) : null,
  169. div({ class: "voting-buttons" },
  170. ['interesting', 'necessary', 'funny', 'disgusting', 'sensible', 'propaganda', 'adultOnly', 'boring', 'confusing', 'inspiring', 'spam'].map(category =>
  171. form({ method: "POST", action: `/bookmarks/opinions/${encodeURIComponent(bookmark.id)}/${category}` },
  172. button({ class: "vote-btn" }, `${i18n[`vote${category.charAt(0).toUpperCase() + category.slice(1)}`]} [${bookmark.opinions?.[category] || 0}]`)
  173. )
  174. )
  175. )
  176. )
  177. );
  178. };