Rework profile page to include working view selector

This commit is contained in:
Paul Frazee 2022-09-06 14:26:39 -05:00
parent 2ec09ba545
commit bb06ef4f6e
19 changed files with 569 additions and 94 deletions

View file

@ -0,0 +1,49 @@
import {makeAutoObservable} from 'mobx'
import {RootStoreModel} from './root-store'
// TODO / DEBUG
// this is a temporary fake for the model until the view actually gets implemented in the bsky api
// -prf
export class BadgesViewModel {
// state
isLoading = false
isRefreshing = false
hasLoaded = false
error = ''
constructor(public rootStore: RootStoreModel) {
makeAutoObservable(
this,
{
rootStore: false,
},
{autoBind: true},
)
}
get hasContent() {
return false
}
get hasError() {
return this.error !== ''
}
get isEmpty() {
return this.hasLoaded && !this.hasContent
}
// public api
// =
async setup() {
this.hasLoaded = true
}
async refresh() {}
async loadMore() {}
async update() {}
}

View file

@ -95,6 +95,7 @@ export class FeedViewModel implements bsky.FeedView.Response {
isLoading = false
isRefreshing = false
hasLoaded = false
hasReachedEnd = false
error = ''
params: bsky.FeedView.Params
_loadPromise: Promise<void> | undefined
@ -244,7 +245,13 @@ export class FeedViewModel implements bsky.FeedView.Response {
'blueskyweb.xyz:FeedView',
params,
)) as bsky.FeedView.Response
this._appendAll(res)
if (res.feed.length === 0) {
runInAction(() => {
this.hasReachedEnd = true
})
} else {
this._appendAll(res)
}
this._xIdle()
} catch (e: any) {
this._xIdle(`Failed to load feed: ${e.toString()}`)
@ -281,6 +288,7 @@ export class FeedViewModel implements bsky.FeedView.Response {
private _replaceAll(res: bsky.FeedView.Response) {
this.feed.length = 0
this.hasReachedEnd = false
this._appendAll(res)
}

View file

@ -0,0 +1,98 @@
import {makeAutoObservable} from 'mobx'
import {RootStoreModel} from './root-store'
import {ProfileViewModel} from './profile-view'
import {FeedViewModel} from './feed-view'
import {BadgesViewModel} from './badges-view'
export const SECTION_IDS = {
POSTS: 0,
BADGES: 1,
}
export interface ProfileUiParams {
user: string
}
export class ProfileUiModel {
// constants
static SELECTOR_ITEMS = ['Posts', 'Badges']
// data
profile: ProfileViewModel
feed: FeedViewModel
badges: BadgesViewModel
// ui state
selectedViewIndex = 0
constructor(
public rootStore: RootStoreModel,
public params: ProfileUiParams,
) {
makeAutoObservable(
this,
{
rootStore: false,
params: false,
},
{autoBind: true},
)
this.profile = new ProfileViewModel(rootStore, {user: params.user})
this.feed = new FeedViewModel(rootStore, {author: params.user, limit: 10})
this.badges = new BadgesViewModel(rootStore)
}
get currentView(): FeedViewModel | BadgesViewModel {
if (this.selectedViewIndex === SECTION_IDS.POSTS) {
return this.feed
}
if (this.selectedViewIndex === SECTION_IDS.BADGES) {
return this.badges
}
throw new Error(`Invalid selector value: ${this.selectedViewIndex}`)
}
get isInitialLoading() {
const view = this.currentView
return view.isLoading && !view.isRefreshing && !view.hasContent
}
get isRefreshing() {
return this.profile.isRefreshing || this.currentView.isRefreshing
}
// public api
// =
setSelectedViewIndex(index: number) {
this.selectedViewIndex = index
}
async setup() {
await Promise.all([
this.profile
.setup()
.catch(err => console.error('Failed to fetch profile', err)),
this.feed
.setup()
.catch(err => console.error('Failed to fetch feed', err)),
this.badges
.setup()
.catch(err => console.error('Failed to fetch badges', err)),
])
}
async update() {
await this.currentView.update()
}
async refresh() {
await Promise.all([this.profile.refresh(), this.currentView.refresh()])
}
async loadMore() {
if (!this.currentView.isLoading && !this.currentView.hasError) {
await this.currentView.loadMore()
}
}
}

View file

@ -65,7 +65,7 @@ export class ProfileViewModel implements bsky.ProfileView.Response {
}
async refresh() {
await this._load()
await this._load(true)
}
async toggleFollowing() {
@ -108,8 +108,8 @@ export class ProfileViewModel implements bsky.ProfileView.Response {
// loader functions
// =
private async _load() {
this._xLoading()
private async _load(isRefreshing = false) {
this._xLoading(isRefreshing)
await new Promise(r => setTimeout(r, 250)) // DEBUG
try {
const res = (await this.rootStore.api.mainPds.view(
@ -119,7 +119,7 @@ export class ProfileViewModel implements bsky.ProfileView.Response {
this._replaceAll(res)
this._xIdle()
} catch (e: any) {
this._xIdle(`Failed to load feed: ${e.toString()}`)
this._xIdle(e.toString())
}
}