Skip to content

Commit

Permalink
Composer update (react-query refactor) (#1899)
Browse files Browse the repository at this point in the history
* Move composer state to a context

* Rework composer to use RQ

---------

Co-authored-by: Eric Bailey <[email protected]>
  • Loading branch information
pfrazee and estrattonbailey authored Nov 14, 2023
1 parent c687172 commit 0a26e78
Show file tree
Hide file tree
Showing 32 changed files with 269 additions and 239 deletions.
13 changes: 6 additions & 7 deletions src/lib/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,11 @@ interface PostOpts {
extLink?: ExternalEmbedDraft
images?: ImageModel[]
labels?: string[]
knownHandles?: Set<string>
onStateChange?: (state: string) => void
langs?: string[]
}

export async function post(store: RootStoreModel, opts: PostOpts) {
export async function post(agent: BskyAgent, opts: PostOpts) {
let embed:
| AppBskyEmbedImages.Main
| AppBskyEmbedExternal.Main
Expand All @@ -103,7 +102,7 @@ export async function post(store: RootStoreModel, opts: PostOpts) {
)

opts.onStateChange?.('Processing...')
await rt.detectFacets(store.agent)
await rt.detectFacets(agent)
rt = shortenLinks(rt)

// filter out any mention facets that didn't map to a user
Expand Down Expand Up @@ -136,7 +135,7 @@ export async function post(store: RootStoreModel, opts: PostOpts) {
await image.compress()
const path = image.compressed?.path ?? image.path
const {width, height} = image.compressed || image
const res = await uploadBlob(store.agent, path, 'image/jpeg')
const res = await uploadBlob(agent, path, 'image/jpeg')
images.push({
image: res.data.blob,
alt: image.altText ?? '',
Expand Down Expand Up @@ -186,7 +185,7 @@ export async function post(store: RootStoreModel, opts: PostOpts) {
}
if (encoding) {
const thumbUploadRes = await uploadBlob(
store.agent,
agent,
opts.extLink.localThumb.path,
encoding,
)
Expand Down Expand Up @@ -225,7 +224,7 @@ export async function post(store: RootStoreModel, opts: PostOpts) {
// add replyTo if post is a reply to another post
if (opts.replyTo) {
const replyToUrip = new AtUri(opts.replyTo)
const parentPost = await store.agent.getPost({
const parentPost = await agent.getPost({
repo: replyToUrip.host,
rkey: replyToUrip.rkey,
})
Expand Down Expand Up @@ -258,7 +257,7 @@ export async function post(store: RootStoreModel, opts: PostOpts) {

try {
opts.onStateChange?.('Posting...')
return await store.agent.post({
return await agent.post({
text: rt.text,
facets: rt.facets,
reply,
Expand Down
2 changes: 1 addition & 1 deletion src/lib/link-meta/bsky.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {LikelyType, LinkMeta} from './link-meta'
import {convertBskyAppUrlIfNeeded, makeRecordUri} from '../strings/url-helpers'
import {RootStoreModel} from 'state/index'
import {PostThreadModel} from 'state/models/content/post-thread'
import {ComposerOptsQuote} from 'state/models/ui/shell'
import {ComposerOptsQuote} from 'state/shell/composer'

// TODO
// import {Home} from 'view/screens/Home'
Expand Down
11 changes: 2 additions & 9 deletions src/lib/media/picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {
openCropper as openCropperFn,
Image as RNImage,
} from 'react-native-image-crop-picker'
import {RootStoreModel} from 'state/index'
import {CameraOpts, CropperOptions} from './types'
export {openPicker} from './picker.shared'

Expand All @@ -16,10 +15,7 @@ export {openPicker} from './picker.shared'
* -prf
*/

export async function openCamera(
_store: RootStoreModel,
opts: CameraOpts,
): Promise<RNImage> {
export async function openCamera(opts: CameraOpts): Promise<RNImage> {
const item = await openCameraFn({
width: opts.width,
height: opts.height,
Expand All @@ -39,10 +35,7 @@ export async function openCamera(
}
}

export async function openCropper(
_store: RootStoreModel,
opts: CropperOptions,
) {
export async function openCropper(opts: CropperOptions) {
const item = await openCropperFn({
...opts,
forceJpg: true, // ios only
Expand Down
11 changes: 2 additions & 9 deletions src/lib/media/picker.web.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,16 @@
/// <reference lib="dom" />

import {CameraOpts, CropperOptions} from './types'
import {RootStoreModel} from 'state/index'
import {Image as RNImage} from 'react-native-image-crop-picker'
export {openPicker} from './picker.shared'
import {unstable__openModal} from '#/state/modals'

export async function openCamera(
_store: RootStoreModel,
_opts: CameraOpts,
): Promise<RNImage> {
export async function openCamera(_opts: CameraOpts): Promise<RNImage> {
// const mediaType = opts.mediaType || 'photo' TODO
throw new Error('TODO')
}

export async function openCropper(
_store: RootStoreModel,
opts: CropperOptions,
): Promise<RNImage> {
export async function openCropper(opts: CropperOptions): Promise<RNImage> {
// TODO handle more opts
return new Promise((resolve, reject) => {
unstable__openModal({
Expand Down
9 changes: 3 additions & 6 deletions src/state/models/media/gallery.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {makeAutoObservable, runInAction} from 'mobx'
import {RootStoreModel} from 'state/index'
import {ImageModel} from './image'
import {Image as RNImage} from 'react-native-image-crop-picker'
import {openPicker} from 'lib/media/picker'
Expand All @@ -8,10 +7,8 @@ import {getImageDim} from 'lib/media/manip'
export class GalleryModel {
images: ImageModel[] = []

constructor(public rootStore: RootStoreModel) {
makeAutoObservable(this, {
rootStore: false,
})
constructor() {
makeAutoObservable(this)
}

get isEmpty() {
Expand All @@ -33,7 +30,7 @@ export class GalleryModel {

// Temporarily enforce uniqueness but can eventually also use index
if (!this.images.some(i => i.path === image_.path)) {
const image = new ImageModel(this.rootStore, image_)
const image = new ImageModel(image_)

// Initial resize
image.manipulate({})
Expand Down
9 changes: 3 additions & 6 deletions src/state/models/media/image.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {Image as RNImage} from 'react-native-image-crop-picker'
import {RootStoreModel} from 'state/index'
import {makeAutoObservable, runInAction} from 'mobx'
import {POST_IMG_MAX} from 'lib/constants'
import * as ImageManipulator from 'expo-image-manipulator'
Expand Down Expand Up @@ -42,10 +41,8 @@ export class ImageModel implements Omit<RNImage, 'size'> {
}
prevAttributes: ImageManipulationAttributes = {}

constructor(public rootStore: RootStoreModel, image: Omit<RNImage, 'size'>) {
makeAutoObservable(this, {
rootStore: false,
})
constructor(image: Omit<RNImage, 'size'>) {
makeAutoObservable(this)

this.path = image.path
this.width = image.width
Expand Down Expand Up @@ -178,7 +175,7 @@ export class ImageModel implements Omit<RNImage, 'size'> {
height: this.height,
})

const cropped = await openCropper(this.rootStore, {
const cropped = await openCropper({
mediaType: 'photo',
path: this.path,
freeStyleCropEnabled: true,
Expand Down
52 changes: 1 addition & 51 deletions src/state/models/ui/shell.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {AppBskyEmbedRecord, AppBskyActorDefs} from '@atproto/api'
import {AppBskyActorDefs} from '@atproto/api'
import {RootStoreModel} from '../root-store'
import {makeAutoObservable, runInAction} from 'mobx'
import {
Expand Down Expand Up @@ -37,41 +37,9 @@ export class ImagesLightbox implements LightboxModel {
}
}

export interface ComposerOptsPostRef {
uri: string
cid: string
text: string
author: {
handle: string
displayName?: string
avatar?: string
}
}
export interface ComposerOptsQuote {
uri: string
cid: string
text: string
indexedAt: string
author: {
did: string
handle: string
displayName?: string
avatar?: string
}
embeds?: AppBskyEmbedRecord.ViewRecord['embeds']
}
export interface ComposerOpts {
replyTo?: ComposerOptsPostRef
onPost?: () => void
quote?: ComposerOptsQuote
mention?: string // handle of user to mention
}

export class ShellUiModel {
isLightboxActive = false
activeLightbox: ProfileImageLightbox | ImagesLightbox | null = null
isComposerActive = false
composerOpts: ComposerOpts | undefined
tickEveryMinute = Date.now()

constructor(public rootStore: RootStoreModel) {
Expand All @@ -92,10 +60,6 @@ export class ShellUiModel {
this.closeLightbox()
return true
}
if (this.isComposerActive) {
this.closeComposer()
return true
}
return false
}

Expand All @@ -106,9 +70,6 @@ export class ShellUiModel {
if (this.isLightboxActive) {
this.closeLightbox()
}
if (this.isComposerActive) {
this.closeComposer()
}
}

openLightbox(lightbox: ProfileImageLightbox | ImagesLightbox) {
Expand All @@ -122,17 +83,6 @@ export class ShellUiModel {
this.activeLightbox = null
}

openComposer(opts: ComposerOpts) {
this.rootStore.emitNavigation()
this.isComposerActive = true
this.composerOpts = opts
}

closeComposer() {
this.isComposerActive = false
this.composerOpts = undefined
}

setupClock() {
setInterval(() => {
runInAction(() => {
Expand Down
54 changes: 53 additions & 1 deletion src/state/queries/actor-autocomplete.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import {AppBskyActorDefs} from '@atproto/api'
import {AppBskyActorDefs, BskyAgent} from '@atproto/api'
import {useQuery} from '@tanstack/react-query'
import {useSession} from '../session'
import {useMyFollowsQuery} from './my-follows'
import AwaitLock from 'await-lock'

export const RQKEY = (prefix: string) => ['actor-autocomplete', prefix]

Expand All @@ -21,6 +22,57 @@ export function useActorAutocompleteQuery(prefix: string) {
})
}

export class ActorAutocomplete {
// state
isLoading = false
isActive = false
prefix = ''
lock = new AwaitLock()

// data
suggestions: AppBskyActorDefs.ProfileViewBasic[] = []

constructor(
public agent: BskyAgent,
public follows?: AppBskyActorDefs.ProfileViewBasic[] | undefined,
) {}

setFollows(follows: AppBskyActorDefs.ProfileViewBasic[]) {
this.follows = follows
}

async query(prefix: string) {
const origPrefix = prefix.trim().toLocaleLowerCase()
this.prefix = origPrefix
await this.lock.acquireAsync()
try {
if (this.prefix) {
if (this.prefix !== origPrefix) {
return // another prefix was set before we got our chance
}

// start with follow results
this.suggestions = computeSuggestions(this.prefix, this.follows)

// ask backend
const res = await this.agent.searchActorsTypeahead({
term: this.prefix,
limit: 8,
})
this.suggestions = computeSuggestions(
this.prefix,
this.follows,
res.data.actors,
)
} else {
this.suggestions = computeSuggestions(this.prefix, this.follows)
}
} finally {
this.lock.release()
}
}
}

function computeSuggestions(
prefix: string,
follows: AppBskyActorDefs.ProfileViewBasic[] = [],
Expand Down
Loading

0 comments on commit 0a26e78

Please sign in to comment.