diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx
index 8629c4fcb3..e0d2858d06 100644
--- a/src/view/com/composer/Composer.tsx
+++ b/src/view/com/composer/Composer.tsx
@@ -48,6 +48,7 @@ import {LabelsBtn} from './labels/LabelsBtn'
import {SelectLangBtn} from './select-language/SelectLangBtn'
import {EmojiPickerButton} from './text-input/web/EmojiPicker.web'
import {insertMentionAt} from 'lib/strings/mention-manip'
+import {TagInput} from './TagInput'
type Props = ComposerOpts & {
onClose: () => void
@@ -380,6 +381,14 @@ export const ComposePost = observer(function ComposePost({
) : undefined}
+
+
+
+
{!extLink && suggestedLinks.size > 0 ? (
diff --git a/src/view/com/composer/TagInput.tsx b/src/view/com/composer/TagInput.tsx
new file mode 100644
index 0000000000..ac374a4899
--- /dev/null
+++ b/src/view/com/composer/TagInput.tsx
@@ -0,0 +1,129 @@
+import React from 'react'
+import {
+ TextInput,
+ View,
+ StyleSheet,
+ NativeSyntheticEvent,
+ TextInputKeyPressEventData,
+ Pressable,
+} from 'react-native'
+import {
+ FontAwesomeIcon,
+ FontAwesomeIconStyle,
+} from '@fortawesome/react-native-fontawesome'
+
+import {Text} from 'view/com/util/text/Text'
+import {usePalette} from 'lib/hooks/usePalette'
+
+function Tag({
+ tag,
+ removeTag,
+}: {
+ tag: string
+ removeTag: (tag: string) => void
+}) {
+ const pal = usePalette('default')
+ return (
+ removeTag(tag)}
+ style={state => ({
+ opacity: state.hovered || state.pressed || state.focused ? 0.8 : 1,
+ outline: 0,
+ })}>
+
+ #{tag}
+
+
+ )
+}
+
+export function TagInput({max}: {max: number}) {
+ const pal = usePalette('default')
+ const input = React.useRef(null)
+ const [value, setValue] = React.useState('')
+ const [tags, setTags] = React.useState([])
+
+ const onKeyPress = React.useCallback(
+ (e: NativeSyntheticEvent) => {
+ if (e.nativeEvent.key === 'Enter') {
+ const _tags = value.trim().split(' ').filter(Boolean)
+
+ if (_tags.length > 0) {
+ setTags(Array.from(new Set([...tags, ..._tags])).slice(0, max))
+ setValue('')
+ }
+
+ setTimeout(() => {
+ input.current?.focus()
+ }, 100)
+ } else if (e.nativeEvent.key === 'Backspace' && value === '') {
+ setTags(tags.slice(0, -1))
+ }
+ },
+ [max, value, tags, setValue, setTags],
+ )
+
+ const onChangeText = React.useCallback((value: string) => {
+ const sanitized = value.replace(/^#/, '')
+ setValue(sanitized)
+ }, [])
+
+ const removeTag = React.useCallback(
+ (tag: string) => {
+ setTags(tags.filter(t => t !== tag))
+ },
+ [tags, setTags],
+ )
+
+ return (
+
+ {!tags.length && (
+
+ )}
+ {tags.map(tag => (
+
+ ))}
+ {tags.length >= max ? null : (
+
+ )}
+
+ )
+}
+
+const styles = StyleSheet.create({
+ outer: {
+ flexDirection: 'row',
+ flexWrap: 'wrap',
+ alignItems: 'center',
+ gap: 8,
+ },
+ input: {
+ flexGrow: 1,
+ minWidth: 100,
+ fontSize: 13,
+ paddingVertical: 4,
+ },
+ tag: {
+ paddingVertical: 4,
+ paddingHorizontal: 8,
+ borderRadius: 4,
+ },
+})
diff --git a/src/view/index.ts b/src/view/index.ts
index 1e6f274191..09177d0f75 100644
--- a/src/view/index.ts
+++ b/src/view/index.ts
@@ -98,6 +98,7 @@ import {faUsersSlash} from '@fortawesome/free-solid-svg-icons/faUsersSlash'
import {faX} from '@fortawesome/free-solid-svg-icons/faX'
import {faXmark} from '@fortawesome/free-solid-svg-icons/faXmark'
import {faChevronDown} from '@fortawesome/free-solid-svg-icons/faChevronDown'
+import {faTags} from '@fortawesome/free-solid-svg-icons/faTags'
export function setup() {
library.add(
@@ -199,5 +200,6 @@ export function setup() {
faX,
faXmark,
faChevronDown,
+ faTags,
)
}