diff options
Diffstat (limited to 'src/state')
-rw-r--r-- | src/state/models/content/post-thread.ts | 40 | ||||
-rw-r--r-- | src/state/models/content/post.ts | 19 | ||||
-rw-r--r-- | src/state/models/feeds/notifications.ts | 46 | ||||
-rw-r--r-- | src/state/models/feeds/posts.ts | 19 | ||||
-rw-r--r-- | src/state/models/muted-threads.ts | 29 | ||||
-rw-r--r-- | src/state/models/root-store.ts | 6 |
6 files changed, 148 insertions, 11 deletions
diff --git a/src/state/models/content/post-thread.ts b/src/state/models/content/post-thread.ts index 794beae20..acc9bffa9 100644 --- a/src/state/models/content/post-thread.ts +++ b/src/state/models/content/post-thread.ts @@ -42,6 +42,17 @@ export class PostThreadItemModel { return this.postRecord?.reply?.parent.uri } + get rootUri(): string { + if (this.postRecord?.reply?.root.uri) { + return this.postRecord.reply.root.uri + } + return this.uri + } + + get isThreadMuted() { + return this.rootStore.mutedThreads.uris.has(this.rootUri) + } + constructor( public rootStore: RootStoreModel, reactKey: string, @@ -188,6 +199,14 @@ export class PostThreadItemModel { } } + async toggleThreadMute() { + if (this.isThreadMuted) { + this.rootStore.mutedThreads.uris.delete(this.rootUri) + } else { + this.rootStore.mutedThreads.uris.add(this.rootUri) + } + } + async delete() { await this.rootStore.agent.deletePost(this.post.uri) this.rootStore.emitPostDeleted(this.post.uri) @@ -230,6 +249,19 @@ export class PostThreadModel { return this.error !== '' } + get rootUri(): string { + if (this.thread) { + if (this.thread.postRecord?.reply?.root.uri) { + return this.thread.postRecord.reply.root.uri + } + } + return this.resolvedUri + } + + get isThreadMuted() { + return this.rootStore.mutedThreads.uris.has(this.rootUri) + } + // public api // = @@ -279,6 +311,14 @@ export class PostThreadModel { this.refresh() } + async toggleThreadMute() { + if (this.isThreadMuted) { + this.rootStore.mutedThreads.uris.delete(this.rootUri) + } else { + this.rootStore.mutedThreads.uris.add(this.rootUri) + } + } + // state transitions // = diff --git a/src/state/models/content/post.ts b/src/state/models/content/post.ts index b5d95bf01..7ba633366 100644 --- a/src/state/models/content/post.ts +++ b/src/state/models/content/post.ts @@ -48,6 +48,17 @@ export class PostModel implements RemoveIndex<Post.Record> { return this.hasLoaded && !this.hasContent } + get rootUri(): string { + if (this.reply?.root.uri) { + return this.reply.root.uri + } + return this.uri + } + + get isThreadMuted() { + return this.rootStore.mutedThreads.uris.has(this.rootUri) + } + // public api // = @@ -55,6 +66,14 @@ export class PostModel implements RemoveIndex<Post.Record> { await this._load() } + async toggleThreadMute() { + if (this.isThreadMuted) { + this.rootStore.mutedThreads.uris.delete(this.rootUri) + } else { + this.rootStore.mutedThreads.uris.add(this.rootUri) + } + } + // state transitions // = diff --git a/src/state/models/feeds/notifications.ts b/src/state/models/feeds/notifications.ts index ff77ab979..e2a18ea04 100644 --- a/src/state/models/feeds/notifications.ts +++ b/src/state/models/feeds/notifications.ts @@ -160,6 +160,13 @@ export class NotificationsFeedItemModel { return '' } + get reasonSubjectRootUri(): string | undefined { + if (this.additionalPost) { + return this.additionalPost.rootUri + } + return undefined + } + toSupportedRecord(v: unknown): SupportedRecord | undefined { for (const ns of [ AppBskyFeedPost, @@ -227,7 +234,7 @@ export class NotificationsFeedModel { // data notifications: NotificationsFeedItemModel[] = [] - queuedNotifications: undefined | ListNotifications.Notification[] = undefined + queuedNotifications: undefined | NotificationsFeedItemModel[] = undefined unreadCount = 0 // this is used to help trigger push notifications @@ -354,7 +361,13 @@ export class NotificationsFeedModel { queue.push(notif) } - this._setQueued(this._filterNotifications(queue)) + // NOTE + // because filtering depends on the added information we have to fetch + // the full models here. this is *not* ideal performance and we need + // to update the notifications route to give all the info we need + // -prf + const queueModels = await this._fetchItemModels(queue) + this._setQueued(this._filterNotifications(queueModels)) this._countUnread() } catch (e) { this.rootStore.log.error('NotificationsModel:syncQueue failed', {e}) @@ -452,7 +465,8 @@ export class NotificationsFeedModel { res.data.notifications[0], ) await notif.fetchAdditionalData() - return notif + const filtered = this._filterNotifications([notif]) + return filtered[0] } // state transitions @@ -505,23 +519,26 @@ export class NotificationsFeedModel { } _filterNotifications( - items: ListNotifications.Notification[], - ): ListNotifications.Notification[] { + items: NotificationsFeedItemModel[], + ): NotificationsFeedItemModel[] { return items.filter(item => { - return ( - this.rootStore.preferences.getLabelPreference(item.labels).pref !== + const hideByLabel = + this.rootStore.preferences.getLabelPreference(item.labels).pref === 'hide' + let mutedThread = !!( + item.reasonSubjectRootUri && + this.rootStore.mutedThreads.uris.has(item.reasonSubjectRootUri) ) + return !hideByLabel && !mutedThread }) } - async _processNotifications( + async _fetchItemModels( items: ListNotifications.Notification[], ): Promise<NotificationsFeedItemModel[]> { const promises = [] const itemModels: NotificationsFeedItemModel[] = [] - items = this._filterNotifications(items) - for (const item of groupNotifications(items)) { + for (const item of items) { const itemModel = new NotificationsFeedItemModel( this.rootStore, `item-${_idCounter++}`, @@ -541,7 +558,14 @@ export class NotificationsFeedModel { return itemModels } - _setQueued(queued: undefined | ListNotifications.Notification[]) { + async _processNotifications( + items: ListNotifications.Notification[], + ): Promise<NotificationsFeedItemModel[]> { + const itemModels = await this._fetchItemModels(groupNotifications(items)) + return this._filterNotifications(itemModels) + } + + _setQueued(queued: undefined | NotificationsFeedItemModel[]) { this.queuedNotifications = queued } diff --git a/src/state/models/feeds/posts.ts b/src/state/models/feeds/posts.ts index 38faf658a..58167284d 100644 --- a/src/state/models/feeds/posts.ts +++ b/src/state/models/feeds/posts.ts @@ -72,6 +72,17 @@ export class PostsFeedItemModel { makeAutoObservable(this, {rootStore: false}) } + get rootUri(): string { + if (this.reply?.root.uri) { + return this.reply.root.uri + } + return this.post.uri + } + + get isThreadMuted() { + return this.rootStore.mutedThreads.uris.has(this.rootUri) + } + copy(v: FeedViewPost) { this.post = v.post this.reply = v.reply @@ -145,6 +156,14 @@ export class PostsFeedItemModel { } } + async toggleThreadMute() { + if (this.isThreadMuted) { + this.rootStore.mutedThreads.uris.delete(this.rootUri) + } else { + this.rootStore.mutedThreads.uris.add(this.rootUri) + } + } + async delete() { await this.rootStore.agent.deletePost(this.post.uri) this.rootStore.emitPostDeleted(this.post.uri) diff --git a/src/state/models/muted-threads.ts b/src/state/models/muted-threads.ts new file mode 100644 index 000000000..e6f202745 --- /dev/null +++ b/src/state/models/muted-threads.ts @@ -0,0 +1,29 @@ +/** + * This is a temporary client-side system for storing muted threads + * When the system lands on prod we should switch to that + */ + +import {makeAutoObservable} from 'mobx' +import {isObj, hasProp, isStrArray} from 'lib/type-guards' + +export class MutedThreads { + uris: Set<string> = new Set() + + constructor() { + makeAutoObservable( + this, + {serialize: false, hydrate: false}, + {autoBind: true}, + ) + } + + serialize() { + return {uris: Array.from(this.uris)} + } + + hydrate(v: unknown) { + if (isObj(v) && hasProp(v, 'uris') && isStrArray(v.uris)) { + this.uris = new Set(v.uris) + } + } +} diff --git a/src/state/models/root-store.ts b/src/state/models/root-store.ts index 9207f27ba..b3e744a40 100644 --- a/src/state/models/root-store.ts +++ b/src/state/models/root-store.ts @@ -20,6 +20,7 @@ import {InvitedUsers} from './invited-users' import {PreferencesModel} from './ui/preferences' import {resetToTab} from '../../Navigation' import {ImageSizesCache} from './cache/image-sizes' +import {MutedThreads} from './muted-threads' export const appInfo = z.object({ build: z.string(), @@ -41,6 +42,7 @@ export class RootStoreModel { profiles = new ProfilesCache(this) linkMetas = new LinkMetasCache(this) imageSizes = new ImageSizesCache() + mutedThreads = new MutedThreads() constructor(agent: BskyAgent) { this.agent = agent @@ -64,6 +66,7 @@ export class RootStoreModel { shell: this.shell.serialize(), preferences: this.preferences.serialize(), invitedUsers: this.invitedUsers.serialize(), + mutedThreads: this.mutedThreads.serialize(), } } @@ -90,6 +93,9 @@ export class RootStoreModel { if (hasProp(v, 'invitedUsers')) { this.invitedUsers.hydrate(v.invitedUsers) } + if (hasProp(v, 'mutedThreads')) { + this.mutedThreads.hydrate(v.mutedThreads) + } } } |