diff --git a/babel.config.js b/babel.config.js
index 6ba90e98..fa49ff86 100644
--- a/babel.config.js
+++ b/babel.config.js
@@ -20,6 +20,7 @@ module.exports = {
alias: {
// This needs to be mirrored in tsconfig.json
lib: './src/lib',
+ platform: './src/platform',
state: './src/state',
view: './src/view',
},
diff --git a/src/view/com/util/ViewHeader.web.tsx b/src/view/com/util/ViewHeader.web.tsx
index 5c0869e8..ef70ecab 100644
--- a/src/view/com/util/ViewHeader.web.tsx
+++ b/src/view/com/util/ViewHeader.web.tsx
@@ -1,69 +1,51 @@
import React from 'react'
import {observer} from 'mobx-react-lite'
import {StyleSheet, TouchableOpacity, View} from 'react-native'
-import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
-import {CenteredView} from './Views'
import {Text} from './text/Text'
-import {useStores} from 'state/index'
+import {Link} from './Link'
import {usePalette} from 'lib/hooks/usePalette'
+import {useStores} from 'state/index'
+import {ComposeIcon, MagnifyingGlassIcon} from 'lib/icons'
import {colors} from 'lib/styles'
-const BACK_HITSLOP = {left: 10, top: 10, right: 30, bottom: 10}
-
export const ViewHeader = observer(function ViewHeader({
title,
- subtitle,
- canGoBack,
}: {
title: string
- subtitle?: string
canGoBack?: boolean
}) {
- const pal = usePalette('default')
const store = useStores()
- const onPressBack = () => {
- store.nav.tab.goBack()
- }
- if (typeof canGoBack === 'undefined') {
- canGoBack = store.nav.tab.canGoBack
- }
+ const pal = usePalette('default')
+ const onPressCompose = () => store.shell.openComposer({})
return (
-
- {canGoBack ? (
- <>
-
-
-
-
-
- {title}
-
- {subtitle ? (
-
- {subtitle}
-
- ) : undefined}
-
- >
- ) : (
-
-
- Home
-
+
+
+
+ {title}
+
+
+
+
+
- )}
-
+
+ New Post
+
+
+
+
+
+ Search
+
+
+
)
})
@@ -71,44 +53,52 @@ const styles = StyleSheet.create({
header: {
flexDirection: 'row',
alignItems: 'center',
- paddingHorizontal: 16,
- paddingVertical: 12,
+ paddingTop: 24,
+ paddingBottom: 18,
+ paddingLeft: 30,
+ paddingRight: 40,
+ marginLeft: 300,
+ borderBottomWidth: 1,
},
titleContainer: {
- flexDirection: 'row',
- alignItems: 'baseline',
marginRight: 'auto',
},
title: {
fontWeight: 'bold',
},
- subtitle: {
- marginLeft: 4,
- maxWidth: 200,
- fontWeight: 'normal',
+
+ search: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ width: 300,
+ borderRadius: 20,
+ paddingVertical: 8,
+ paddingHorizontal: 10,
+ borderWidth: 1,
+ },
+ searchIconWrapper: {
+ flexDirection: 'row',
+ width: 30,
+ justifyContent: 'center',
+ marginRight: 2,
},
- backBtn: {
- width: 30,
- },
- backIcon: {
- position: 'relative',
- top: -1,
- },
- btn: {
+ newPostBtn: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
- width: 36,
- height: 36,
- borderRadius: 20,
- marginLeft: 4,
+ borderRadius: 24,
+ paddingTop: 8,
+ paddingBottom: 9,
+ paddingHorizontal: 18,
+ backgroundColor: colors.blue3,
+ marginRight: 10,
},
- littleXIcon: {
- color: colors.red3,
- position: 'absolute',
- right: 7,
- bottom: 7,
+ newPostBtnIconWrapper: {
+ marginRight: 8,
+ },
+ newPostBtnLabel: {
+ color: colors.white,
},
})
diff --git a/src/view/com/util/Views.web.tsx b/src/view/com/util/Views.web.tsx
index f00d3c07..3d9abd89 100644
--- a/src/view/com/util/Views.web.tsx
+++ b/src/view/com/util/Views.web.tsx
@@ -22,7 +22,6 @@ import {
View,
ViewProps,
} from 'react-native'
-import {useTheme} from 'lib/ThemeContext'
import {addStyle, colors} from 'lib/styles'
export function CenteredView({
@@ -40,15 +39,10 @@ export const FlatList = React.forwardRef(function (
}: React.PropsWithChildren>,
ref: React.Ref,
) {
- const theme = useTheme()
contentContainerStyle = addStyle(
contentContainerStyle,
styles.containerScroll,
)
- contentContainerStyle = addStyle(
- contentContainerStyle,
- theme.colorScheme === 'dark' ? styles.containerDark : styles.containerLight,
- )
return (
(
export const ScrollView = React.forwardRef(function (
{contentContainerStyle, ...props}: React.PropsWithChildren,
- ref: React.Ref,
+ ref: React.Ref,
) {
- const theme = useTheme()
contentContainerStyle = addStyle(
contentContainerStyle,
styles.containerScroll,
)
- contentContainerStyle = addStyle(
- contentContainerStyle,
- theme.colorScheme === 'dark' ? styles.containerDark : styles.containerLight,
- )
return (
+ {isWeb && }
-
+ {!isWeb && }
{store.me.mainFeed.hasNewLatest && !store.me.mainFeed.isRefreshing && (
)}
diff --git a/src/view/screens/Search.web.tsx b/src/view/screens/Search.web.tsx
new file mode 100644
index 00000000..38f7cefb
--- /dev/null
+++ b/src/view/screens/Search.web.tsx
@@ -0,0 +1,217 @@
+import React from 'react'
+import {
+ Keyboard,
+ StyleSheet,
+ TextInput,
+ TouchableOpacity,
+ View,
+} from 'react-native'
+import {ScrollView} from '../com/util/Views'
+import {observer} from 'mobx-react-lite'
+import {UserAvatar} from '../com/util/UserAvatar'
+import {Text} from '../com/util/text/Text'
+import {ScreenParams} from '../routes'
+import {useStores} from 'state/index'
+import {UserAutocompleteViewModel} from 'state/models/user-autocomplete-view'
+import {s} from 'lib/styles'
+import {MagnifyingGlassIcon} from 'lib/icons'
+import {ViewHeader} from '../com/util/ViewHeader'
+import {WhoToFollow} from '../com/discover/WhoToFollow'
+import {SuggestedPosts} from '../com/discover/SuggestedPosts'
+import {ProfileCard} from '../com/profile/ProfileCard'
+import {usePalette} from 'lib/hooks/usePalette'
+import {useOnMainScroll} from 'lib/hooks/useOnMainScroll'
+import {useAnalytics} from 'lib/analytics'
+
+const MENU_HITSLOP = {left: 10, top: 10, right: 30, bottom: 10}
+const FIVE_MIN = 5 * 60 * 1e3
+
+export const Search = observer(({navIdx, visible, params}: ScreenParams) => {
+ const pal = usePalette('default')
+ const store = useStores()
+ const {track} = useAnalytics()
+ const scrollElRef = React.useRef(null)
+ const onMainScroll = useOnMainScroll(store)
+ const textInput = React.useRef(null)
+ const [lastRenderTime, setRenderTime] = React.useState(Date.now()) // used to trigger reloads
+ const [isInputFocused, setIsInputFocused] = React.useState(false)
+ const [query, setQuery] = React.useState('')
+ const autocompleteView = React.useMemo(
+ () => new UserAutocompleteViewModel(store),
+ [store],
+ )
+ const {name} = params
+
+ const onSoftReset = () => {
+ scrollElRef.current?.scrollTo({x: 0, y: 0})
+ }
+
+ React.useEffect(() => {
+ const softResetSub = store.onScreenSoftReset(onSoftReset)
+ const cleanup = () => {
+ softResetSub.remove()
+ }
+
+ if (visible) {
+ const now = Date.now()
+ if (now - lastRenderTime > FIVE_MIN) {
+ setRenderTime(Date.now()) // trigger reload of suggestions
+ }
+ store.shell.setMinimalShellMode(false)
+ autocompleteView.setup()
+ store.nav.setTitle(navIdx, 'Search')
+ }
+ return cleanup
+ }, [store, visible, name, navIdx, autocompleteView, lastRenderTime])
+
+ const onPressMenu = () => {
+ track('ViewHeader:MenuButtonClicked')
+ store.shell.setMainMenuOpen(true)
+ }
+
+ const onChangeQuery = (text: string) => {
+ setQuery(text)
+ if (text.length > 0) {
+ autocompleteView.setActive(true)
+ autocompleteView.setPrefix(text)
+ } else {
+ autocompleteView.setActive(false)
+ }
+ }
+ const onPressCancelSearch = () => {
+ setQuery('')
+ autocompleteView.setActive(false)
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+ setIsInputFocused(true)}
+ onBlur={() => setIsInputFocused(false)}
+ onChangeText={onChangeQuery}
+ />
+
+ {query ? (
+
+
+ Cancel
+
+
+ ) : undefined}
+
+ {query && autocompleteView.searchRes.length ? (
+ <>
+ {autocompleteView.searchRes.map(item => (
+
+ ))}
+ >
+ ) : query && !autocompleteView.searchRes.length ? (
+
+
+ No results found for {autocompleteView.prefix}
+
+
+ ) : isInputFocused ? (
+
+
+ Search for users on the network
+
+
+ ) : (
+
+
+
+
+
+ )}
+
+
+
+ )
+})
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ },
+
+ header: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ paddingHorizontal: 12,
+ paddingTop: 4,
+ marginBottom: 14,
+ },
+ headerMenuBtn: {
+ width: 40,
+ height: 30,
+ marginLeft: 6,
+ },
+ headerSearchContainer: {
+ flex: 1,
+ flexDirection: 'row',
+ alignItems: 'center',
+ borderRadius: 30,
+ paddingHorizontal: 12,
+ paddingVertical: 8,
+ },
+ headerSearchIcon: {
+ marginRight: 6,
+ alignSelf: 'center',
+ },
+ headerSearchInput: {
+ flex: 1,
+ fontSize: 17,
+ },
+ headerCancelBtn: {
+ width: 60,
+ paddingLeft: 10,
+ },
+
+ searchPrompt: {
+ textAlign: 'center',
+ paddingTop: 10,
+ },
+})
diff --git a/src/view/shell/web/DesktopLeftColumn.tsx b/src/view/shell/web/DesktopLeftColumn.tsx
index 819bcba6..54e3e93e 100644
--- a/src/view/shell/web/DesktopLeftColumn.tsx
+++ b/src/view/shell/web/DesktopLeftColumn.tsx
@@ -70,12 +70,7 @@ export const DesktopLeftColumn = observer(() => {
styles.containerBgLight,
styles.containerBgDark,
)
- const hoverBg = useColorSchemeStyle(
- styles.navItemHoverBgLight,
- styles.navItemHoverBgDark,
- )
const pal = usePalette('default')
- const onPressCompose = () => store.shell.openComposer({})
const avi = (
{
Bluesky
-
-
-
- Search
-
-
{
icon={}
iconFilled={}
/>
-
- [
- // @ts-ignore Pressable state differs for RNW -prf
- state.hovered && hoverBg,
- ]}>
-
-
-
-
- New Post
-
-
{
- const store = useStores()
return (
@@ -21,6 +19,7 @@ const styles = StyleSheet.create({
container: {
position: 'absolute',
right: 0,
+ top: 90,
width: '400px',
paddingHorizontal: 16,
paddingRight: 32,
diff --git a/tsconfig.json b/tsconfig.json
index 2b93dd0f..cace91f5 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -3,6 +3,7 @@
"compilerOptions": {
"paths": {
"lib/*": ["./src/lib/*"],
+ "platform/*": ["./src/platform/*"],
"state/*": ["./src/state/*"],
"view/*": ["./src/view/*"]
}