From 5537d19e555c39f5f9a0ec16735ea4c3860357c4 Mon Sep 17 00:00:00 2001 From: Paul Frazee Date: Thu, 18 May 2023 14:39:04 -0500 Subject: [PATCH] Update saved feeds to use preferences --- src/state/models/feeds/custom-feed.ts | 20 ++------ src/state/models/media/image.ts | 2 +- src/state/models/ui/preferences.ts | 57 ++++++++++++++++----- src/state/models/ui/saved-feeds.ts | 25 ++++----- src/state/models/ui/shell.ts | 2 +- src/view/com/feeds/CustomFeed.tsx | 2 +- src/view/com/util/ViewHeader.tsx | 2 +- src/view/com/util/moderation/ImageHider.tsx | 8 +-- 8 files changed, 68 insertions(+), 50 deletions(-) diff --git a/src/state/models/feeds/custom-feed.ts b/src/state/models/feeds/custom-feed.ts index 5e550ec6..e457d2d1 100644 --- a/src/state/models/feeds/custom-feed.ts +++ b/src/state/models/feeds/custom-feed.ts @@ -38,7 +38,7 @@ export class CustomFeedModel { } get isSaved() { - return this.data.viewer?.saved + return this.rootStore.preferences.savedFeeds.includes(this.uri) } get isLiked() { @@ -49,23 +49,11 @@ export class CustomFeedModel { // = async save() { - await this.rootStore.agent.app.bsky.feed.saveFeed({ - feed: this.uri, - }) - runInAction(() => { - this.data.viewer = this.data.viewer || {} - this.data.viewer.saved = true - }) + await this.rootStore.preferences.addSavedFeed(this.uri) } async unsave() { - await this.rootStore.agent.app.bsky.feed.unsaveFeed({ - feed: this.uri, - }) - runInAction(() => { - this.data.viewer = this.data.viewer || {} - this.data.viewer.saved = false - }) + await this.rootStore.preferences.removeSavedFeed(this.uri) } async like() { @@ -82,7 +70,7 @@ export class CustomFeedModel { } async unlike() { - if (!this.data.viewer.like) { + if (!this.data.viewer?.like) { return } try { diff --git a/src/state/models/media/image.ts b/src/state/models/media/image.ts index ec93bf5b..6edf88d9 100644 --- a/src/state/models/media/image.ts +++ b/src/state/models/media/image.ts @@ -135,7 +135,7 @@ export class ImageModel implements RNImage { // Only for mobile async crop() { try { - const cropped = await openCropper({ + const cropped = await openCropper(this.rootStore, { mediaType: 'photo', path: this.path, freeStyleCropEnabled: true, diff --git a/src/state/models/ui/preferences.ts b/src/state/models/ui/preferences.ts index 05a1eb12..120b4adc 100644 --- a/src/state/models/ui/preferences.ts +++ b/src/state/models/ui/preferences.ts @@ -46,6 +46,7 @@ export class PreferencesModel { contentLanguages: string[] = deviceLocales?.map?.(locale => locale.languageCode) || [] contentLabels = new LabelPreferencesModel() + savedFeeds: string[] = [] pinnedFeeds: string[] = [] constructor(public rootStore: RootStoreModel) { @@ -56,6 +57,7 @@ export class PreferencesModel { return { contentLanguages: this.contentLanguages, contentLabels: this.contentLabels, + savedFeeds: this.savedFeeds, pinnedFeeds: this.pinnedFeeds, } } @@ -75,6 +77,13 @@ export class PreferencesModel { // default to the device languages this.contentLanguages = deviceLocales.map(locale => locale.languageCode) } + if ( + hasProp(v, 'savedFeeds') && + Array.isArray(v.savedFeeds) && + typeof v.savedFeeds.every(item => typeof item === 'string') + ) { + this.savedFeeds = v.savedFeeds + } if ( hasProp(v, 'pinnedFeeds') && Array.isArray(v.pinnedFeeds) && @@ -106,10 +115,11 @@ export class PreferencesModel { pref.visibility as LabelPreference } } else if ( - AppBskyActorDefs.isPinnedFeedsPref(pref) && - AppBskyActorDefs.validatePinnedFeedsPref(pref).success + AppBskyActorDefs.isSavedFeedsPref(pref) && + AppBskyActorDefs.validateSavedFeedsPref(pref).success ) { - this.pinnedFeeds = pref.feeds + this.savedFeeds = pref.saved + this.pinnedFeeds = pref.pinned } } }) @@ -220,38 +230,57 @@ export class PreferencesModel { return res } - async setPinnedFeeds(v: string[]) { - const old = this.pinnedFeeds - this.pinnedFeeds = v + async setSavedFeeds(saved: string[], pinned: string[]) { + const oldSaved = this.savedFeeds + const oldPinned = this.pinnedFeeds + this.savedFeeds = saved + this.pinnedFeeds = pinned try { await this.update((prefs: AppBskyActorDefs.Preferences) => { const existing = prefs.find( pref => - AppBskyActorDefs.isPinnedFeedsPref(pref) && - AppBskyActorDefs.validatePinnedFeedsPref(pref).success, + AppBskyActorDefs.isSavedFeedsPref(pref) && + AppBskyActorDefs.validateSavedFeedsPref(pref).success, ) if (existing) { - existing.feeds = v + existing.saved = saved + existing.pinned = pinned } else { prefs.push({ - $type: 'app.bsky.actor.defs#pinnedFeedsPref', - feeds: v, + $type: 'app.bsky.actor.defs#savedFeedsPref', + saved, + pinned, }) } }) } catch (e) { runInAction(() => { - this.pinnedFeeds = old + this.savedFeeds = oldSaved + this.pinnedFeeds = oldPinned }) throw e } } + async addSavedFeed(v: string) { + return this.setSavedFeeds([...this.savedFeeds, v], this.pinnedFeeds) + } + + async removeSavedFeed(v: string) { + return this.setSavedFeeds( + this.savedFeeds.filter(uri => uri !== v), + this.pinnedFeeds.filter(uri => uri !== v), + ) + } + async addPinnedFeed(v: string) { - return this.setPinnedFeeds([...this.pinnedFeeds, v]) + return this.setSavedFeeds(this.savedFeeds, [...this.pinnedFeeds, v]) } async removePinnedFeed(v: string) { - return this.setPinnedFeeds(this.pinnedFeeds.filter(uri => uri !== v)) + return this.setSavedFeeds( + this.savedFeeds, + this.pinnedFeeds.filter(uri => uri !== v), + ) } } diff --git a/src/state/models/ui/saved-feeds.ts b/src/state/models/ui/saved-feeds.ts index f500aef2..9de28e02 100644 --- a/src/state/models/ui/saved-feeds.ts +++ b/src/state/models/ui/saved-feeds.ts @@ -5,8 +5,6 @@ import {bundleAsync} from 'lib/async/bundle' import {cleanError} from 'lib/strings/errors' import {CustomFeedModel} from '../feeds/custom-feed' -const PAGE_SIZE = 100 - export class SavedFeedsModel { // state isLoading = false @@ -69,16 +67,15 @@ export class SavedFeedsModel { try { let feeds: AppBskyFeedDefs.GeneratorView[] = [] let cursor - for (let i = 0; i < 100; i++) { - const res = await this.rootStore.agent.app.bsky.feed.getSavedFeeds({ - limit: PAGE_SIZE, - cursor, + for ( + let i = 0; + i < this.rootStore.preferences.savedFeeds.length; + i += 25 + ) { + const res = await this.rootStore.agent.app.bsky.feed.getFeedGenerators({ + feeds: this.rootStore.preferences.savedFeeds.slice(i, 25), }) feeds = feeds.concat(res.data.feeds) - cursor = res.data.cursor - if (!cursor) { - break - } } runInAction(() => { this.feeds = feeds.map(f => new CustomFeedModel(this.rootStore, f)) @@ -127,7 +124,8 @@ export class SavedFeedsModel { } async reorderPinnedFeeds(feeds: CustomFeedModel[]) { - return this.rootStore.preferences.setPinnedFeeds( + return this.rootStore.preferences.setSavedFeeds( + this.rootStore.preferences.savedFeeds, feeds.filter(feed => this.isPinned(feed)).map(feed => feed.uri), ) } @@ -151,7 +149,10 @@ export class SavedFeedsModel { pinned[index] = pinned[index + 1] pinned[index + 1] = temp } - await this.rootStore.preferences.setPinnedFeeds(pinned) + await this.rootStore.preferences.setSavedFeeds( + this.rootStore.preferences.savedFeeds, + pinned, + ) } // state transitions diff --git a/src/state/models/ui/shell.ts b/src/state/models/ui/shell.ts index 9b9a176b..95b66624 100644 --- a/src/state/models/ui/shell.ts +++ b/src/state/models/ui/shell.ts @@ -119,7 +119,7 @@ export type Modal = // Moderation | ReportAccountModal | ReportPostModal - | CreateMuteListModal + | CreateOrEditMuteListModal | ListAddRemoveUserModal // Posts diff --git a/src/view/com/feeds/CustomFeed.tsx b/src/view/com/feeds/CustomFeed.tsx index d4e843b6..9a71eb84 100644 --- a/src/view/com/feeds/CustomFeed.tsx +++ b/src/view/com/feeds/CustomFeed.tsx @@ -40,7 +40,7 @@ export const CustomFeed = observer( const navigation = useNavigation() const onToggleSaved = React.useCallback(async () => { - if (item.data.viewer?.saved) { + if (item.isSaved) { store.shell.openModal({ name: 'confirm', title: 'Remove from my feeds', diff --git a/src/view/com/util/ViewHeader.tsx b/src/view/com/util/ViewHeader.tsx index 7f13f183..c17a65b1 100644 --- a/src/view/com/util/ViewHeader.tsx +++ b/src/view/com/util/ViewHeader.tsx @@ -121,7 +121,7 @@ const Container = observer( }: { children: React.ReactNode hideOnScroll: boolean - showBorder: boolean + showBorder?: boolean }) => { const store = useStores() const pal = usePalette('default') diff --git a/src/view/com/util/moderation/ImageHider.tsx b/src/view/com/util/moderation/ImageHider.tsx index b42c6397..40add5b6 100644 --- a/src/view/com/util/moderation/ImageHider.tsx +++ b/src/view/com/util/moderation/ImageHider.tsx @@ -27,6 +27,10 @@ export function ImageHider({ setOverride(false) }, [setOverride]) + if (moderation.behavior === ModerationBehaviorCode.Hide) { + return null + } + if (moderation.behavior !== ModerationBehaviorCode.WarnImages) { return ( @@ -35,10 +39,6 @@ export function ImageHider({ ) } - if (moderation.behavior === ModerationBehaviorCode.Hide) { - return null - } - return (