@jant/core 0.6.14 → 0.6.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/commands/import-site.js +88 -13
- package/bin/commands/migrate.js +55 -0
- package/bin/commands/posts/rebuild-html.js +184 -0
- package/bin/commands/site/snapshot/import.js +14 -10
- package/bin/lib/d1-query.js +23 -5
- package/bin/lib/migration-runner.js +29 -7
- package/bin/lib/node-database.js +32 -0
- package/bin/lib/site-snapshot.js +347 -46
- package/bin/lib/sql-export.js +1 -1
- package/bin/lib/thread-collection-migration.js +326 -0
- package/dist/app-C75Zc8dD.js +6 -0
- package/dist/{app-2i7-WQHg.js → app-C7_4oab9.js} +7774 -6425
- package/dist/client/.vite/manifest.json +8 -8
- package/dist/client/_assets/chunks/{url-pLre2DM_.js → url-CU9pEbk5.js} +1 -1
- package/dist/client/_assets/{client-BvID5ucz.js → client-Bq0vre8Y.js} +59 -38
- package/dist/client/_assets/client-D3VZyG4L.css +2 -0
- package/dist/client/_assets/client-auth-F2rb9WfF.js +5571 -0
- package/dist/{env-OHRKGcMj.js → env-BPYViDJJ.js} +1 -1
- package/dist/{export-DWn149b7.js → export-Dl5KCiEs.js} +1348 -128
- package/dist/{github-api-BgSiE71w.js → github-api-BNF35Lkx.js} +1 -1
- package/dist/{github-app-BbklkFmU.js → github-app-DdiD-bC4.js} +1 -1
- package/dist/{github-sync-C0zyF6XS.js → github-sync-Bqg62IvV.js} +3 -3
- package/dist/{github-sync-Ckk0sJxK.js → github-sync-HdK2EgvJ.js} +3 -3
- package/dist/index.js +5 -5
- package/dist/node.js +7 -7
- package/dist/{url-BMYO-Zlt.js → url-CbLAtlZe.js} +517 -12
- package/package.json +11 -10
- package/src/__tests__/bin/posts-rebuild-html.test.ts +137 -0
- package/src/__tests__/dev-navigation-defaults.test.ts +22 -0
- package/src/__tests__/export-hugo-build.test.ts +63 -4
- package/src/__tests__/export-import-roundtrip.test.ts +6 -4
- package/src/__tests__/export-service.test.ts +142 -9
- package/src/__tests__/helpers/app.ts +3 -0
- package/src/__tests__/helpers/export-fixtures.ts +2 -0
- package/src/__tests__/import-site-command.test.ts +65 -0
- package/src/__tests__/mise-config.test.ts +22 -0
- package/src/__tests__/vite-config.test.ts +45 -0
- package/src/app.tsx +16 -3
- package/src/client/__tests__/collection-created-notice.test.ts +36 -0
- package/src/client/__tests__/collection-form-bridge.test.ts +10 -1
- package/src/client/__tests__/collection-page-actions.test.ts +71 -2
- package/src/client/__tests__/compose-bridge.test.ts +316 -2
- package/src/client/__tests__/compose-shortcuts.test.ts +20 -0
- package/src/client/__tests__/feed-video-player.test.ts +16 -1
- package/src/client/__tests__/footnote-rail.test.ts +472 -0
- package/src/client/__tests__/hydrate-partial.test.ts +3 -0
- package/src/client/__tests__/toast.test.ts +82 -1
- package/src/client/collection-created-notice.ts +66 -0
- package/src/client/collection-form-bridge.ts +17 -0
- package/src/client/collection-navigation.ts +44 -0
- package/src/client/collection-page-actions.ts +57 -1
- package/src/client/components/__tests__/compose-format-convert.test.ts +0 -19
- package/src/client/components/__tests__/jant-collection-directory.test.ts +155 -6
- package/src/client/components/__tests__/jant-command-palette.test.ts +51 -0
- package/src/client/components/__tests__/jant-compose-dialog.test.ts +2268 -206
- package/src/client/components/__tests__/jant-compose-editor.test.ts +147 -59
- package/src/client/components/__tests__/jant-compose-fullscreen.test.ts +114 -64
- package/src/client/components/__tests__/jant-config-editor.test.ts +405 -0
- package/src/client/components/__tests__/jant-media-lightbox.test.ts +121 -0
- package/src/client/components/__tests__/jant-nav-manager.test.ts +538 -2
- package/src/client/components/__tests__/jant-post-form.test.ts +2 -2
- package/src/client/components/__tests__/jant-post-menu.test.ts +47 -5
- package/src/client/components/__tests__/jant-settings-avatar.test.ts +2 -2
- package/src/client/components/__tests__/jant-settings-general.test.ts +3 -3
- package/src/client/components/collection-manager-types.ts +9 -3
- package/src/client/components/compose-format-convert.ts +5 -1
- package/src/client/components/compose-types.ts +55 -3
- package/src/client/components/jant-collection-directory.ts +165 -30
- package/src/client/components/jant-command-palette.ts +14 -7
- package/src/client/components/jant-compose-dialog.ts +1542 -694
- package/src/client/components/jant-compose-editor.ts +237 -99
- package/src/client/components/jant-compose-fullscreen.ts +27 -9
- package/src/client/components/jant-config-editor.ts +722 -0
- package/src/client/components/jant-media-lightbox.ts +100 -18
- package/src/client/components/jant-nav-manager.ts +1283 -54
- package/src/client/components/jant-post-form.ts +3 -3
- package/src/client/components/jant-post-menu.ts +177 -95
- package/src/client/components/nav-manager-types.ts +51 -2
- package/src/client/components/post-form-types.ts +1 -1
- package/src/client/compose-bridge.ts +147 -123
- package/src/client/compose-launch.ts +13 -0
- package/src/client/compose-shortcuts.ts +5 -1
- package/src/client/feed-video-player.ts +62 -2
- package/src/client/footnote-rail.ts +478 -0
- package/src/client/hydrate-partial.ts +2 -0
- package/src/client/media-lightbox-events.ts +63 -0
- package/src/client/post-refresh.ts +135 -0
- package/src/client/site-header-fragment.ts +38 -0
- package/src/client/tiptap/__tests__/blockquote-editing.test.ts +201 -0
- package/src/client/tiptap/__tests__/clear-formatting.test.ts +168 -0
- package/src/client/tiptap/__tests__/link-input-rules.test.ts +121 -0
- package/src/client/tiptap/__tests__/list-editing.test.ts +447 -0
- package/src/client/tiptap/__tests__/markdown-clipboard.test.ts +727 -4
- package/src/client/tiptap/__tests__/table-controls.test.ts +263 -0
- package/src/client/tiptap/__tests__/table-size-picker.test.ts +179 -0
- package/src/client/tiptap/bubble-menu.ts +88 -10
- package/src/client/tiptap/continuous-lists.ts +102 -0
- package/src/client/tiptap/create-editor.ts +3 -0
- package/src/client/tiptap/extensions.ts +14 -5
- package/src/client/tiptap/link-input-rules.ts +4 -4
- package/src/client/tiptap/markdown-clipboard.ts +608 -11
- package/src/client/tiptap/slash-commands.ts +8 -1
- package/src/client/tiptap/structural-keymap.ts +211 -0
- package/src/client/tiptap/tab-indent.ts +8 -22
- package/src/client/tiptap/table-control-labels.ts +30 -0
- package/src/client/tiptap/table-controls.ts +537 -0
- package/src/client/tiptap/table-size-picker.ts +254 -0
- package/src/client/toast.ts +133 -39
- package/src/client-auth.ts +1 -0
- package/src/client.ts +1 -0
- package/src/db/__tests__/backfill-thread-activity.test.ts +234 -0
- package/src/db/__tests__/d1-query.test.ts +43 -1
- package/src/db/__tests__/migration-runner.test.ts +63 -0
- package/src/db/__tests__/migrations.test.ts +469 -0
- package/src/db/__tests__/thread-activity.test.ts +113 -0
- package/src/db/__tests__/thread-collection-migration.test.ts +364 -0
- package/src/db/backfills/0005_split_thread_activity_from_quiet_replies.sql +68 -0
- package/src/db/migrations/0027_old_blue_blade.sql +39 -0
- package/src/db/migrations/0028_superb_post.sql +2 -0
- package/src/db/migrations/0029_boring_magma.sql +1 -0
- package/src/db/migrations/0030_typical_vivisector.sql +50 -0
- package/src/db/migrations/0031_many_ego.sql +3 -0
- package/src/db/migrations/meta/0027_snapshot.json +2511 -0
- package/src/db/migrations/meta/0028_snapshot.json +2511 -0
- package/src/db/migrations/meta/0029_snapshot.json +2519 -0
- package/src/db/migrations/meta/0030_snapshot.json +2541 -0
- package/src/db/migrations/meta/0031_snapshot.json +2562 -0
- package/src/db/migrations/meta/_journal.json +35 -0
- package/src/db/migrations/pg/0025_moaning_epoch.sql +39 -0
- package/src/db/migrations/pg/0026_talented_killer_shrike.sql +2 -0
- package/src/db/migrations/pg/0027_short_cannonball.sql +1 -0
- package/src/db/migrations/pg/0028_perfect_sharon_carter.sql +27 -0
- package/src/db/migrations/pg/0029_rare_hairball.sql +3 -0
- package/src/db/migrations/pg/meta/0025_snapshot.json +3204 -0
- package/src/db/migrations/pg/meta/0026_snapshot.json +3204 -0
- package/src/db/migrations/pg/meta/0027_snapshot.json +3211 -0
- package/src/db/migrations/pg/meta/0028_snapshot.json +3248 -0
- package/src/db/migrations/pg/meta/0029_snapshot.json +3289 -0
- package/src/db/migrations/pg/meta/_journal.json +35 -0
- package/src/db/pg/schema.ts +46 -20
- package/src/db/rehearsal-fixtures/demo-current.json +39 -4
- package/src/db/rehearsal-fixtures/demo-current.sql +121 -6
- package/src/db/schema.ts +48 -20
- package/src/db/thread-activity.ts +101 -0
- package/src/i18n/__tests__/detect.test.ts +8 -2
- package/src/i18n/detect.ts +14 -5
- package/src/i18n/locales/public/en.po +222 -51
- package/src/i18n/locales/public/en.ts +1 -1
- package/src/i18n/locales/public/zh-Hans.po +216 -45
- package/src/i18n/locales/public/zh-Hans.ts +1 -1
- package/src/i18n/locales/public/zh-Hant.po +216 -45
- package/src/i18n/locales/public/zh-Hant.ts +1 -1
- package/src/i18n/locales/settings/en.po +513 -10
- package/src/i18n/locales/settings/en.ts +1 -1
- package/src/i18n/locales/settings/zh-Hans.po +515 -12
- package/src/i18n/locales/settings/zh-Hans.ts +1 -1
- package/src/i18n/locales/settings/zh-Hant.po +515 -12
- package/src/i18n/locales/settings/zh-Hant.ts +1 -1
- package/src/lib/__tests__/api-settings.test.ts +299 -0
- package/src/lib/__tests__/constants.test.ts +5 -0
- package/src/lib/__tests__/feed-policy.test.ts +25 -0
- package/src/lib/__tests__/feed.test.ts +168 -0
- package/src/lib/__tests__/footnotes-upgrade.test.ts +181 -0
- package/src/lib/__tests__/markdown-roundtrip-embed.test.ts +61 -0
- package/src/lib/__tests__/markdown-to-tiptap.test.ts +32 -4
- package/src/lib/__tests__/markdown.test.ts +24 -6
- package/src/lib/__tests__/navigation.test.ts +59 -2
- package/src/lib/__tests__/post-body-html.test.ts +176 -0
- package/src/lib/__tests__/post-display.test.ts +64 -0
- package/src/lib/__tests__/resolve-config.test.ts +113 -1
- package/src/lib/__tests__/slug.test.ts +18 -0
- package/src/lib/__tests__/summary.test.ts +82 -0
- package/src/lib/__tests__/timeline.test.ts +109 -70
- package/src/lib/__tests__/timezones.test.ts +31 -3
- package/src/lib/__tests__/tiptap-render.test.ts +268 -9
- package/src/lib/__tests__/tiptap-to-markdown.test.ts +6 -0
- package/src/lib/__tests__/translit.test.ts +87 -0
- package/src/lib/__tests__/url.test.ts +53 -0
- package/src/lib/__tests__/view.test.ts +53 -0
- package/src/lib/__tests__/worker-response-cache.test.ts +18 -1
- package/src/lib/api-settings.ts +239 -12
- package/src/lib/collection-sort.ts +7 -5
- package/src/lib/constants.ts +2 -0
- package/src/lib/feed-path.ts +25 -0
- package/src/lib/feed-policy.ts +64 -0
- package/src/lib/feed.ts +113 -11
- package/src/lib/footnotes.ts +264 -0
- package/src/lib/github-sync-site-config.ts +2 -0
- package/src/lib/hugo-markdown.ts +15 -3
- package/src/lib/jant-branding.ts +1 -0
- package/src/lib/link-preview.ts +25 -0
- package/src/lib/markdown-manager.ts +193 -5
- package/src/lib/markdown.ts +10 -3
- package/src/lib/navigation.ts +16 -3
- package/src/lib/post-body-html.ts +131 -0
- package/src/lib/post-display.ts +47 -8
- package/src/lib/render.tsx +9 -2
- package/src/lib/resolve-config.ts +100 -16
- package/src/lib/schemas.ts +133 -1
- package/src/lib/settings-paths.ts +5 -0
- package/src/lib/site-skill.ts +78 -0
- package/src/lib/summary.ts +16 -5
- package/src/lib/timeline.ts +137 -131
- package/src/lib/timezones.ts +54 -14
- package/src/lib/tiptap-render.ts +359 -83
- package/src/lib/translit.ts +288 -0
- package/src/lib/url.ts +123 -12
- package/src/lib/view.ts +58 -20
- package/src/lib/worker-response-cache.ts +11 -18
- package/src/middleware/__tests__/public-content-access.test.ts +91 -0
- package/src/middleware/config.ts +2 -2
- package/src/middleware/public-content-access.ts +87 -0
- package/src/node/__tests__/cli-export.test.ts +14 -0
- package/src/node/__tests__/cli-migrate.test.ts +6 -0
- package/src/node/__tests__/cli-site-snapshot.test.ts +205 -2
- package/src/node/__tests__/cli-snapshot-meta.test.ts +100 -0
- package/src/node/index.ts +2 -1
- package/src/preset.css +16 -2
- package/src/routes/__tests__/compose.test.ts +51 -0
- package/src/routes/__tests__/site-skill.test.ts +112 -0
- package/src/routes/api/__tests__/collections.test.ts +91 -19
- package/src/routes/api/__tests__/mcp.test.ts +65 -1
- package/src/routes/api/__tests__/nav-items.test.ts +91 -0
- package/src/routes/api/__tests__/palette.test.ts +44 -0
- package/src/routes/api/__tests__/public-access.test.ts +81 -0
- package/src/routes/api/__tests__/settings.test.ts +217 -0
- package/src/routes/api/collections.ts +22 -26
- package/src/routes/api/export.ts +2 -0
- package/src/routes/api/internal/__tests__/post-body-html.test.ts +163 -0
- package/src/routes/api/internal/__tests__/sites.test.ts +35 -2
- package/src/routes/api/internal/post-body-html.ts +43 -0
- package/src/routes/api/internal/sites.ts +24 -0
- package/src/routes/api/nav-items.ts +24 -1
- package/src/routes/api/posts.ts +14 -10
- package/src/routes/api/public/__tests__/archive.test.ts +35 -0
- package/src/routes/api/public/__tests__/posts.test.ts +246 -0
- package/src/routes/api/public/archive.ts +4 -0
- package/src/routes/api/public/posts.ts +48 -24
- package/src/routes/api/search.ts +3 -0
- package/src/routes/api/settings.ts +33 -0
- package/src/routes/auth/__tests__/setup.test.ts +7 -4
- package/src/routes/compose.tsx +8 -3
- package/src/routes/dash/settings.tsx +45 -2
- package/src/routes/feed/__tests__/feed.test.ts +210 -6
- package/src/routes/feed/__tests__/sitemap.test.ts +68 -1
- package/src/routes/feed/feed.ts +23 -14
- package/src/routes/pages/__tests__/archive-params.test.ts +384 -3
- package/src/routes/pages/__tests__/collection-routing.test.ts +63 -5
- package/src/routes/pages/__tests__/collections.test.ts +9 -9
- package/src/routes/pages/__tests__/featured.test.ts +6 -2
- package/src/routes/pages/__tests__/preview.test.ts +176 -0
- package/src/routes/pages/__tests__/search.test.ts +69 -0
- package/src/routes/pages/archive.tsx +90 -22
- package/src/routes/pages/collection.tsx +32 -19
- package/src/routes/pages/collections.tsx +6 -1
- package/src/routes/pages/page.tsx +86 -11
- package/src/routes/site-skill.ts +18 -0
- package/src/services/__tests__/auth.test.ts +7 -5
- package/src/services/__tests__/collection.test.ts +190 -44
- package/src/services/__tests__/navigation.test.ts +229 -21
- package/src/services/__tests__/post-timeline.test.ts +249 -39
- package/src/services/__tests__/post.test.ts +844 -5
- package/src/services/__tests__/search.test.ts +48 -1
- package/src/services/__tests__/settings.test.ts +8 -0
- package/src/services/__tests__/site-admin.test.ts +121 -1
- package/src/services/auth.ts +2 -2
- package/src/services/bootstrap.ts +3 -2
- package/src/services/collection.ts +224 -167
- package/src/services/export-theme/assets/client-site.css +1 -1
- package/src/services/export-theme/assets/client-site.js +46 -25
- package/src/services/export-theme/layouts/_default/list.html +18 -4
- package/src/services/export-theme/layouts/_default/rss.xml +8 -12
- package/src/services/export-theme/layouts/featured/list.html +5 -4
- package/src/services/export-theme/layouts/partials/featured-thread.html +56 -0
- package/src/services/export-theme/layouts/partials/footer.html +1 -1
- package/src/services/export-theme/layouts/partials/media-gallery.html +1 -0
- package/src/services/export-theme/styles/main.css +30 -2
- package/src/services/export.ts +94 -85
- package/src/services/mcp.ts +37 -27
- package/src/services/media.ts +1 -1
- package/src/services/navigation.ts +185 -46
- package/src/services/path.ts +4 -1
- package/src/services/post.ts +1142 -342
- package/src/services/search.ts +17 -2
- package/src/services/settings.ts +2 -10
- package/src/services/site-admin.ts +63 -43
- package/src/style-cjk-jp.css +1 -1
- package/src/style-cjk-kr.css +1 -1
- package/src/style-cjk-tc.css +1 -1
- package/src/style-cjk.css +1 -1
- package/src/styles/components.css +540 -14
- package/src/styles/site-media.css +111 -19
- package/src/styles/tokens.css +76 -10
- package/src/styles/ui.css +2227 -1347
- package/src/types/bindings.ts +3 -0
- package/src/types/config.ts +243 -12
- package/src/types/constants.ts +22 -1
- package/src/types/entities.ts +14 -8
- package/src/types/operations.ts +18 -10
- package/src/types/props.ts +20 -2
- package/src/types/raw-assets.d.ts +5 -0
- package/src/types/views.ts +22 -4
- package/src/ui/__tests__/font-themes.test.ts +42 -30
- package/src/ui/compose/ComposeDialog.tsx +109 -19
- package/src/ui/dash/appearance/NavigationContent.tsx +332 -4
- package/src/ui/dash/appearance/__tests__/NavigationContent.test.tsx +23 -0
- package/src/ui/dash/settings/ConfigEditorContent.tsx +578 -0
- package/src/ui/dash/settings/GeneralContent.tsx +15 -5
- package/src/ui/dash/settings/SettingsRootContent.tsx +21 -1
- package/src/ui/dash/settings/__tests__/ConfigEditorContent.test.tsx +98 -0
- package/src/ui/dash/settings/__tests__/SettingsRootContent.test.tsx +15 -0
- package/src/ui/feed/CuratedThreadPreview.tsx +16 -4
- package/src/ui/feed/LinkCard.tsx +2 -12
- package/src/ui/feed/LinkPreview.tsx +2 -7
- package/src/ui/feed/NoteCard.tsx +10 -20
- package/src/ui/feed/PostStatusBadges.tsx +42 -3
- package/src/ui/feed/QuoteCard.tsx +2 -12
- package/src/ui/feed/ThreadPreview.tsx +26 -34
- package/src/ui/feed/TimelineFeed.tsx +2 -2
- package/src/ui/feed/__tests__/thread-preview.test.ts +197 -56
- package/src/ui/feed/__tests__/timeline-cards.test.ts +178 -3
- package/src/ui/feed/thread-preview-state.ts +5 -7
- package/src/ui/font-themes.ts +74 -26
- package/src/ui/layouts/BaseLayout.tsx +22 -8
- package/src/ui/layouts/__tests__/BaseLayout.test.tsx +64 -1
- package/src/ui/pages/ArchivePage.tsx +131 -24
- package/src/ui/pages/CollectionPage.tsx +101 -22
- package/src/ui/pages/CollectionsPage.tsx +3 -1
- package/src/ui/pages/PostPage.tsx +39 -12
- package/src/ui/pages/SearchPage.tsx +6 -14
- package/src/ui/pages/__tests__/ArchivePage.test.tsx +70 -0
- package/src/ui/pages/__tests__/PostPage.test.tsx +88 -0
- package/src/ui/shared/CollectionDirectory.tsx +13 -9
- package/src/ui/shared/CollectionsManager.tsx +17 -7
- package/src/ui/shared/DraftPreviewBar.tsx +49 -0
- package/src/ui/shared/HomePageBranding.tsx +2 -2
- package/src/ui/shared/MediaGallery.tsx +1 -0
- package/src/ui/shared/PostFooter.tsx +38 -13
- package/src/ui/shared/__tests__/home-page-branding.test.tsx +1 -1
- package/src/ui/shared/__tests__/media-gallery.test.ts +1 -0
- package/src/ui/shared/__tests__/navigation-labels.test.ts +2 -2
- package/src/ui/shared/__tests__/post-footer.test.ts +15 -0
- package/src/ui/shared/collection-management-labels.ts +33 -2
- package/src/ui/shared/custom-icons.ts +5 -0
- package/src/ui/shared/navigation-labels.ts +2 -4
- package/src/ui/shared/post-article-attributes.ts +52 -0
- package/dist/app-DZDlmh7C.js +0 -6
- package/dist/client/_assets/client-Cevfpm8H.css +0 -2
- package/dist/client/_assets/client-auth-v3yvhbgZ.js +0 -4736
- package/src/client/tiptap/wrapping-input-rules.ts +0 -102
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { eq } from "drizzle-orm";
|
|
1
2
|
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
3
|
import {
|
|
3
4
|
createTestDatabase,
|
|
4
5
|
DEFAULT_TEST_SITE_ID,
|
|
5
6
|
} from "../../__tests__/helpers/db.js";
|
|
6
|
-
import {
|
|
7
|
+
import { posts, threadCollections } from "../../db/schema.js";
|
|
7
8
|
import { createPostService } from "../post.js";
|
|
8
9
|
import { createCollectionService } from "../collection.js";
|
|
9
10
|
import type { Database } from "../../db/index.js";
|
|
@@ -236,7 +237,7 @@ describe("PostService - Timeline features", () => {
|
|
|
236
237
|
expect(result.size).toBe(0);
|
|
237
238
|
});
|
|
238
239
|
|
|
239
|
-
it("returns
|
|
240
|
+
it("returns the only reply as leading and latest context", async () => {
|
|
240
241
|
const root = await postService.create({
|
|
241
242
|
format: "note",
|
|
242
243
|
bodyMarkdown: "root",
|
|
@@ -250,13 +251,13 @@ describe("PostService - Timeline features", () => {
|
|
|
250
251
|
const result = await postService.getThreadTimelineContext([root.id]);
|
|
251
252
|
const ctx = result.get(root.id);
|
|
252
253
|
expect(ctx).toBeDefined();
|
|
253
|
-
expect(ctx?.
|
|
254
|
-
expect(ctx?.
|
|
254
|
+
expect(ctx?.leadingReplies.map((post) => post.id)).toEqual([reply.id]);
|
|
255
|
+
expect(ctx?.trailingReplies).toEqual([]);
|
|
255
256
|
expect(ctx?.latestReply.id).toBe(reply.id);
|
|
256
257
|
expect(ctx?.totalReplyCount).toBe(1);
|
|
257
258
|
});
|
|
258
259
|
|
|
259
|
-
it("returns
|
|
260
|
+
it("returns overlapping leading and trailing context for short threads", async () => {
|
|
260
261
|
const root = await postService.create({
|
|
261
262
|
format: "note",
|
|
262
263
|
bodyMarkdown: "root",
|
|
@@ -275,13 +276,16 @@ describe("PostService - Timeline features", () => {
|
|
|
275
276
|
const result = await postService.getThreadTimelineContext([root.id]);
|
|
276
277
|
const ctx = result.get(root.id);
|
|
277
278
|
expect(ctx).toBeDefined();
|
|
278
|
-
expect(ctx?.
|
|
279
|
-
|
|
279
|
+
expect(ctx?.leadingReplies.map((post) => post.id)).toEqual([
|
|
280
|
+
reply1.id,
|
|
281
|
+
reply2.id,
|
|
282
|
+
]);
|
|
283
|
+
expect(ctx?.trailingReplies.map((post) => post.id)).toEqual([reply1.id]);
|
|
280
284
|
expect(ctx?.latestReply.id).toBe(reply2.id);
|
|
281
285
|
expect(ctx?.totalReplyCount).toBe(2);
|
|
282
286
|
});
|
|
283
287
|
|
|
284
|
-
it("returns
|
|
288
|
+
it("returns the first two and last three reply positions for longer threads", async () => {
|
|
285
289
|
const root = await postService.create({
|
|
286
290
|
format: "note",
|
|
287
291
|
bodyMarkdown: "root",
|
|
@@ -298,8 +302,14 @@ describe("PostService - Timeline features", () => {
|
|
|
298
302
|
const result = await postService.getThreadTimelineContext([root.id]);
|
|
299
303
|
const ctx = result.get(root.id);
|
|
300
304
|
expect(ctx).toBeDefined();
|
|
301
|
-
expect(ctx?.
|
|
302
|
-
|
|
305
|
+
expect(ctx?.leadingReplies.map((post) => post.bodyText)).toEqual([
|
|
306
|
+
"reply 0",
|
|
307
|
+
"reply 1",
|
|
308
|
+
]);
|
|
309
|
+
expect(ctx?.trailingReplies.map((post) => post.bodyText)).toEqual([
|
|
310
|
+
"reply 2",
|
|
311
|
+
"reply 3",
|
|
312
|
+
]);
|
|
303
313
|
expect(ctx?.latestReply.bodyText).toBe("reply 4");
|
|
304
314
|
expect(ctx?.totalReplyCount).toBe(5);
|
|
305
315
|
});
|
|
@@ -326,7 +336,7 @@ describe("PostService - Timeline features", () => {
|
|
|
326
336
|
const result = await postService.getThreadTimelineContext([root.id]);
|
|
327
337
|
const ctx = result.get(root.id);
|
|
328
338
|
expect(ctx).toBeDefined();
|
|
329
|
-
expect(ctx?.
|
|
339
|
+
expect(ctx?.leadingReplies.map((post) => post.id)).toEqual([reply1.id]);
|
|
330
340
|
expect(ctx?.latestReply.id).toBe(reply1.id);
|
|
331
341
|
expect(ctx?.totalReplyCount).toBe(1);
|
|
332
342
|
});
|
|
@@ -351,7 +361,9 @@ describe("PostService - Timeline features", () => {
|
|
|
351
361
|
const result = await postService.getThreadTimelineContext([root.id]);
|
|
352
362
|
const ctx = result.get(root.id);
|
|
353
363
|
expect(ctx).toBeDefined();
|
|
354
|
-
expect(ctx?.
|
|
364
|
+
expect(ctx?.leadingReplies.map((post) => post.id)).toEqual([
|
|
365
|
+
publishedReply.id,
|
|
366
|
+
]);
|
|
355
367
|
expect(ctx?.latestReply.id).toBe(publishedReply.id);
|
|
356
368
|
expect(ctx?.totalReplyCount).toBe(1);
|
|
357
369
|
});
|
|
@@ -381,14 +393,14 @@ describe("PostService - Timeline features", () => {
|
|
|
381
393
|
root2.id,
|
|
382
394
|
]);
|
|
383
395
|
expect(result.size).toBe(2);
|
|
384
|
-
expect(result.get(root1.id)?.
|
|
385
|
-
expect(result.get(root2.id)?.
|
|
396
|
+
expect(result.get(root1.id)?.leadingReplies[0]?.id).toBe(r1Reply.id);
|
|
397
|
+
expect(result.get(root2.id)?.leadingReplies[0]?.id).toBe(r2Reply.id);
|
|
386
398
|
expect(result.get(root1.id)?.latestReply.id).toBe(r1Reply.id);
|
|
387
399
|
expect(result.get(root2.id)?.latestReply.id).toBe(r2Reply.id);
|
|
388
400
|
});
|
|
389
401
|
});
|
|
390
402
|
|
|
391
|
-
describe("
|
|
403
|
+
describe("getThreadTailIds", () => {
|
|
392
404
|
it("keeps independent root threads separate", async () => {
|
|
393
405
|
const root1 = await postService.create({
|
|
394
406
|
format: "note",
|
|
@@ -400,10 +412,7 @@ describe("PostService - Timeline features", () => {
|
|
|
400
412
|
quoteText: "quoted",
|
|
401
413
|
});
|
|
402
414
|
|
|
403
|
-
const result = await postService.
|
|
404
|
-
root1.id,
|
|
405
|
-
root2.id,
|
|
406
|
-
]);
|
|
415
|
+
const result = await postService.getThreadTailIds([root1.id, root2.id]);
|
|
407
416
|
|
|
408
417
|
expect(result.get(root1.id)).toBe(root1.id);
|
|
409
418
|
expect(result.get(root2.id)).toBe(root2.id);
|
|
@@ -424,14 +433,57 @@ describe("PostService - Timeline features", () => {
|
|
|
424
433
|
replyToId: root1.id,
|
|
425
434
|
});
|
|
426
435
|
|
|
427
|
-
const result = await postService.
|
|
428
|
-
root1.id,
|
|
429
|
-
root2.id,
|
|
430
|
-
]);
|
|
436
|
+
const result = await postService.getThreadTailIds([root1.id, root2.id]);
|
|
431
437
|
|
|
432
438
|
expect(result.get(root1.id)).toBe(reply1.id);
|
|
433
439
|
expect(result.get(root2.id)).toBe(root2.id);
|
|
434
440
|
});
|
|
441
|
+
|
|
442
|
+
it("skips a trailing draft by default", async () => {
|
|
443
|
+
const root = await postService.create({
|
|
444
|
+
format: "note",
|
|
445
|
+
bodyMarkdown: "root",
|
|
446
|
+
});
|
|
447
|
+
const reply = await postService.create({
|
|
448
|
+
format: "note",
|
|
449
|
+
bodyMarkdown: "published reply",
|
|
450
|
+
replyToId: root.id,
|
|
451
|
+
});
|
|
452
|
+
await postService.create({
|
|
453
|
+
format: "note",
|
|
454
|
+
bodyMarkdown: "unfinished",
|
|
455
|
+
replyToId: reply.id,
|
|
456
|
+
status: "draft",
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
const result = await postService.getThreadTailIds([root.id]);
|
|
460
|
+
|
|
461
|
+
expect(result.get(root.id)).toBe(reply.id);
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
it("returns a trailing draft when drafts are included", async () => {
|
|
465
|
+
const root = await postService.create({
|
|
466
|
+
format: "note",
|
|
467
|
+
bodyMarkdown: "root",
|
|
468
|
+
});
|
|
469
|
+
const reply = await postService.create({
|
|
470
|
+
format: "note",
|
|
471
|
+
bodyMarkdown: "published reply",
|
|
472
|
+
replyToId: root.id,
|
|
473
|
+
});
|
|
474
|
+
const draft = await postService.create({
|
|
475
|
+
format: "note",
|
|
476
|
+
bodyMarkdown: "unfinished",
|
|
477
|
+
replyToId: reply.id,
|
|
478
|
+
status: "draft",
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
const result = await postService.getThreadTailIds([root.id], {
|
|
482
|
+
includeDrafts: true,
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
expect(result.get(root.id)).toBe(draft.id);
|
|
486
|
+
});
|
|
435
487
|
});
|
|
436
488
|
|
|
437
489
|
describe("timeline assembly", () => {
|
|
@@ -464,6 +516,39 @@ describe("PostService - Timeline features", () => {
|
|
|
464
516
|
});
|
|
465
517
|
});
|
|
466
518
|
|
|
519
|
+
describe("countCollectionThreadRootsForCollections", () => {
|
|
520
|
+
it("counts rated Threads once even when multiple children are rated", async () => {
|
|
521
|
+
const collection = await collectionService.create({
|
|
522
|
+
slug: "rated-threads",
|
|
523
|
+
title: "Rated Threads",
|
|
524
|
+
});
|
|
525
|
+
const ratedRoot = await postService.create({
|
|
526
|
+
format: "note",
|
|
527
|
+
bodyMarkdown: "Rated root",
|
|
528
|
+
rating: 5,
|
|
529
|
+
});
|
|
530
|
+
await postService.create({
|
|
531
|
+
format: "note",
|
|
532
|
+
bodyMarkdown: "Rated child",
|
|
533
|
+
rating: 4,
|
|
534
|
+
replyToId: ratedRoot.id,
|
|
535
|
+
});
|
|
536
|
+
const unratedRoot = await postService.create({
|
|
537
|
+
format: "note",
|
|
538
|
+
bodyMarkdown: "Unrated root",
|
|
539
|
+
});
|
|
540
|
+
await collectionService.addThread(collection.id, ratedRoot.id);
|
|
541
|
+
await collectionService.addThread(collection.id, unratedRoot.id);
|
|
542
|
+
|
|
543
|
+
const count = await postService.countCollectionThreadRootsForCollections(
|
|
544
|
+
[collection.id],
|
|
545
|
+
{ status: "published", hasRating: true },
|
|
546
|
+
);
|
|
547
|
+
|
|
548
|
+
expect(count).toBe(1);
|
|
549
|
+
});
|
|
550
|
+
});
|
|
551
|
+
|
|
467
552
|
describe("listCollectionThreadRootIds", () => {
|
|
468
553
|
it("bumps a hidden collection thread when a non-quiet reply is published", async () => {
|
|
469
554
|
const collection = await collectionService.create({
|
|
@@ -482,8 +567,8 @@ describe("PostService - Timeline features", () => {
|
|
|
482
567
|
publishedAt: 2000,
|
|
483
568
|
});
|
|
484
569
|
|
|
485
|
-
await collectionService.
|
|
486
|
-
await collectionService.
|
|
570
|
+
await collectionService.addThread(collection.id, hiddenRoot.id);
|
|
571
|
+
await collectionService.addThread(collection.id, newerRoot.id);
|
|
487
572
|
|
|
488
573
|
await postService.create({
|
|
489
574
|
format: "note",
|
|
@@ -519,8 +604,8 @@ describe("PostService - Timeline features", () => {
|
|
|
519
604
|
publishedAt: 2000,
|
|
520
605
|
});
|
|
521
606
|
|
|
522
|
-
await collectionService.
|
|
523
|
-
await collectionService.
|
|
607
|
+
await collectionService.addThread(collection.id, olderRoot.id);
|
|
608
|
+
await collectionService.addThread(collection.id, newerRoot.id);
|
|
524
609
|
|
|
525
610
|
await postService.create({
|
|
526
611
|
format: "note",
|
|
@@ -540,6 +625,131 @@ describe("PostService - Timeline features", () => {
|
|
|
540
625
|
|
|
541
626
|
expect(rootIds).toEqual([newerRoot.id, olderRoot.id]);
|
|
542
627
|
});
|
|
628
|
+
|
|
629
|
+
// Quietness used to live only in control flow: the reply skipped the
|
|
630
|
+
// recalculation and nothing recorded that it had. Any later recalculation
|
|
631
|
+
// recomputed MAX(published_at) over the whole Thread, swallowed the quiet
|
|
632
|
+
// reply, and bumped the Thread anyway. It is a stored column now, so the
|
|
633
|
+
// decision survives whatever else happens to the Thread.
|
|
634
|
+
it("keeps a quiet reply quiet after an unrelated post is deleted", async () => {
|
|
635
|
+
const collection = await collectionService.create({
|
|
636
|
+
slug: "durable",
|
|
637
|
+
title: "Durable",
|
|
638
|
+
});
|
|
639
|
+
const olderRoot = await postService.create({
|
|
640
|
+
format: "note",
|
|
641
|
+
bodyMarkdown: "Older root",
|
|
642
|
+
publishedAt: 1000,
|
|
643
|
+
});
|
|
644
|
+
const newerRoot = await postService.create({
|
|
645
|
+
format: "note",
|
|
646
|
+
bodyMarkdown: "Newer root",
|
|
647
|
+
publishedAt: 2000,
|
|
648
|
+
});
|
|
649
|
+
await collectionService.addThread(collection.id, olderRoot.id);
|
|
650
|
+
await collectionService.addThread(collection.id, newerRoot.id);
|
|
651
|
+
|
|
652
|
+
const normalReply = await postService.create({
|
|
653
|
+
format: "note",
|
|
654
|
+
bodyMarkdown: "Normal reply",
|
|
655
|
+
replyToId: olderRoot.id,
|
|
656
|
+
publishedAt: 1500,
|
|
657
|
+
});
|
|
658
|
+
await postService.create({
|
|
659
|
+
format: "note",
|
|
660
|
+
bodyMarkdown: "Quiet reply",
|
|
661
|
+
replyToId: normalReply.id,
|
|
662
|
+
publishedAt: 3000,
|
|
663
|
+
quietReply: true,
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
// Triggers a full recalculation of the Thread.
|
|
667
|
+
await postService.delete(normalReply.id);
|
|
668
|
+
|
|
669
|
+
const root = await postService.getById(olderRoot.id);
|
|
670
|
+
expect(root?.lastActivityAt).toBe(1000);
|
|
671
|
+
expect(root?.threadUpdatedAt).toBe(3000);
|
|
672
|
+
|
|
673
|
+
const rootIds = await postService.listCollectionThreadRootIds(
|
|
674
|
+
collection.id,
|
|
675
|
+
{ status: "published", sortOrder: "newest" },
|
|
676
|
+
);
|
|
677
|
+
expect(rootIds).toEqual([newerRoot.id, olderRoot.id]);
|
|
678
|
+
});
|
|
679
|
+
|
|
680
|
+
it("records both activity timestamps for a quiet reply", async () => {
|
|
681
|
+
const root = await postService.create({
|
|
682
|
+
format: "note",
|
|
683
|
+
bodyMarkdown: "Root",
|
|
684
|
+
publishedAt: 1000,
|
|
685
|
+
});
|
|
686
|
+
await postService.create({
|
|
687
|
+
format: "note",
|
|
688
|
+
bodyMarkdown: "Quiet reply",
|
|
689
|
+
replyToId: root.id,
|
|
690
|
+
publishedAt: 3000,
|
|
691
|
+
quietReply: true,
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
const updated = await postService.getById(root.id);
|
|
695
|
+
expect(updated?.lastActivityAt).toBe(1000);
|
|
696
|
+
expect(updated?.threadUpdatedAt).toBe(3000);
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
it("moves both timestamps for a normal reply", async () => {
|
|
700
|
+
const root = await postService.create({
|
|
701
|
+
format: "note",
|
|
702
|
+
bodyMarkdown: "Root",
|
|
703
|
+
publishedAt: 1000,
|
|
704
|
+
});
|
|
705
|
+
await postService.create({
|
|
706
|
+
format: "note",
|
|
707
|
+
bodyMarkdown: "Normal reply",
|
|
708
|
+
replyToId: root.id,
|
|
709
|
+
publishedAt: 3000,
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
const updated = await postService.getById(root.id);
|
|
713
|
+
expect(updated?.lastActivityAt).toBe(3000);
|
|
714
|
+
expect(updated?.threadUpdatedAt).toBe(3000);
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
// Activity means the Thread gained a post. Editing an old post is not
|
|
718
|
+
// activity — otherwise fixing a typo would drag it back to the top of its
|
|
719
|
+
// collection, and "recently updated" would stop meaning anything.
|
|
720
|
+
it("does not reorder a Thread when its root is only edited", async () => {
|
|
721
|
+
const collection = await collectionService.create({
|
|
722
|
+
slug: "edited",
|
|
723
|
+
title: "Edited",
|
|
724
|
+
});
|
|
725
|
+
const editedRoot = await postService.create({
|
|
726
|
+
format: "note",
|
|
727
|
+
bodyMarkdown: "Edited root",
|
|
728
|
+
publishedAt: 1000,
|
|
729
|
+
});
|
|
730
|
+
const newerRoot = await postService.create({
|
|
731
|
+
format: "note",
|
|
732
|
+
bodyMarkdown: "Newer root",
|
|
733
|
+
publishedAt: 2000,
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
await collectionService.addThread(collection.id, editedRoot.id);
|
|
737
|
+
await collectionService.addThread(collection.id, newerRoot.id);
|
|
738
|
+
await db
|
|
739
|
+
.update(posts)
|
|
740
|
+
.set({ createdAt: 500, updatedAt: 3000 })
|
|
741
|
+
.where(eq(posts.id, editedRoot.id));
|
|
742
|
+
|
|
743
|
+
const rootIds = await postService.listCollectionThreadRootIds(
|
|
744
|
+
collection.id,
|
|
745
|
+
{
|
|
746
|
+
status: "published",
|
|
747
|
+
sortOrder: "newest",
|
|
748
|
+
},
|
|
749
|
+
);
|
|
750
|
+
|
|
751
|
+
expect(rootIds).toEqual([newerRoot.id, editedRoot.id]);
|
|
752
|
+
});
|
|
543
753
|
});
|
|
544
754
|
|
|
545
755
|
describe("listCollectionFeedEntries", () => {
|
|
@@ -562,16 +772,16 @@ describe("PostService - Timeline features", () => {
|
|
|
562
772
|
bodyMarkdown: "Second root",
|
|
563
773
|
});
|
|
564
774
|
|
|
565
|
-
await db.insert(
|
|
775
|
+
await db.insert(threadCollections).values([
|
|
566
776
|
{
|
|
567
777
|
siteId: DEFAULT_TEST_SITE_ID,
|
|
568
|
-
|
|
778
|
+
threadId: firstRoot.id,
|
|
569
779
|
collectionId: collection.id,
|
|
570
780
|
createdAt: 100,
|
|
571
781
|
},
|
|
572
782
|
{
|
|
573
783
|
siteId: DEFAULT_TEST_SITE_ID,
|
|
574
|
-
|
|
784
|
+
threadId: secondRoot.id,
|
|
575
785
|
collectionId: collection.id,
|
|
576
786
|
createdAt: 200,
|
|
577
787
|
},
|
|
@@ -607,17 +817,17 @@ describe("PostService - Timeline features", () => {
|
|
|
607
817
|
publishedAt: 2000,
|
|
608
818
|
});
|
|
609
819
|
|
|
610
|
-
await db.insert(
|
|
820
|
+
await db.insert(threadCollections).values([
|
|
611
821
|
{
|
|
612
822
|
siteId: DEFAULT_TEST_SITE_ID,
|
|
613
|
-
|
|
823
|
+
threadId: olderRoot.id,
|
|
614
824
|
collectionId: collection.id,
|
|
615
825
|
createdAt: 100,
|
|
616
826
|
pinnedAt: 5000,
|
|
617
827
|
},
|
|
618
828
|
{
|
|
619
829
|
siteId: DEFAULT_TEST_SITE_ID,
|
|
620
|
-
|
|
830
|
+
threadId: newerRoot.id,
|
|
621
831
|
collectionId: collection.id,
|
|
622
832
|
createdAt: 200,
|
|
623
833
|
},
|
|
@@ -670,22 +880,22 @@ describe("PostService - Timeline features", () => {
|
|
|
670
880
|
bodyMarkdown: "Second root",
|
|
671
881
|
});
|
|
672
882
|
|
|
673
|
-
await db.insert(
|
|
883
|
+
await db.insert(threadCollections).values([
|
|
674
884
|
{
|
|
675
885
|
siteId: DEFAULT_TEST_SITE_ID,
|
|
676
|
-
|
|
886
|
+
threadId: sharedRoot.id,
|
|
677
887
|
collectionId: smart.id,
|
|
678
888
|
createdAt: 100,
|
|
679
889
|
},
|
|
680
890
|
{
|
|
681
891
|
siteId: DEFAULT_TEST_SITE_ID,
|
|
682
|
-
|
|
892
|
+
threadId: sharedRoot.id,
|
|
683
893
|
collectionId: movies.id,
|
|
684
894
|
createdAt: 300,
|
|
685
895
|
},
|
|
686
896
|
{
|
|
687
897
|
siteId: DEFAULT_TEST_SITE_ID,
|
|
688
|
-
|
|
898
|
+
threadId: secondRoot.id,
|
|
689
899
|
collectionId: movies.id,
|
|
690
900
|
createdAt: 200,
|
|
691
901
|
},
|
|
@@ -722,8 +932,8 @@ describe("PostService - Timeline features", () => {
|
|
|
722
932
|
publishedAt: 2000,
|
|
723
933
|
});
|
|
724
934
|
|
|
725
|
-
await collectionService.
|
|
726
|
-
await collectionService.
|
|
935
|
+
await collectionService.addThread(collection.id, hiddenRoot.id);
|
|
936
|
+
await collectionService.addThread(collection.id, newerRoot.id);
|
|
727
937
|
|
|
728
938
|
await postService.create({
|
|
729
939
|
format: "note",
|