diff options
author | Paul Frazee <pfrazee@gmail.com> | 2023-01-19 16:54:30 -0600 |
---|---|---|
committer | Paul Frazee <pfrazee@gmail.com> | 2023-01-19 16:54:30 -0600 |
commit | 6dcf9f8d5b1136d77cf6910409da25297ad04f4e (patch) | |
tree | 321735062351e17528e671d36361d0ad2fb8f67f /src | |
parent | a24b29d64bb552e46b8cf3789c53d883f3bae394 (diff) | |
download | voidsky-6dcf9f8d5b1136d77cf6910409da25297ad04f4e.tar.zst |
Fixes to likes list
Diffstat (limited to 'src')
-rw-r--r-- | src/state/models/votes-view.ts | 82 | ||||
-rw-r--r-- | src/view/com/post-thread/PostVotedBy.tsx | 69 |
2 files changed, 68 insertions, 83 deletions
diff --git a/src/state/models/votes-view.ts b/src/state/models/votes-view.ts index 937667b44..df939226f 100644 --- a/src/state/models/votes-view.ts +++ b/src/state/models/votes-view.ts @@ -1,31 +1,11 @@ import {makeAutoObservable, runInAction} from 'mobx' import {AtUri} from '../../third-party/uri' -import { - AppBskyFeedGetVotes as GetVotes, - AppBskyActorRef as ActorRef, -} from '@atproto/api' +import {AppBskyFeedGetVotes as GetVotes} from '@atproto/api' import {RootStoreModel} from './root-store' -export class VotesViewItemModel implements GetVotes.Vote { - // ui state - _reactKey: string = '' +const PAGE_SIZE = 30 - // data - direction: 'up' | 'down' = 'up' - indexedAt: string = '' - createdAt: string = '' - actor: ActorRef.WithInfo = { - did: '', - handle: '', - declaration: {cid: '', actorType: ''}, - } - - constructor(reactKey: string, v: GetVotes.Vote) { - makeAutoObservable(this) - this._reactKey = reactKey - Object.assign(this, v) - } -} +export type VoteItem = GetVotes.Vote export class VotesViewModel { // state @@ -35,10 +15,13 @@ export class VotesViewModel { error = '' resolvedUri = '' params: GetVotes.QueryParams + hasMore = true + loadMoreCursor?: string + private _loadMorePromise: Promise<void> | undefined // data uri: string = '' - votes: VotesViewItemModel[] = [] + votes: VoteItem[] = [] constructor(public rootStore: RootStoreModel, params: GetVotes.QueryParams) { makeAutoObservable( @@ -67,19 +50,20 @@ export class VotesViewModel { // public api // = - async setup() { - if (!this.resolvedUri) { - await this._resolveUri() - } - await this._fetch() - } - async refresh() { - await this._fetch(true) + return this.loadMore(true) } - async loadMore() { - // TODO + async loadMore(isRefreshing = false) { + if (this._loadMorePromise) { + return this._loadMorePromise + } + if (!this.resolvedUri) { + await this._resolveUri() + } + this._loadMorePromise = this._loadMore(isRefreshing) + await this._loadMorePromise + this._loadMorePromise = undefined } // state transitions @@ -118,28 +102,28 @@ export class VotesViewModel { }) } - private async _fetch(isRefreshing = false) { + private async _loadMore(isRefreshing = false) { this._xLoading(isRefreshing) try { - const res = await this.rootStore.api.app.bsky.feed.getVotes( - Object.assign({}, this.params, {uri: this.resolvedUri}), - ) - this._replaceAll(res) + const params = Object.assign({}, this.params, { + uri: this.resolvedUri, + limit: PAGE_SIZE, + before: this.loadMoreCursor, + }) + if (this.isRefreshing) { + this.votes = [] + } + const res = await this.rootStore.api.app.bsky.feed.getVotes(params) + this._appendAll(res) this._xIdle() } catch (e: any) { this._xIdle(e) } } - private _replaceAll(res: GetVotes.Response) { - this.votes.length = 0 - let counter = 0 - for (const item of res.data.votes) { - this._append(counter++, item) - } - } - - private _append(keyId: number, item: GetVotes.Vote) { - this.votes.push(new VotesViewItemModel(`item-${keyId}`, item)) + private _appendAll(res: GetVotes.Response) { + this.loadMoreCursor = res.data.cursor + this.hasMore = !!this.loadMoreCursor + this.votes = this.votes.concat(res.data.votes) } } diff --git a/src/view/com/post-thread/PostVotedBy.tsx b/src/view/com/post-thread/PostVotedBy.tsx index 34a333f22..06fe53888 100644 --- a/src/view/com/post-thread/PostVotedBy.tsx +++ b/src/view/com/post-thread/PostVotedBy.tsx @@ -1,10 +1,7 @@ import React, {useEffect} from 'react' import {observer} from 'mobx-react-lite' import {ActivityIndicator, FlatList, StyleSheet, View} from 'react-native' -import { - VotesViewModel, - VotesViewItemModel, -} from '../../../state/models/votes-view' +import {VotesViewModel, VotesItem} from '../../../state/models/votes-view' import {Link} from '../util/Link' import {Text} from '../util/text/Text' import {ErrorMessage} from '../util/error/ErrorMessage' @@ -20,30 +17,25 @@ export const PostVotedBy = observer(function PostVotedBy({ direction: 'up' | 'down' }) { const store = useStores() - const [view, setView] = React.useState<VotesViewModel | undefined>() + const view = React.useMemo( + () => new VotesViewModel(store, {uri, direction}), + [store, uri, direction], + ) useEffect(() => { - if (view?.params.uri === uri) { - return // no change needed? or trigger refresh? - } - const newView = new VotesViewModel(store, {uri, direction}) - setView(newView) - newView - .setup() - .catch(err => store.log.error('Failed to fetch voted by', err)) - }, [uri, view?.params.uri, store]) + view.loadMore().catch(err => store.log.error('Failed to fetch votes', err)) + }, [view, store.log]) const onRefresh = () => { - view?.refresh() + view.refresh() + } + const onEndReached = () => { + view + .loadMore() + .catch(err => view?.rootStore.log.error('Failed to load more votes', err)) } - // loading - // = - if ( - !view || - (view.isLoading && !view.isRefreshing) || - view.params.uri !== uri - ) { + if (!view.hasLoaded) { return ( <View> <ActivityIndicator /> @@ -67,22 +59,27 @@ export const PostVotedBy = observer(function PostVotedBy({ // loaded // = - const renderItem = ({item}: {item: VotesViewItemModel}) => ( - <LikedByItem item={item} /> - ) + const renderItem = ({item}: {item: VotesItem}) => <LikedByItem item={item} /> return ( - <View> - <FlatList - data={view.votes} - keyExtractor={item => item._reactKey} - renderItem={renderItem} - contentContainerStyle={{paddingBottom: 200}} - /> - </View> + <FlatList + data={view.votes} + keyExtractor={item => item.actor.did} + refreshing={view.isRefreshing} + onRefresh={onRefresh} + onEndReached={onEndReached} + renderItem={renderItem} + initialNumToRender={15} + ListFooterComponent={() => ( + <View style={styles.footer}> + {view.isLoading && <ActivityIndicator />} + </View> + )} + extraData={view.isLoading} + /> ) }) -const LikedByItem = ({item}: {item: VotesViewItemModel}) => { +const LikedByItem = ({item}: {item: VotesItem}) => { return ( <Link style={styles.outer} @@ -135,4 +132,8 @@ const styles = StyleSheet.create({ paddingTop: 10, paddingBottom: 10, }, + footer: { + height: 200, + paddingTop: 20, + }, }) |