WIP model

This commit is contained in:
Eric Bailey
2024-10-08 13:24:04 -05:00
parent 95ee2a9e2c
commit f43d2ebefa
7 changed files with 81 additions and 19 deletions
+1
View File
@@ -143,6 +143,7 @@
"expo-updates": "~0.25.14", "expo-updates": "~0.25.14",
"expo-web-browser": "~13.0.3", "expo-web-browser": "~13.0.3",
"fast-text-encoding": "^1.0.6", "fast-text-encoding": "^1.0.6",
"fuse.js": "^7.0.0",
"history": "^5.3.0", "history": "^5.3.0",
"hls.js": "^1.5.11", "hls.js": "^1.5.11",
"js-sha256": "^0.9.0", "js-sha256": "^0.9.0",
+8 -1
View File
@@ -1,7 +1,7 @@
import {MMKV} from 'react-native-mmkv' import {MMKV} from 'react-native-mmkv'
import {IS_DEV} from '#/env' import {IS_DEV} from '#/env'
import {Device} from '#/storage/schema' import {Account} from '#/storage/schema'
export * from '#/storage/schema' export * from '#/storage/schema'
@@ -74,6 +74,13 @@ export class Storage<Scopes extends unknown[], Schema> {
*/ */
export const device = new Storage<[], Device>({id: 'bsky_device'}) export const device = new Storage<[], Device>({id: 'bsky_device'})
/**
* Account data that's specific to the account
*
* `account.set([did, key], true)`
*/
export const account = new Storage<[string], Account>({id: 'bsky_account'})
if (IS_DEV && typeof window !== 'undefined') { if (IS_DEV && typeof window !== 'undefined') {
// @ts-ignore // @ts-ignore
window.bsky_storage = { window.bsky_storage = {
+9
View File
@@ -1,3 +1,5 @@
import {Result} from '#/view/com/composer/text-input/tagsAutocompleteState'
/** /**
* Device data that's specific to the device and does not vary based account * Device data that's specific to the device and does not vary based account
*/ */
@@ -9,3 +11,10 @@ export type Device = {
countryCode: string | undefined countryCode: string | undefined
} }
} }
/**
* Account data that's specific to the current account
*/
export type Account = {
recentTags: Result[] | undefined
}
@@ -13,15 +13,17 @@ import {Text as TiptapText} from '@tiptap/extension-text'
import {generateJSON} from '@tiptap/html' import {generateJSON} from '@tiptap/html'
import {EditorContent, JSONContent, useEditor} from '@tiptap/react' import {EditorContent, JSONContent, useEditor} from '@tiptap/react'
import {useColorSchemeStyle} from '#/lib/hooks/useColorSchemeStyle'
import {usePalette} from '#/lib/hooks/usePalette' import {usePalette} from '#/lib/hooks/usePalette'
import {blobToDataUri, isUriImage} from '#/lib/media/util'
import {useActorAutocompleteFn} from '#/state/queries/actor-autocomplete' import {useActorAutocompleteFn} from '#/state/queries/actor-autocomplete'
import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle' import {useSession} from '#/state/session'
import {blobToDataUri, isUriImage} from 'lib/media/util' import {tagAutocompleteModel} from '#/view/com/composer/text-input/tagsAutocompleteState'
import {textInputWebEmitter} from '#/view/com/composer/text-input/textInputWebEmitter'
import { import {
LinkFacetMatch, LinkFacetMatch,
suggestLinkCardUri, suggestLinkCardUri,
} from 'view/com/composer/text-input/text-input-util' } from '#/view/com/composer/text-input/text-input-util'
import {textInputWebEmitter} from '#/view/com/composer/text-input/textInputWebEmitter'
import {atoms as a, useAlf} from '#/alf' import {atoms as a, useAlf} from '#/alf'
import {Portal} from '#/components/Portal' import {Portal} from '#/components/Portal'
import {normalizeTextStyles} from '#/components/Typography' import {normalizeTextStyles} from '#/components/Typography'
@@ -29,8 +31,7 @@ import {Text} from '../../util/text/Text'
import {createSuggestion} from './web/Autocomplete' import {createSuggestion} from './web/Autocomplete'
import {Emoji} from './web/EmojiPicker.web' import {Emoji} from './web/EmojiPicker.web'
import {LinkDecorator} from './web/LinkDecorator' import {LinkDecorator} from './web/LinkDecorator'
import {TagDecorator} from './web/TagDecorator' import {createTagsAutocomplete,Tags} from './web/Tags'
import {Tags, createTagsAutocomplete} from './web/Tags'
export interface TextInputRef { export interface TextInputRef {
focus: () => void focus: () => void
@@ -65,6 +66,7 @@ export const TextInput = React.forwardRef(function TextInputImpl(
const autocomplete = useActorAutocompleteFn() const autocomplete = useActorAutocompleteFn()
const pal = usePalette('default') const pal = usePalette('default')
const modeClass = useColorSchemeStyle('ProseMirror-light', 'ProseMirror-dark') const modeClass = useColorSchemeStyle('ProseMirror-light', 'ProseMirror-dark')
const {currentAccount} = useSession()
const [isDropping, setIsDropping] = React.useState(false) const [isDropping, setIsDropping] = React.useState(false)
@@ -78,6 +80,7 @@ export const TextInput = React.forwardRef(function TextInputImpl(
class: 'inline-tag', class: 'inline-tag',
}, },
suggestion: createTagsAutocomplete({ suggestion: createTagsAutocomplete({
model: tagAutocompleteModel({currentDid: currentAccount?.did!}),
}), }),
}), }),
Mention.configure({ Mention.configure({
@@ -94,7 +97,7 @@ export const TextInput = React.forwardRef(function TextInputImpl(
History, History,
Hardbreak, Hardbreak,
], ],
[autocomplete, placeholder], [autocomplete, placeholder, currentAccount],
) )
React.useEffect(() => { React.useEffect(() => {
@@ -0,0 +1,31 @@
import {account} from '#/storage'
export type Result = {
value: string
}
export type Model = {
search(query: string): Promise<Result[]>
save(tag: string): void
}
export function tagAutocompleteModel({
currentDid,
}: {
currentDid: string
}): Model {
let recentTags = account.get([currentDid, 'recentTags']) || []
return {
async search(query: string) {
if (!query) return [{value: query}]
return [{value: query}]
},
save(tag: string) {
recentTags = [
{value: tag},
...recentTags.filter(t => t.value !== tag),
].slice(0, 40)
},
}
}
@@ -9,28 +9,32 @@ import {
import tippy, {Instance as TippyInstance} from 'tippy.js' import tippy, {Instance as TippyInstance} from 'tippy.js'
// import {TagsAutocompleteModel} from 'state/models/ui/tags-autocomplete' // import {TagsAutocompleteModel} from 'state/models/ui/tags-autocomplete'
import {usePalette} from 'lib/hooks/usePalette' import {usePalette} from '#/lib/hooks/usePalette'
import {Text} from 'view/com/util/text/Text' import {
Model,
Result,
} from '#/view/com/composer/text-input/tagsAutocompleteState'
import {Text} from '#/view/com/util/text/Text'
import {parsePunctuationFromTag} from './utils' import {parsePunctuationFromTag} from './utils'
type AutocompleteResult = string type ListProps = SuggestionProps<Result> & {
type ListProps = SuggestionProps<AutocompleteResult> & { model: Model
// autocompleteModel: TagsAutocompleteModel
} }
type AutocompleteRef = { type AutocompleteRef = {
onKeyDown: (props: SuggestionKeyDownProps) => boolean onKeyDown: (props: SuggestionKeyDownProps) => boolean
} }
export function createTagsAutocomplete({}: // autocompleteModel, export function createTagsAutocomplete({
{ model,
// autocompleteModel: TagsAutocompleteModel }: {
model: Model
}): Omit<SuggestionOptions, 'editor'> { }): Omit<SuggestionOptions, 'editor'> {
return { return {
/** /**
* This `query` param comes from the result of `findSuggestionMatch` * This `query` param comes from the result of `findSuggestionMatch`
*/ */
async items({query}) { async items({query}) {
return [query, 'javascript'] return await model.search(query)
}, },
render() { render() {
let component: ReactRenderer<AutocompleteRef> | undefined let component: ReactRenderer<AutocompleteRef> | undefined
@@ -41,6 +45,7 @@ export function createTagsAutocomplete({}: // autocompleteModel,
component = new ReactRenderer(Autocomplete, { component = new ReactRenderer(Autocomplete, {
props: { props: {
...props, ...props,
model,
}, },
editor: props.editor, editor: props.editor,
}) })
@@ -113,7 +118,7 @@ const Autocomplete = forwardRef<AutocompleteRef, ListProps>(
const selectItem = React.useCallback( const selectItem = React.useCallback(
(index: number) => { (index: number) => {
const item = items[index] const item = items[index]
if (item) commit(item) if (item) commit(item.value)
}, },
[items, commit], [items, commit],
) )
@@ -157,7 +162,8 @@ const Autocomplete = forwardRef<AutocompleteRef, ListProps>(
return ( return (
<div className="items"> <div className="items">
<View style={[pal.borderDark, pal.view, styles.container]}> <View style={[pal.borderDark, pal.view, styles.container]}>
{items.map((tag, index) => { {items.map(({value}, index) => {
const {tag} = parsePunctuationFromTag(value)
const isSelected = selectedIndex === index const isSelected = selectedIndex === index
const isFirst = index === 0 const isFirst = index === 0
const isLast = index === items.length - 1 const isLast = index === items.length - 1
+5
View File
@@ -12153,6 +12153,11 @@ functions-have-names@^1.2.2, functions-have-names@^1.2.3:
resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
fuse.js@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-7.0.0.tgz#6573c9fcd4c8268e403b4fc7d7131ffcf99a9eb2"
integrity sha512-14F4hBIxqKvD4Zz/XjDc3y94mNZN6pRv3U13Udo0lNLCWRBUsrMv2xwcF/y/Z5sV6+FQW+/ow68cHpm4sunt8Q==
gensync@^1.0.0-beta.2: gensync@^1.0.0-beta.2:
version "1.0.0-beta.2" version "1.0.0-beta.2"
resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"