Update saved feeds to use preferences

zio/stable
Paul Frazee 2023-05-18 14:39:04 -05:00
parent acea0e074d
commit 5537d19e55
8 changed files with 68 additions and 50 deletions

View File

@ -38,7 +38,7 @@ export class CustomFeedModel {
} }
get isSaved() { get isSaved() {
return this.data.viewer?.saved return this.rootStore.preferences.savedFeeds.includes(this.uri)
} }
get isLiked() { get isLiked() {
@ -49,23 +49,11 @@ export class CustomFeedModel {
// = // =
async save() { async save() {
await this.rootStore.agent.app.bsky.feed.saveFeed({ await this.rootStore.preferences.addSavedFeed(this.uri)
feed: this.uri,
})
runInAction(() => {
this.data.viewer = this.data.viewer || {}
this.data.viewer.saved = true
})
} }
async unsave() { async unsave() {
await this.rootStore.agent.app.bsky.feed.unsaveFeed({ await this.rootStore.preferences.removeSavedFeed(this.uri)
feed: this.uri,
})
runInAction(() => {
this.data.viewer = this.data.viewer || {}
this.data.viewer.saved = false
})
} }
async like() { async like() {
@ -82,7 +70,7 @@ export class CustomFeedModel {
} }
async unlike() { async unlike() {
if (!this.data.viewer.like) { if (!this.data.viewer?.like) {
return return
} }
try { try {

View File

@ -135,7 +135,7 @@ export class ImageModel implements RNImage {
// Only for mobile // Only for mobile
async crop() { async crop() {
try { try {
const cropped = await openCropper({ const cropped = await openCropper(this.rootStore, {
mediaType: 'photo', mediaType: 'photo',
path: this.path, path: this.path,
freeStyleCropEnabled: true, freeStyleCropEnabled: true,

View File

@ -46,6 +46,7 @@ export class PreferencesModel {
contentLanguages: string[] = contentLanguages: string[] =
deviceLocales?.map?.(locale => locale.languageCode) || [] deviceLocales?.map?.(locale => locale.languageCode) || []
contentLabels = new LabelPreferencesModel() contentLabels = new LabelPreferencesModel()
savedFeeds: string[] = []
pinnedFeeds: string[] = [] pinnedFeeds: string[] = []
constructor(public rootStore: RootStoreModel) { constructor(public rootStore: RootStoreModel) {
@ -56,6 +57,7 @@ export class PreferencesModel {
return { return {
contentLanguages: this.contentLanguages, contentLanguages: this.contentLanguages,
contentLabels: this.contentLabels, contentLabels: this.contentLabels,
savedFeeds: this.savedFeeds,
pinnedFeeds: this.pinnedFeeds, pinnedFeeds: this.pinnedFeeds,
} }
} }
@ -75,6 +77,13 @@ export class PreferencesModel {
// default to the device languages // default to the device languages
this.contentLanguages = deviceLocales.map(locale => locale.languageCode) 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 ( if (
hasProp(v, 'pinnedFeeds') && hasProp(v, 'pinnedFeeds') &&
Array.isArray(v.pinnedFeeds) && Array.isArray(v.pinnedFeeds) &&
@ -106,10 +115,11 @@ export class PreferencesModel {
pref.visibility as LabelPreference pref.visibility as LabelPreference
} }
} else if ( } else if (
AppBskyActorDefs.isPinnedFeedsPref(pref) && AppBskyActorDefs.isSavedFeedsPref(pref) &&
AppBskyActorDefs.validatePinnedFeedsPref(pref).success 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 return res
} }
async setPinnedFeeds(v: string[]) { async setSavedFeeds(saved: string[], pinned: string[]) {
const old = this.pinnedFeeds const oldSaved = this.savedFeeds
this.pinnedFeeds = v const oldPinned = this.pinnedFeeds
this.savedFeeds = saved
this.pinnedFeeds = pinned
try { try {
await this.update((prefs: AppBskyActorDefs.Preferences) => { await this.update((prefs: AppBskyActorDefs.Preferences) => {
const existing = prefs.find( const existing = prefs.find(
pref => pref =>
AppBskyActorDefs.isPinnedFeedsPref(pref) && AppBskyActorDefs.isSavedFeedsPref(pref) &&
AppBskyActorDefs.validatePinnedFeedsPref(pref).success, AppBskyActorDefs.validateSavedFeedsPref(pref).success,
) )
if (existing) { if (existing) {
existing.feeds = v existing.saved = saved
existing.pinned = pinned
} else { } else {
prefs.push({ prefs.push({
$type: 'app.bsky.actor.defs#pinnedFeedsPref', $type: 'app.bsky.actor.defs#savedFeedsPref',
feeds: v, saved,
pinned,
}) })
} }
}) })
} catch (e) { } catch (e) {
runInAction(() => { runInAction(() => {
this.pinnedFeeds = old this.savedFeeds = oldSaved
this.pinnedFeeds = oldPinned
}) })
throw e 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) { async addPinnedFeed(v: string) {
return this.setPinnedFeeds([...this.pinnedFeeds, v]) return this.setSavedFeeds(this.savedFeeds, [...this.pinnedFeeds, v])
} }
async removePinnedFeed(v: string) { async removePinnedFeed(v: string) {
return this.setPinnedFeeds(this.pinnedFeeds.filter(uri => uri !== v)) return this.setSavedFeeds(
this.savedFeeds,
this.pinnedFeeds.filter(uri => uri !== v),
)
} }
} }

View File

@ -5,8 +5,6 @@ import {bundleAsync} from 'lib/async/bundle'
import {cleanError} from 'lib/strings/errors' import {cleanError} from 'lib/strings/errors'
import {CustomFeedModel} from '../feeds/custom-feed' import {CustomFeedModel} from '../feeds/custom-feed'
const PAGE_SIZE = 100
export class SavedFeedsModel { export class SavedFeedsModel {
// state // state
isLoading = false isLoading = false
@ -69,16 +67,15 @@ export class SavedFeedsModel {
try { try {
let feeds: AppBskyFeedDefs.GeneratorView[] = [] let feeds: AppBskyFeedDefs.GeneratorView[] = []
let cursor let cursor
for (let i = 0; i < 100; i++) { for (
const res = await this.rootStore.agent.app.bsky.feed.getSavedFeeds({ let i = 0;
limit: PAGE_SIZE, i < this.rootStore.preferences.savedFeeds.length;
cursor, 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) feeds = feeds.concat(res.data.feeds)
cursor = res.data.cursor
if (!cursor) {
break
}
} }
runInAction(() => { runInAction(() => {
this.feeds = feeds.map(f => new CustomFeedModel(this.rootStore, f)) this.feeds = feeds.map(f => new CustomFeedModel(this.rootStore, f))
@ -127,7 +124,8 @@ export class SavedFeedsModel {
} }
async reorderPinnedFeeds(feeds: CustomFeedModel[]) { 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), feeds.filter(feed => this.isPinned(feed)).map(feed => feed.uri),
) )
} }
@ -151,7 +149,10 @@ export class SavedFeedsModel {
pinned[index] = pinned[index + 1] pinned[index] = pinned[index + 1]
pinned[index + 1] = temp pinned[index + 1] = temp
} }
await this.rootStore.preferences.setPinnedFeeds(pinned) await this.rootStore.preferences.setSavedFeeds(
this.rootStore.preferences.savedFeeds,
pinned,
)
} }
// state transitions // state transitions

View File

@ -119,7 +119,7 @@ export type Modal =
// Moderation // Moderation
| ReportAccountModal | ReportAccountModal
| ReportPostModal | ReportPostModal
| CreateMuteListModal | CreateOrEditMuteListModal
| ListAddRemoveUserModal | ListAddRemoveUserModal
// Posts // Posts

View File

@ -40,7 +40,7 @@ export const CustomFeed = observer(
const navigation = useNavigation<NavigationProp>() const navigation = useNavigation<NavigationProp>()
const onToggleSaved = React.useCallback(async () => { const onToggleSaved = React.useCallback(async () => {
if (item.data.viewer?.saved) { if (item.isSaved) {
store.shell.openModal({ store.shell.openModal({
name: 'confirm', name: 'confirm',
title: 'Remove from my feeds', title: 'Remove from my feeds',

View File

@ -121,7 +121,7 @@ const Container = observer(
}: { }: {
children: React.ReactNode children: React.ReactNode
hideOnScroll: boolean hideOnScroll: boolean
showBorder: boolean showBorder?: boolean
}) => { }) => {
const store = useStores() const store = useStores()
const pal = usePalette('default') const pal = usePalette('default')

View File

@ -27,6 +27,10 @@ export function ImageHider({
setOverride(false) setOverride(false)
}, [setOverride]) }, [setOverride])
if (moderation.behavior === ModerationBehaviorCode.Hide) {
return null
}
if (moderation.behavior !== ModerationBehaviorCode.WarnImages) { if (moderation.behavior !== ModerationBehaviorCode.WarnImages) {
return ( return (
<View testID={testID} style={style}> <View testID={testID} style={style}>
@ -35,10 +39,6 @@ export function ImageHider({
) )
} }
if (moderation.behavior === ModerationBehaviorCode.Hide) {
return null
}
return ( return (
<View style={[styles.container, containerStyle]}> <View style={[styles.container, containerStyle]}>
<View testID={testID} style={style}> <View testID={testID} style={style}>