From 3a9cec4c64edf58ed57cc73cef9172473e14f0bd Mon Sep 17 00:00:00 2001 From: Stephen Zhou <38493346+hyoban@users.noreply.github.com> Date: Wed, 23 Jul 2025 18:26:38 +0800 Subject: [PATCH] feat: hide from timeline option (#4229) --- .../src/modules/discover/FeedForm.tsx | 33 +- .../src/modules/discover/ListForm.tsx | 33 +- apps/mobile/src/modules/feed/FollowFeed.tsx | 26 +- apps/mobile/src/modules/list/FollowList.tsx | 23 + locales/app/en.json | 2 + locales/mobile/default/en.json | 2 + .../src/drizzle/0032_orange_prima.sql | 1 + .../src/drizzle/meta/0032_snapshot.json | 886 ++++++++++++++++++ .../database/src/drizzle/meta/_journal.json | 7 + .../database/src/drizzle/migrations.js | 2 + .../internal/database/src/schemas/index.ts | 1 + packages/internal/store/src/entry/store.ts | 8 +- packages/internal/store/src/morph/api.ts | 2 + .../internal/store/src/subscription/types.ts | 13 +- 14 files changed, 1025 insertions(+), 14 deletions(-) create mode 100644 packages/internal/database/src/drizzle/0032_orange_prima.sql create mode 100644 packages/internal/database/src/drizzle/meta/0032_snapshot.json diff --git a/apps/desktop/layer/renderer/src/modules/discover/FeedForm.tsx b/apps/desktop/layer/renderer/src/modules/discover/FeedForm.tsx index 01b2649f6..2b3e7948e 100644 --- a/apps/desktop/layer/renderer/src/modules/discover/FeedForm.tsx +++ b/apps/desktop/layer/renderer/src/modules/discover/FeedForm.tsx @@ -46,6 +46,7 @@ const formSchema = z.object({ view: z.string(), category: z.string().nullable().optional(), isPrivate: z.boolean().optional(), + hideFromTimeline: z.boolean().optional(), title: z.string().optional(), }) export type FeedFormDataValuesType = z.infer @@ -189,6 +190,7 @@ const FeedInnerForm = ({ category?: string | null isPrivate?: boolean title?: string | null + hideFromTimeline?: boolean | null } feed: FeedModel entries?: EntryModelSimple[] @@ -217,8 +219,11 @@ const FeedInnerForm = ({ if (subscription) { form.setValue("view", `${subscription?.view}`) subscription?.category && form.setValue("category", subscription.category) - form.setValue("isPrivate", subscription?.isPrivate || false) - form.setValue("title", subscription?.title || "") + typeof subscription.isPrivate === "boolean" && + form.setValue("isPrivate", subscription.isPrivate) + typeof subscription.hideFromTimeline === "boolean" && + form.setValue("hideFromTimeline", subscription.hideFromTimeline) + subscription?.title && form.setValue("title", subscription.title) } }, [subscription]) @@ -239,6 +244,7 @@ const FeedInnerForm = ({ view: Number.parseInt(values.view), category: values.category, isPrivate: values.isPrivate, + hideFromTimeline: values.hideFromTimeline, title: values.title, feedId: feed.id, } @@ -387,6 +393,29 @@ const FeedInnerForm = ({ )} /> + ( + +
+
+ {t("feed_form.hide_from_timeline")} + + {t("feed_form.hide_from_timeline_description")} + +
+ + + +
+
+ )} + /> { if (subscription) { form.setValue("view", `${subscription?.view}`) - form.setValue("isPrivate", subscription?.isPrivate || false) - form.setValue("title", subscription?.title || "") + typeof subscription.isPrivate === "boolean" && + form.setValue("isPrivate", subscription.isPrivate) + subscription?.title && form.setValue("title", subscription.title) + typeof subscription.hideFromTimeline === "boolean" && + form.setValue("hideFromTimeline", subscription.hideFromTimeline) } }, [subscription]) @@ -213,6 +218,7 @@ const ListInnerForm = ({ view: Number.parseInt(values.view), category: values.category, isPrivate: values.isPrivate, + hideFromTimeline: values.hideFromTimeline, title: values.title, TOTPCode: values.TOTPCode, } @@ -334,6 +340,29 @@ const ListInnerForm = ({ )} /> + ( + +
+
+ {t("feed_form.hide_from_timeline")} + + {t("feed_form.hide_from_timeline_description")} + +
+ + + +
+
+ )} + /> {!!list.fee && !isSubscribed && (
diff --git a/apps/mobile/src/modules/feed/FollowFeed.tsx b/apps/mobile/src/modules/feed/FollowFeed.tsx index 399e1bdad..136da2f77 100644 --- a/apps/mobile/src/modules/feed/FollowFeed.tsx +++ b/apps/mobile/src/modules/feed/FollowFeed.tsx @@ -37,6 +37,7 @@ const formSchema = z.object({ view: z.coerce.number(), category: z.string().nullable().optional(), isPrivate: z.boolean().optional(), + hideFromTimeline: z.boolean().optional(), title: z.string().optional(), }) @@ -93,9 +94,10 @@ function FollowImpl(props: { feedId: string; defaultView?: FeedViewType }) { const form = useForm>({ resolver: zodResolver(formSchema), defaultValues: { - category: subscription?.category ?? "", - isPrivate: subscription?.isPrivate ?? false, - title: subscription?.title ?? "", + category: subscription?.category ?? undefined, + isPrivate: subscription?.isPrivate ?? undefined, + hideFromTimeline: subscription?.hideFromTimeline ?? undefined, + title: subscription?.title ?? undefined, view: subscription?.view ?? defaultView ?? FeedViewType.Articles, }, }) @@ -121,8 +123,10 @@ function FollowImpl(props: { feedId: string; defaultView?: FeedViewType }) { view: values.view, category: values.category ?? "", isPrivate: values.isPrivate ?? false, + hideFromTimeline: values.hideFromTimeline, title: values.title ?? "", feedId: feed?.id, + listId: undefined, } if (isSubscribed) { @@ -276,6 +280,22 @@ function FollowImpl(props: { feedId: string; defaultView?: FeedViewType }) { /> + + ( + + )} + /> + + diff --git a/apps/mobile/src/modules/list/FollowList.tsx b/apps/mobile/src/modules/list/FollowList.tsx index 03c7cf346..cec629807 100644 --- a/apps/mobile/src/modules/list/FollowList.tsx +++ b/apps/mobile/src/modules/list/FollowList.tsx @@ -54,6 +54,7 @@ export const FollowList = (props: { id: string }) => { const formSchema = z.object({ view: z.number(), isPrivate: z.boolean(), + hideFromTimeline: z.boolean().optional(), title: z.string().optional(), }) @@ -71,6 +72,7 @@ const Impl = (props: { id: string }) => { defaultValues: { view: list?.view ?? FeedViewType.Articles, isPrivate: subscription?.isPrivate, + hideFromTimeline: subscription?.hideFromTimeline ?? undefined, title: subscription?.title ?? undefined, }, }) @@ -89,6 +91,11 @@ const Impl = (props: { id: string }) => { isPrivate: payload.isPrivate, title: payload.title, + hideFromTimeline: payload.hideFromTimeline, + + url: undefined, + category: undefined, + feedId: undefined, } if (isSubscribed) { @@ -220,6 +227,22 @@ const Impl = (props: { id: string }) => { /> + + ( + + )} + /> + + {!!list.fee && ( diff --git a/locales/app/en.json b/locales/app/en.json index a4382fc11..3dfc2e899 100644 --- a/locales/app/en.json +++ b/locales/app/en.json @@ -233,6 +233,8 @@ "feed_form.follow": "Follow", "feed_form.follow_with_fee": "Follow with {{fee}} Power", "feed_form.followed": "🎉 Followed.", + "feed_form.hide_from_timeline": "Hide from Timeline", + "feed_form.hide_from_timeline_description": "Whether this subscription's entries are visible on your main view timeline.", "feed_form.private_follow": "Private Follow", "feed_form.private_follow_description": "Whether this follow is publicly visible on your profile page.", "feed_form.retry": "Retry", diff --git a/locales/mobile/default/en.json b/locales/mobile/default/en.json index ca3d0c0e5..1a611aba4 100644 --- a/locales/mobile/default/en.json +++ b/locales/mobile/default/en.json @@ -63,6 +63,8 @@ "signin.sign_up_to": "Sign up to", "subscription_form.category": "Category", "subscription_form.category_description": "By default, your follows will be grouped by website.", + "subscription_form.hide_from_timeline": "Hide from Timeline", + "subscription_form.hide_from_timeline_description": "Whether this subscription's entries are visible on your main view timeline.", "subscription_form.private_follow": "Private Follow", "subscription_form.private_follow_description": "Whether this follow is publicly visible on your profile page.", "subscription_form.title": "Title", diff --git a/packages/internal/database/src/drizzle/0032_orange_prima.sql b/packages/internal/database/src/drizzle/0032_orange_prima.sql new file mode 100644 index 000000000..21decb1b7 --- /dev/null +++ b/packages/internal/database/src/drizzle/0032_orange_prima.sql @@ -0,0 +1 @@ +ALTER TABLE `subscriptions` ADD `hide_from_timeline` integer; \ No newline at end of file diff --git a/packages/internal/database/src/drizzle/meta/0032_snapshot.json b/packages/internal/database/src/drizzle/meta/0032_snapshot.json new file mode 100644 index 000000000..825563f69 --- /dev/null +++ b/packages/internal/database/src/drizzle/meta/0032_snapshot.json @@ -0,0 +1,886 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "611bbe39-b8f0-4c81-a067-315ac9b0f8a3", + "prevId": "022f2968-0d86-43b1-a257-1c81d8fd42d4", + "tables": { + "ai_chat_messages": { + "name": "ai_chat_messages", + "columns": { + "room_id": { + "name": "room_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch() * 1000)" + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "ai_chat_messages_unq": { + "name": "ai_chat_messages_unq", + "columns": ["room_id", "id"], + "isUnique": true + } + }, + "foreignKeys": { + "ai_chat_messages_room_id_ai_chat_room_id_fk": { + "name": "ai_chat_messages_room_id_ai_chat_room_id_fk", + "tableFrom": "ai_chat_messages", + "tableTo": "ai_chat", + "columnsFrom": ["room_id"], + "columnsTo": ["room_id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "ai_chat": { + "name": "ai_chat", + "columns": { + "room_id": { + "name": "room_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch() * 1000)" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "collections": { + "name": "collections", + "columns": { + "feed_id": { + "name": "feed_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "entry_id": { + "name": "entry_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "view": { + "name": "view", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "entries": { + "name": "entries", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "source_content": { + "name": "source_content", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "readability_updated_at": { + "name": "readability_updated_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "guid": { + "name": "guid", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "author": { + "name": "author", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "author_url": { + "name": "author_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "author_avatar": { + "name": "author_avatar", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "inserted_at": { + "name": "inserted_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "published_at": { + "name": "published_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "media": { + "name": "media", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "categories": { + "name": "categories", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "attachments": { + "name": "attachments", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "extra": { + "name": "extra", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "language": { + "name": "language", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "feed_id": { + "name": "feed_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "inbox_handle": { + "name": "inbox_handle", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "read": { + "name": "read", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sources": { + "name": "sources", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "settings": { + "name": "settings", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "feeds": { + "name": "feeds", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "error_at": { + "name": "error_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "site_url": { + "name": "site_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "owner_user_id": { + "name": "owner_user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "subscription_count": { + "name": "subscription_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updates_per_week": { + "name": "updates_per_week", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "latest_entry_published_at": { + "name": "latest_entry_published_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "tip_users": { + "name": "tip_users", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "published_at": { + "name": "published_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "images": { + "name": "images", + "columns": { + "url": { + "name": "url", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "colors": { + "name": "colors", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch() * 1000)" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "inboxes": { + "name": "inboxes", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "lists": { + "name": "lists", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "feed_ids": { + "name": "feed_ids", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "view": { + "name": "view", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "fee": { + "name": "fee", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "owner_user_id": { + "name": "owner_user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "subscription_count": { + "name": "subscription_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "purchase_amount": { + "name": "purchase_amount", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "subscriptions": { + "name": "subscriptions", + "columns": { + "feed_id": { + "name": "feed_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "list_id": { + "name": "list_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "inbox_id": { + "name": "inbox_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "view": { + "name": "view", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "is_private": { + "name": "is_private", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "hide_from_timeline": { + "name": "hide_from_timeline", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "summaries": { + "name": "summaries", + "columns": { + "entry_id": { + "name": "entry_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "readability_summary": { + "name": "readability_summary", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "language": { + "name": "language", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "unq": { + "name": "unq", + "columns": ["entry_id", "language"], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "translations": { + "name": "translations", + "columns": { + "entry_id": { + "name": "entry_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "language": { + "name": "language", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "readability_content": { + "name": "readability_content", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "translation-unique-index": { + "name": "translation-unique-index", + "columns": ["entry_id", "language"], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "unread": { + "name": "unread", + "columns": { + "subscription_id": { + "name": "subscription_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "count": { + "name": "count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "users": { + "name": "users", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "handle": { + "name": "handle", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_me": { + "name": "is_me", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "email_verified": { + "name": "email_verified", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "bio": { + "name": "bio", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "website": { + "name": "website", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "social_links": { + "name": "social_links", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} diff --git a/packages/internal/database/src/drizzle/meta/_journal.json b/packages/internal/database/src/drizzle/meta/_journal.json index f71e7c7cc..ff7902552 100644 --- a/packages/internal/database/src/drizzle/meta/_journal.json +++ b/packages/internal/database/src/drizzle/meta/_journal.json @@ -225,6 +225,13 @@ "when": 1751640829832, "tag": "0031_kind_ikaris", "breakpoints": true + }, + { + "idx": 32, + "version": "6", + "when": 1753240775348, + "tag": "0032_orange_prima", + "breakpoints": true } ] } diff --git a/packages/internal/database/src/drizzle/migrations.js b/packages/internal/database/src/drizzle/migrations.js index da9f3dfbe..110dc1b24 100644 --- a/packages/internal/database/src/drizzle/migrations.js +++ b/packages/internal/database/src/drizzle/migrations.js @@ -32,6 +32,7 @@ import m0028 from "./0028_chief_cyclops.sql" import m0029 from "./0029_flaky_gorgon.sql" import m0030 from "./0030_common_gabe_jones.sql" import m0031 from "./0031_kind_ikaris.sql" +import m0032 from "./0032_orange_prima.sql" import journal from "./meta/_journal.json" export default { @@ -69,5 +70,6 @@ export default { m0029, m0030, m0031, + m0032, }, } diff --git a/packages/internal/database/src/schemas/index.ts b/packages/internal/database/src/schemas/index.ts index f188197f5..c3b431bc4 100644 --- a/packages/internal/database/src/schemas/index.ts +++ b/packages/internal/database/src/schemas/index.ts @@ -31,6 +31,7 @@ export const subscriptionsTable = sqliteTable("subscriptions", { userId: text("user_id").notNull(), view: integer("view").notNull().$type(), isPrivate: integer("is_private", { mode: "boolean" }).notNull(), + hideFromTimeline: integer("hide_from_timeline", { mode: "boolean" }), title: text("title"), category: text("category"), createdAt: text("created_at"), diff --git a/packages/internal/store/src/entry/store.ts b/packages/internal/store/src/entry/store.ts index cab745e23..be96f2e19 100644 --- a/packages/internal/store/src/entry/store.ts +++ b/packages/internal/store/src/entry/store.ts @@ -91,7 +91,9 @@ class EntryActions implements Hydratable, Resetable { if (!feedId) return const subscription = getSubscriptionById(feedId) - const ignore = hidePrivateSubscriptionsInTimeline && subscription?.isPrivate + const ignore = + (hidePrivateSubscriptionsInTimeline && subscription?.isPrivate) || + subscription?.hideFromTimeline if (typeof subscription?.view === "number" && !ignore) { draft.entryIdByView[subscription.view].add(entryId) @@ -100,7 +102,9 @@ class EntryActions implements Hydratable, Resetable { // lists for (const s of sources ?? []) { const subscription = getSubscriptionById(s) - const ignore = hidePrivateSubscriptionsInTimeline && subscription?.isPrivate + const ignore = + (hidePrivateSubscriptionsInTimeline && subscription?.isPrivate) || + subscription?.hideFromTimeline if (typeof subscription?.view === "number" && !ignore) { draft.entryIdByView[subscription.view].add(entryId) diff --git a/packages/internal/store/src/morph/api.ts b/packages/internal/store/src/morph/api.ts index 5af75f512..7810850c7 100644 --- a/packages/internal/store/src/morph/api.ts +++ b/packages/internal/store/src/morph/api.ts @@ -40,6 +40,8 @@ class APIMorph { userId: item.userId, view: item.view, isPrivate: item.isPrivate, + // @ts-expect-error update client sdk + hideFromTimeline: item.hideFromTimeline, title: item.title, createdAt: item.createdAt, } as SubscriptionModel diff --git a/packages/internal/store/src/subscription/types.ts b/packages/internal/store/src/subscription/types.ts index c37c8b08b..2530bfb66 100644 --- a/packages/internal/store/src/subscription/types.ts +++ b/packages/internal/store/src/subscription/types.ts @@ -1,14 +1,17 @@ import type { FeedViewType } from "@follow/constants" import type { SubscriptionSchema } from "@follow/database/schemas/types" +type Nullable = T | null | undefined + export interface SubscriptionForm { - url?: string + url: string | undefined view: FeedViewType - category?: string + category: Nullable isPrivate: boolean - title?: string | null - feedId?: string - listId?: string + hideFromTimeline: Nullable + title: Nullable + feedId: Nullable + listId: string | undefined } export type SubscriptionModel = Omit