diff options
Diffstat (limited to 'src/state/models/votes-view.ts')
-rw-r--r-- | src/state/models/votes-view.ts | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/src/state/models/votes-view.ts b/src/state/models/votes-view.ts new file mode 100644 index 000000000..d70737021 --- /dev/null +++ b/src/state/models/votes-view.ts @@ -0,0 +1,133 @@ +import {makeAutoObservable, runInAction} from 'mobx' +import {AtUri} from '../../third-party/uri' +import * as GetVotes from '../../third-party/api/src/client/types/app/bsky/feed/getVotes' +import {RootStoreModel} from './root-store' + +type VoteItem = GetVotes.OutputSchema['votes'][number] + +export class VotesViewItemModel implements VoteItem { + // ui state + _reactKey: string = '' + + // data + direction: 'up' | 'down' = 'up' + indexedAt: string = '' + createdAt: string = '' + actor: GetVotes.Actor = {did: '', handle: ''} + + constructor(reactKey: string, v: VoteItem) { + makeAutoObservable(this) + this._reactKey = reactKey + Object.assign(this, v) + } +} + +export class VotesViewModel { + // state + isLoading = false + isRefreshing = false + hasLoaded = false + error = '' + resolvedUri = '' + params: GetVotes.QueryParams + + // data + uri: string = '' + votes: VotesViewItemModel[] = [] + + constructor(public rootStore: RootStoreModel, params: GetVotes.QueryParams) { + makeAutoObservable( + this, + { + rootStore: false, + params: false, + }, + {autoBind: true}, + ) + this.params = params + } + + get hasContent() { + return this.uri !== '' + } + + get hasError() { + return this.error !== '' + } + + get isEmpty() { + return this.hasLoaded && !this.hasContent + } + + // public api + // = + + async setup() { + if (!this.resolvedUri) { + await this._resolveUri() + } + await this._fetch() + } + + async refresh() { + await this._fetch(true) + } + + async loadMore() { + // TODO + } + + // state transitions + // = + + private _xLoading(isRefreshing = false) { + this.isLoading = true + this.isRefreshing = isRefreshing + this.error = '' + } + + private _xIdle(err: string = '') { + this.isLoading = false + this.isRefreshing = false + this.hasLoaded = true + this.error = err + } + + // loader functions + // = + + private async _resolveUri() { + const urip = new AtUri(this.params.uri) + if (!urip.host.startsWith('did:')) { + urip.host = await this.rootStore.resolveName(urip.host) + } + runInAction(() => { + this.resolvedUri = urip.toString() + }) + } + + private async _fetch(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) + this._xIdle() + } catch (e: any) { + this._xIdle(`Failed to load feed: ${e.toString()}`) + } + } + + 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: VoteItem) { + this.votes.push(new VotesViewItemModel(`item-${keyId}`, item)) + } +} |