Update saved feeds to use preferences
parent
acea0e074d
commit
5537d19e55
|
@ -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 {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -119,7 +119,7 @@ export type Modal =
|
||||||
// Moderation
|
// Moderation
|
||||||
| ReportAccountModal
|
| ReportAccountModal
|
||||||
| ReportPostModal
|
| ReportPostModal
|
||||||
| CreateMuteListModal
|
| CreateOrEditMuteListModal
|
||||||
| ListAddRemoveUserModal
|
| ListAddRemoveUserModal
|
||||||
|
|
||||||
// Posts
|
// Posts
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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}>
|
||||||
|
|
Loading…
Reference in New Issue