diff options
author | Paul Frazee <pfrazee@gmail.com> | 2023-05-25 20:02:37 -0500 |
---|---|---|
committer | Paul Frazee <pfrazee@gmail.com> | 2023-05-25 20:02:37 -0500 |
commit | 257686f3603e800e355850a23b3a4011e5558aeb (patch) | |
tree | 7bd6863f48c1362741a6e83b0aa56d70c1f9d1e5 /src/state/models/feeds/posts.ts | |
parent | df6d249e8570a5dabd576d81ea7fc8ac4517ffa6 (diff) | |
download | voidsky-257686f3603e800e355850a23b3a4011e5558aeb.tar.zst |
Add feeds tab
Diffstat (limited to 'src/state/models/feeds/posts.ts')
-rw-r--r-- | src/state/models/feeds/posts.ts | 270 |
1 files changed, 5 insertions, 265 deletions
diff --git a/src/state/models/feeds/posts.ts b/src/state/models/feeds/posts.ts index ac32044b4..02ef5f38b 100644 --- a/src/state/models/feeds/posts.ts +++ b/src/state/models/feeds/posts.ts @@ -1,11 +1,8 @@ import {makeAutoObservable, runInAction} from 'mobx' import { AppBskyFeedGetTimeline as GetTimeline, - AppBskyFeedDefs, - AppBskyFeedPost, AppBskyFeedGetAuthorFeed as GetAuthorFeed, AppBskyFeedGetFeed as GetCustomFeed, - RichText, } from '@atproto/api' import AwaitLock from 'await-lock' import {bundleAsync} from 'lib/async/bundle' @@ -19,269 +16,11 @@ import { mergePosts, } from 'lib/api/build-suggested-posts' import {FeedTuner, FeedViewPostsSlice} from 'lib/api/feed-manip' -import {updateDataOptimistically} from 'lib/async/revertible' -import {PostLabelInfo, PostModeration} from 'lib/labeling/types' -import { - getEmbedLabels, - getEmbedMuted, - getEmbedMutedByList, - getEmbedBlocking, - getEmbedBlockedBy, - getPostModeration, - mergePostModerations, - filterAccountLabels, - filterProfileLabels, -} from 'lib/labeling/helpers' - -type FeedViewPost = AppBskyFeedDefs.FeedViewPost -type ReasonRepost = AppBskyFeedDefs.ReasonRepost -type PostView = AppBskyFeedDefs.PostView +import {PostsFeedSliceModel} from './post' const PAGE_SIZE = 30 let _idCounter = 0 -export class PostsFeedItemModel { - // ui state - _reactKey: string = '' - - // data - post: PostView - postRecord?: AppBskyFeedPost.Record - reply?: FeedViewPost['reply'] - reason?: FeedViewPost['reason'] - richText?: RichText - - constructor( - public rootStore: RootStoreModel, - reactKey: string, - v: FeedViewPost, - ) { - this._reactKey = reactKey - this.post = v.post - if (AppBskyFeedPost.isRecord(this.post.record)) { - const valid = AppBskyFeedPost.validateRecord(this.post.record) - if (valid.success) { - this.postRecord = this.post.record - this.richText = new RichText(this.postRecord, {cleanNewlines: true}) - } else { - this.postRecord = undefined - this.richText = undefined - rootStore.log.warn( - 'Received an invalid app.bsky.feed.post record', - valid.error, - ) - } - } else { - this.postRecord = undefined - this.richText = undefined - rootStore.log.warn( - 'app.bsky.feed.getTimeline or app.bsky.feed.getAuthorFeed served an unexpected record type', - this.post.record, - ) - } - this.reply = v.reply - this.reason = v.reason - 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) - } - - get labelInfo(): PostLabelInfo { - return { - postLabels: (this.post.labels || []).concat( - getEmbedLabels(this.post.embed), - ), - accountLabels: filterAccountLabels(this.post.author.labels), - profileLabels: filterProfileLabels(this.post.author.labels), - isMuted: - this.post.author.viewer?.muted || - getEmbedMuted(this.post.embed) || - false, - mutedByList: - this.post.author.viewer?.mutedByList || - getEmbedMutedByList(this.post.embed), - isBlocking: - !!this.post.author.viewer?.blocking || - getEmbedBlocking(this.post.embed) || - false, - isBlockedBy: - !!this.post.author.viewer?.blockedBy || - getEmbedBlockedBy(this.post.embed) || - false, - } - } - - get moderation(): PostModeration { - return getPostModeration(this.rootStore, this.labelInfo) - } - - copy(v: FeedViewPost) { - this.post = v.post - this.reply = v.reply - this.reason = v.reason - } - - copyMetrics(v: FeedViewPost) { - this.post.replyCount = v.post.replyCount - this.post.repostCount = v.post.repostCount - this.post.likeCount = v.post.likeCount - this.post.viewer = v.post.viewer - } - - get reasonRepost(): ReasonRepost | undefined { - if (this.reason?.$type === 'app.bsky.feed.defs#reasonRepost') { - return this.reason as ReasonRepost - } - } - - async toggleLike() { - this.post.viewer = this.post.viewer || {} - if (this.post.viewer.like) { - const url = this.post.viewer.like - await updateDataOptimistically( - this.post, - () => { - this.post.likeCount = (this.post.likeCount || 0) - 1 - this.post.viewer!.like = undefined - }, - () => this.rootStore.agent.deleteLike(url), - ) - } else { - await updateDataOptimistically( - this.post, - () => { - this.post.likeCount = (this.post.likeCount || 0) + 1 - this.post.viewer!.like = 'pending' - }, - () => this.rootStore.agent.like(this.post.uri, this.post.cid), - res => { - this.post.viewer!.like = res.uri - }, - ) - } - } - - async toggleRepost() { - this.post.viewer = this.post.viewer || {} - if (this.post.viewer?.repost) { - const url = this.post.viewer.repost - await updateDataOptimistically( - this.post, - () => { - this.post.repostCount = (this.post.repostCount || 0) - 1 - this.post.viewer!.repost = undefined - }, - () => this.rootStore.agent.deleteRepost(url), - ) - } else { - await updateDataOptimistically( - this.post, - () => { - this.post.repostCount = (this.post.repostCount || 0) + 1 - this.post.viewer!.repost = 'pending' - }, - () => this.rootStore.agent.repost(this.post.uri, this.post.cid), - res => { - this.post.viewer!.repost = res.uri - }, - ) - } - } - - 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) - } -} - -export class PostsFeedSliceModel { - // ui state - _reactKey: string = '' - - // data - items: PostsFeedItemModel[] = [] - - constructor( - public rootStore: RootStoreModel, - reactKey: string, - slice: FeedViewPostsSlice, - ) { - this._reactKey = reactKey - for (const item of slice.items) { - this.items.push( - new PostsFeedItemModel(rootStore, `item-${_idCounter++}`, item), - ) - } - makeAutoObservable(this, {rootStore: false}) - } - - get uri() { - if (this.isReply) { - return this.items[1].post.uri - } - return this.items[0].post.uri - } - - get isThread() { - return ( - this.items.length > 1 && - this.items.every( - item => item.post.author.did === this.items[0].post.author.did, - ) - ) - } - - get isReply() { - return this.items.length > 1 && !this.isThread - } - - get rootItem() { - if (this.isReply) { - return this.items[1] - } - return this.items[0] - } - - get moderation() { - return mergePostModerations(this.items.map(item => item.moderation)) - } - - containsUri(uri: string) { - return !!this.items.find(item => item.post.uri === uri) - } - - isThreadParentAt(i: number) { - if (this.items.length === 1) { - return false - } - return i < this.items.length - 1 - } - - isThreadChildAt(i: number) { - if (this.items.length === 1) { - return false - } - return i > 0 - } -} - export class PostsFeedModel { // state isLoading = false @@ -297,6 +36,7 @@ export class PostsFeedModel { loadMoreCursor: string | undefined pollCursor: string | undefined tuner = new FeedTuner() + pageSize = PAGE_SIZE // used to linearize async modifications to state lock = new AwaitLock() @@ -418,7 +158,7 @@ export class PostsFeedModel { this.tuner.reset() this._xLoading(isRefreshing) try { - const res = await this._getFeed({limit: PAGE_SIZE}) + const res = await this._getFeed({limit: this.pageSize}) await this._replaceAll(res) this._xIdle() } catch (e: any) { @@ -457,7 +197,7 @@ export class PostsFeedModel { try { const res = await this._getFeed({ cursor: this.loadMoreCursor, - limit: PAGE_SIZE, + limit: this.pageSize, }) await this._appendAll(res) this._xIdle() @@ -526,7 +266,7 @@ export class PostsFeedModel { if (this.hasNewLatest || this.feedType === 'suggested') { return } - const res = await this._getFeed({limit: PAGE_SIZE}) + const res = await this._getFeed({limit: this.pageSize}) const tuner = new FeedTuner() const slices = tuner.tune(res.data.feed, this.feedTuners) this.setHasNewLatest(slices[0]?.uri !== this.slices[0]?.uri) |