diff options
Diffstat (limited to 'src/state/models/content')
-rw-r--r-- | src/state/models/content/list.ts | 2 | ||||
-rw-r--r-- | src/state/models/content/post-thread-item.ts | 8 | ||||
-rw-r--r-- | src/state/models/content/post-thread.ts | 101 | ||||
-rw-r--r-- | src/state/models/content/profile.ts | 58 |
4 files changed, 124 insertions, 45 deletions
diff --git a/src/state/models/content/list.ts b/src/state/models/content/list.ts index 2498cf581..5d4ffb4fa 100644 --- a/src/state/models/content/list.ts +++ b/src/state/models/content/list.ts @@ -306,7 +306,7 @@ export class ListModel { this.hasMore = !!this.loadMoreCursor this.list = res.data.list this.items = this.items.concat( - res.data.items.map(item => ({...item, _reactKey: item.subject})), + res.data.items.map(item => ({...item, _reactKey: item.subject.did})), ) } } diff --git a/src/state/models/content/post-thread-item.ts b/src/state/models/content/post-thread-item.ts index 14aa607ed..942f3acc8 100644 --- a/src/state/models/content/post-thread-item.ts +++ b/src/state/models/content/post-thread-item.ts @@ -3,9 +3,9 @@ import { AppBskyFeedPost as FeedPost, AppBskyFeedDefs, RichText, + PostModeration, } from '@atproto/api' import {RootStoreModel} from '../root-store' -import {PostLabelInfo, PostModeration} from 'lib/labeling/types' import {PostsFeedItemModel} from '../feeds/post' type PostView = AppBskyFeedDefs.PostView @@ -67,10 +67,6 @@ export class PostThreadItemModel { return this.data.isThreadMuted } - get labelInfo(): PostLabelInfo { - return this.data.labelInfo - } - get moderation(): PostModeration { return this.data.moderation } @@ -111,7 +107,7 @@ export class PostThreadItemModel { const itemModel = new PostThreadItemModel(this.rootStore, item) itemModel._depth = this._depth + 1 itemModel._showParentReplyLine = - itemModel.parentUri !== highlightedPostUri && replies.length === 0 + itemModel.parentUri !== highlightedPostUri if (item.replies?.length) { itemModel._showChildReplyLine = true itemModel.assignTreeModels(item, highlightedPostUri, false, true) diff --git a/src/state/models/content/post-thread.ts b/src/state/models/content/post-thread.ts index 0a67c783e..85ed13cb4 100644 --- a/src/state/models/content/post-thread.ts +++ b/src/state/models/content/post-thread.ts @@ -2,6 +2,7 @@ import {makeAutoObservable, runInAction} from 'mobx' import { AppBskyFeedGetPostThread as GetPostThread, AppBskyFeedDefs, + PostModeration, } from '@atproto/api' import {AtUri} from '@atproto/api' import {RootStoreModel} from '../root-store' @@ -12,6 +13,8 @@ import {PostThreadItemModel} from './post-thread-item' export class PostThreadModel { // state isLoading = false + isLoadingFromCache = false + isFromCache = false isRefreshing = false hasLoaded = false error = '' @@ -20,7 +23,7 @@ export class PostThreadModel { params: GetPostThread.QueryParams // data - thread?: PostThreadItemModel + thread?: PostThreadItemModel | null = null isBlocked = false constructor( @@ -52,7 +55,7 @@ export class PostThreadModel { } get hasContent() { - return typeof this.thread !== 'undefined' + return !!this.thread } get hasError() { @@ -82,10 +85,16 @@ export class PostThreadModel { if (!this.resolvedUri) { await this._resolveUri() } + if (this.hasContent) { await this.update() } else { - await this._load() + const precache = this.rootStore.posts.cache.get(this.resolvedUri) + if (precache) { + await this._loadPrecached(precache) + } else { + await this._load() + } } } @@ -169,6 +178,37 @@ export class PostThreadModel { }) } + async _loadPrecached(precache: AppBskyFeedDefs.PostView) { + // start with the cached version + this.isLoadingFromCache = true + this.isFromCache = true + this._replaceAll({ + success: true, + headers: {}, + data: { + thread: { + post: precache, + }, + }, + }) + this._xIdle() + + // then update in the background + try { + const res = await this.rootStore.agent.getPostThread( + Object.assign({}, this.params, {uri: this.resolvedUri}), + ) + this._replaceAll(res) + } catch (e: any) { + console.log(e) + this._xIdle(e) + } finally { + runInAction(() => { + this.isLoadingFromCache = false + }) + } + } + async _load(isRefreshing = false) { if (this.hasLoaded && !isRefreshing) { return @@ -192,7 +232,6 @@ export class PostThreadModel { return } pruneReplies(res.data.thread) - sortThread(res.data.thread) const thread = new PostThreadItemModel( this.rootStore, res.data.thread as AppBskyFeedDefs.ThreadViewPost, @@ -202,6 +241,7 @@ export class PostThreadModel { res.data.thread as AppBskyFeedDefs.ThreadViewPost, thread.uri, ) + sortThread(thread) this.thread = thread } } @@ -223,24 +263,28 @@ function pruneReplies(post: MaybePost) { } } -function sortThread(post: MaybePost) { - if (post.notFound) { +type MaybeThreadItem = + | PostThreadItemModel + | AppBskyFeedDefs.NotFoundPost + | AppBskyFeedDefs.BlockedPost +function sortThread(item: MaybeThreadItem) { + if ('notFound' in item) { return } - post = post as AppBskyFeedDefs.ThreadViewPost - if (post.replies) { - post.replies.sort((a: MaybePost, b: MaybePost) => { - post = post as AppBskyFeedDefs.ThreadViewPost - if (a.notFound) { + item = item as PostThreadItemModel + if (item.replies) { + item.replies.sort((a: MaybeThreadItem, b: MaybeThreadItem) => { + if ('notFound' in a && a.notFound) { return 1 } - if (b.notFound) { + if ('notFound' in b && b.notFound) { return -1 } - a = a as AppBskyFeedDefs.ThreadViewPost - b = b as AppBskyFeedDefs.ThreadViewPost - const aIsByOp = a.post.author.did === post.post.author.did - const bIsByOp = b.post.author.did === post.post.author.did + item = item as PostThreadItemModel + a = a as PostThreadItemModel + b = b as PostThreadItemModel + const aIsByOp = a.post.author.did === item.post.author.did + const bIsByOp = b.post.author.did === item.post.author.did if (aIsByOp && bIsByOp) { return a.post.indexedAt.localeCompare(b.post.indexedAt) // oldest } else if (aIsByOp) { @@ -248,8 +292,31 @@ function sortThread(post: MaybePost) { } else if (bIsByOp) { return 1 // op's own reply } + // put moderated content down at the bottom + if (modScore(a.moderation) !== modScore(b.moderation)) { + return modScore(a.moderation) - modScore(b.moderation) + } return b.post.indexedAt.localeCompare(a.post.indexedAt) // newest }) - post.replies.forEach(reply => sortThread(reply)) + item.replies.forEach(reply => sortThread(reply)) + } +} + +function modScore(mod: PostModeration): number { + if (mod.content.blur && mod.content.noOverride) { + return 5 + } + if (mod.content.blur) { + return 4 + } + if (mod.content.alert) { + return 3 + } + if (mod.embed.blur && mod.embed.noOverride) { + return 2 + } + if (mod.embed.blur) { + return 1 } + return 0 } diff --git a/src/state/models/content/profile.ts b/src/state/models/content/profile.ts index 34b2ea28e..26fa6008c 100644 --- a/src/state/models/content/profile.ts +++ b/src/state/models/content/profile.ts @@ -6,18 +6,14 @@ import { AppBskyActorGetProfile as GetProfile, AppBskyActorProfile, RichText, + moderateProfile, + ProfileModeration, } from '@atproto/api' import {RootStoreModel} from '../root-store' import * as apilib from 'lib/api/index' import {cleanError} from 'lib/strings/errors' import {FollowState} from '../cache/my-follows' import {Image as RNImage} from 'react-native-image-crop-picker' -import {ProfileLabelInfo, ProfileModeration} from 'lib/labeling/types' -import { - getProfileModeration, - filterAccountLabels, - filterProfileLabels, -} from 'lib/labeling/helpers' import {track} from 'lib/analytics/analytics' export class ProfileViewerModel { @@ -26,7 +22,8 @@ export class ProfileViewerModel { following?: string followedBy?: string blockedBy?: boolean - blocking?: string + blocking?: string; + [key: string]: unknown constructor() { makeAutoObservable(this) @@ -53,7 +50,8 @@ export class ProfileModel { followsCount: number = 0 postsCount: number = 0 labels?: ComAtprotoLabelDefs.Label[] = undefined - viewer = new ProfileViewerModel() + viewer = new ProfileViewerModel(); + [key: string]: unknown // added data descriptionRichText?: RichText = new RichText({text: ''}) @@ -85,25 +83,20 @@ export class ProfileModel { return this.hasLoaded && !this.hasContent } - get labelInfo(): ProfileLabelInfo { - return { - accountLabels: filterAccountLabels(this.labels), - profileLabels: filterProfileLabels(this.labels), - isMuted: this.viewer?.muted || false, - isBlocking: !!this.viewer?.blocking || false, - isBlockedBy: !!this.viewer?.blockedBy || false, - } - } - get moderation(): ProfileModeration { - return getProfileModeration(this.rootStore, this.labelInfo) + return moderateProfile(this, this.rootStore.preferences.moderationOpts) } // public api // = async setup() { - await this._load() + const precache = await this.rootStore.profiles.cache.get(this.params.actor) + if (precache) { + await this._loadWithCache(precache) + } else { + await this._load() + } } async refresh() { @@ -252,7 +245,13 @@ export class ProfileModel { this._xLoading(isRefreshing) try { const res = await this.rootStore.agent.getProfile(this.params) - this.rootStore.profiles.overwrite(this.params.actor, res) // cache invalidation + this.rootStore.profiles.overwrite(this.params.actor, res) + if (res.data.handle) { + this.rootStore.handleResolutions.cache.set( + res.data.handle, + res.data.did, + ) + } this._replaceAll(res) await this._createRichText() this._xIdle() @@ -261,6 +260,23 @@ export class ProfileModel { } } + async _loadWithCache(precache: GetProfile.Response) { + // use cached value + this._replaceAll(precache) + await this._createRichText() + this._xIdle() + + // fetch latest + try { + const res = await this.rootStore.agent.getProfile(this.params) + this.rootStore.profiles.overwrite(this.params.actor, res) // cache invalidation + this._replaceAll(res) + await this._createRichText() + } catch (e: any) { + this._xIdle(e) + } + } + _replaceAll(res: GetProfile.Response) { this.did = res.data.did this.handle = res.data.handle |