Lex refactor (#362)

* Remove the hackcheck for upgrades

* Rename the PostEmbeds folder to match the codebase style

* Updates to latest lex refactor

* Update to use new bsky agent

* Update to use api package's richtext library

* Switch to upsertProfile

* Add TextEncoder/TextDecoder polyfill

* Add Intl.Segmenter polyfill

* Update composer to calculate lengths by grapheme

* Fix detox

* Fix login in e2e

* Create account e2e passing

* Implement an e2e mocking framework

* Don't use private methods on mobx models as mobx can't track them

* Add tooling for e2e-specific builds and add e2e media-picker mock

* Add some tests and fix some bugs around profile editing

* Add shell tests

* Add home screen tests

* Add thread screen tests

* Add tests for other user profile screens

* Add search screen tests

* Implement profile imagery change tools and tests

* Update to new embed behaviors

* Add post tests

* Fix to profile-screen test

* Fix session resumption

* Update web composer to new api

* 1.11.0

* Fix pagination cursor parameters

* Add quote posts to notifications

* Fix embed layouts

* Remove youtube inline player and improve tap handling on link cards

* Reset minimal shell mode on all screen loads and feed swipes (close #299)

* Update podfile.lock

* Improve post notfound UI (close #366)

* Bump atproto packages
This commit is contained in:
Paul Frazee 2023-03-31 13:17:26 -05:00 committed by GitHub
parent 19f3a2fa92
commit a3334a01a2
133 changed files with 3103 additions and 2839 deletions

View file

@ -2,15 +2,12 @@ import {makeAutoObservable, runInAction} from 'mobx'
import {PickedMedia} from 'lib/media/picker'
import {
AppBskyActorGetProfile as GetProfile,
AppBskySystemDeclRef,
AppBskyActorUpdateProfile,
AppBskyActorProfile,
RichText,
} from '@atproto/api'
type DeclRef = AppBskySystemDeclRef.Main
import {extractEntities} from 'lib/strings/rich-text-detection'
import {RootStoreModel} from './root-store'
import * as apilib from 'lib/api/index'
import {cleanError} from 'lib/strings/errors'
import {RichText} from 'lib/strings/rich-text'
export const ACTOR_TYPE_USER = 'app.bsky.system.actorUser'
@ -35,22 +32,18 @@ export class ProfileViewModel {
// data
did: string = ''
handle: string = ''
declaration: DeclRef = {
cid: '',
actorType: '',
}
creator: string = ''
displayName?: string
description?: string
avatar?: string
banner?: string
displayName?: string = ''
description?: string = ''
avatar?: string = ''
banner?: string = ''
followersCount: number = 0
followsCount: number = 0
postsCount: number = 0
viewer = new ProfileViewViewerModel()
// added data
descriptionRichText?: RichText
descriptionRichText?: RichText = new RichText({text: ''})
constructor(
public rootStore: RootStoreModel,
@ -79,10 +72,6 @@ export class ProfileViewModel {
return this.hasLoaded && !this.hasContent
}
get isUser() {
return this.declaration.actorType === ACTOR_TYPE_USER
}
// public api
// =
@ -111,18 +100,14 @@ export class ProfileViewModel {
}
if (followUri) {
await apilib.unfollow(this.rootStore, followUri)
await this.rootStore.agent.deleteFollow(followUri)
runInAction(() => {
this.followersCount--
this.viewer.following = undefined
this.rootStore.me.follows.removeFollow(this.did)
})
} else {
const res = await apilib.follow(
this.rootStore,
this.did,
this.declaration.cid,
)
const res = await this.rootStore.agent.follow(this.did)
runInAction(() => {
this.followersCount++
this.viewer.following = res.uri
@ -132,49 +117,48 @@ export class ProfileViewModel {
}
async updateProfile(
updates: AppBskyActorUpdateProfile.InputSchema,
updates: AppBskyActorProfile.Record,
newUserAvatar: PickedMedia | undefined | null,
newUserBanner: PickedMedia | undefined | null,
) {
if (newUserAvatar) {
const res = await apilib.uploadBlob(
this.rootStore,
newUserAvatar.path,
newUserAvatar.mime,
)
updates.avatar = {
cid: res.data.cid,
mimeType: newUserAvatar.mime,
await this.rootStore.agent.upsertProfile(async existing => {
existing = existing || {}
existing.displayName = updates.displayName
existing.description = updates.description
if (newUserAvatar) {
const res = await apilib.uploadBlob(
this.rootStore,
newUserAvatar.path,
newUserAvatar.mime,
)
existing.avatar = res.data.blob
} else if (newUserAvatar === null) {
existing.avatar = undefined
}
} else if (newUserAvatar === null) {
updates.avatar = null
}
if (newUserBanner) {
const res = await apilib.uploadBlob(
this.rootStore,
newUserBanner.path,
newUserBanner.mime,
)
updates.banner = {
cid: res.data.cid,
mimeType: newUserBanner.mime,
if (newUserBanner) {
const res = await apilib.uploadBlob(
this.rootStore,
newUserBanner.path,
newUserBanner.mime,
)
existing.banner = res.data.blob
} else if (newUserBanner === null) {
existing.banner = undefined
}
} else if (newUserBanner === null) {
updates.banner = null
}
await this.rootStore.api.app.bsky.actor.updateProfile(updates)
return existing
})
await this.rootStore.me.load()
await this.refresh()
}
async muteAccount() {
await this.rootStore.api.app.bsky.graph.mute({user: this.did})
await this.rootStore.agent.mute(this.did)
this.viewer.muted = true
await this.refresh()
}
async unmuteAccount() {
await this.rootStore.api.app.bsky.graph.unmute({user: this.did})
await this.rootStore.agent.unmute(this.did)
this.viewer.muted = false
await this.refresh()
}
@ -182,13 +166,13 @@ export class ProfileViewModel {
// state transitions
// =
private _xLoading(isRefreshing = false) {
_xLoading(isRefreshing = false) {
this.isLoading = true
this.isRefreshing = isRefreshing
this.error = ''
}
private _xIdle(err?: any) {
_xIdle(err?: any) {
this.isLoading = false
this.isRefreshing = false
this.hasLoaded = true
@ -201,40 +185,40 @@ export class ProfileViewModel {
// loader functions
// =
private async _load(isRefreshing = false) {
async _load(isRefreshing = false) {
this._xLoading(isRefreshing)
try {
const res = await this.rootStore.api.app.bsky.actor.getProfile(
this.params,
)
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()
this._xIdle()
} catch (e: any) {
this._xIdle(e)
}
}
private _replaceAll(res: GetProfile.Response) {
_replaceAll(res: GetProfile.Response) {
this.did = res.data.did
this.handle = res.data.handle
Object.assign(this.declaration, res.data.declaration)
this.creator = res.data.creator
this.displayName = res.data.displayName
this.description = res.data.description
this.avatar = res.data.avatar
this.banner = res.data.banner
this.followersCount = res.data.followersCount
this.followsCount = res.data.followsCount
this.postsCount = res.data.postsCount
this.followersCount = res.data.followersCount || 0
this.followsCount = res.data.followsCount || 0
this.postsCount = res.data.postsCount || 0
if (res.data.viewer) {
Object.assign(this.viewer, res.data.viewer)
this.rootStore.me.follows.hydrate(this.did, res.data.viewer.following)
}
}
async _createRichText() {
this.descriptionRichText = new RichText(
this.description || '',
extractEntities(this.description || ''),
{text: this.description || ''},
{cleanNewlines: true},
)
await this.descriptionRichText.detectFacets(this.rootStore.agent)
}
}