about summary refs log tree commit diff
path: root/src/view/shell/desktop
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/shell/desktop')
-rw-r--r--src/view/shell/desktop/Feeds.tsx4
-rw-r--r--src/view/shell/desktop/LeftNav.tsx46
-rw-r--r--src/view/shell/desktop/RightNav.tsx37
-rw-r--r--src/view/shell/desktop/Search.tsx104
4 files changed, 135 insertions, 56 deletions
diff --git a/src/view/shell/desktop/Feeds.tsx b/src/view/shell/desktop/Feeds.tsx
index 93b96e704..a8f5f1c66 100644
--- a/src/view/shell/desktop/Feeds.tsx
+++ b/src/view/shell/desktop/Feeds.tsx
@@ -21,7 +21,7 @@ export function DesktopFeeds() {
   })
 
   return (
-    <View style={[styles.container, pal.view, pal.border]}>
+    <View style={[styles.container, pal.view]}>
       <FeedItem href="/" title="Following" current={route.name === 'Home'} />
       {feeds
         .filter(f => f.displayName !== 'Following')
@@ -91,7 +91,5 @@ const styles = StyleSheet.create({
     width: 300,
     paddingHorizontal: 12,
     paddingVertical: 18,
-    borderTopWidth: 1,
-    borderBottomWidth: 1,
   },
 })
diff --git a/src/view/shell/desktop/LeftNav.tsx b/src/view/shell/desktop/LeftNav.tsx
index f3c8c1d11..b27898828 100644
--- a/src/view/shell/desktop/LeftNav.tsx
+++ b/src/view/shell/desktop/LeftNav.tsx
@@ -239,24 +239,26 @@ function ComposeBtn() {
     return null
   }
   return (
-    <TouchableOpacity
-      disabled={isFetchingHandle}
-      style={[styles.newPostBtn]}
-      onPress={onPressCompose}
-      accessibilityRole="button"
-      accessibilityLabel={_(msg`New post`)}
-      accessibilityHint="">
-      <View style={styles.newPostBtnIconWrapper}>
-        <ComposeIcon2
-          size={19}
-          strokeWidth={2}
-          style={styles.newPostBtnLabel}
-        />
-      </View>
-      <Text type="button" style={styles.newPostBtnLabel}>
-        <Trans>New Post</Trans>
-      </Text>
-    </TouchableOpacity>
+    <View style={styles.newPostBtnContainer}>
+      <TouchableOpacity
+        disabled={isFetchingHandle}
+        style={styles.newPostBtn}
+        onPress={onPressCompose}
+        accessibilityRole="button"
+        accessibilityLabel={_(msg`New post`)}
+        accessibilityHint="">
+        <View style={styles.newPostBtnIconWrapper}>
+          <ComposeIcon2
+            size={19}
+            strokeWidth={2}
+            style={styles.newPostBtnLabel}
+          />
+        </View>
+        <Text type="button" style={styles.newPostBtnLabel}>
+          <Trans context="action">New Post</Trans>
+        </Text>
+      </TouchableOpacity>
+    </View>
   )
 }
 
@@ -440,10 +442,11 @@ export function DesktopLeftNav() {
 
 const styles = StyleSheet.create({
   leftNav: {
-    position: 'absolute',
+    // @ts-ignore web only
+    position: 'fixed',
     top: 10,
     // @ts-ignore web only
-    right: 'calc(50vw + 312px)',
+    left: 'calc(50vw - 300px - 220px - 20px)',
     width: 220,
     // @ts-ignore web only
     maxHeight: 'calc(100vh - 10px)',
@@ -512,6 +515,9 @@ const styles = StyleSheet.create({
     fontSize: 14,
   },
 
+  newPostBtnContainer: {
+    flexDirection: 'row',
+  },
   newPostBtn: {
     flexDirection: 'row',
     alignItems: 'center',
diff --git a/src/view/shell/desktop/RightNav.tsx b/src/view/shell/desktop/RightNav.tsx
index 8d9961a5f..328c527e4 100644
--- a/src/view/shell/desktop/RightNav.tsx
+++ b/src/view/shell/desktop/RightNav.tsx
@@ -16,7 +16,7 @@ import {Plural, Trans, msg, plural} from '@lingui/macro'
 import {useSession} from '#/state/session'
 import {useInviteCodesQuery} from '#/state/queries/invites'
 
-export function DesktopRightNav() {
+export function DesktopRightNav({routeName}: {routeName: string}) {
   const pal = usePalette('default')
   const palError = usePalette('error')
   const {_} = useLingui()
@@ -30,12 +30,20 @@ export function DesktopRightNav() {
   return (
     <View style={[styles.rightNav, pal.view]}>
       <View style={{paddingVertical: 20}}>
-        <DesktopSearch />
-
-        {hasSession && (
-          <View style={{paddingTop: 18, marginBottom: 18}}>
+        {routeName === 'Search' ? (
+          <View style={{marginBottom: 18}}>
             <DesktopFeeds />
           </View>
+        ) : (
+          <>
+            <DesktopSearch />
+
+            {hasSession && (
+              <View style={[pal.border, styles.desktopFeedsContainer]}>
+                <DesktopFeeds />
+              </View>
+            )}
+          </>
         )}
 
         <View
@@ -48,7 +56,7 @@ export function DesktopRightNav() {
           {isSandbox ? (
             <View style={[palError.view, styles.messageLine, s.p10]}>
               <Text type="md" style={[palError.text, s.bold]}>
-                SANDBOX. Posts and accounts are not permanent.
+                <Trans>SANDBOX. Posts and accounts are not permanent.</Trans>
               </Text>
             </View>
           ) : undefined}
@@ -169,17 +177,18 @@ function InviteCodes() {
 
 const styles = StyleSheet.create({
   rightNav: {
-    position: 'absolute',
     // @ts-ignore web only
-    left: 'calc(50vw + 320px)',
-    width: 304,
+    position: 'fixed',
+    // @ts-ignore web only
+    left: 'calc(50vw + 300px + 20px)',
+    width: 300,
     maxHeight: '100%',
     overflowY: 'auto',
   },
 
   message: {
     paddingVertical: 18,
-    paddingHorizontal: 10,
+    paddingHorizontal: 12,
   },
   messageLine: {
     marginBottom: 10,
@@ -187,7 +196,7 @@ const styles = StyleSheet.create({
 
   inviteCodes: {
     borderTopWidth: 1,
-    paddingHorizontal: 16,
+    paddingHorizontal: 12,
     paddingVertical: 12,
     flexDirection: 'row',
   },
@@ -196,4 +205,10 @@ const styles = StyleSheet.create({
     marginRight: 6,
     flexShrink: 0,
   },
+  desktopFeedsContainer: {
+    borderTopWidth: 1,
+    borderBottomWidth: 1,
+    marginTop: 18,
+    marginBottom: 18,
+  },
 })
diff --git a/src/view/shell/desktop/Search.tsx b/src/view/shell/desktop/Search.tsx
index 6201f828f..4a9483733 100644
--- a/src/view/shell/desktop/Search.tsx
+++ b/src/view/shell/desktop/Search.tsx
@@ -29,13 +29,63 @@ import {UserAvatar} from '#/view/com/util/UserAvatar'
 import {useActorAutocompleteFn} from '#/state/queries/actor-autocomplete'
 import {useModerationOpts} from '#/state/queries/preferences'
 
-export function SearchResultCard({
-  profile,
+export const MATCH_HANDLE =
+  /@?([a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*(?:\.[a-zA-Z]{2,}))/
+
+export function SearchLinkCard({
+  label,
+  to,
+  onPress,
   style,
+}: {
+  label: string
+  to?: string
+  onPress?: () => void
+  style?: ViewStyle
+}) {
+  const pal = usePalette('default')
+
+  const inner = (
+    <View
+      style={[pal.border, {paddingVertical: 16, paddingHorizontal: 12}, style]}>
+      <Text type="md" style={[pal.text]}>
+        {label}
+      </Text>
+    </View>
+  )
+
+  if (onPress) {
+    return (
+      <TouchableOpacity
+        onPress={onPress}
+        accessibilityLabel={label}
+        accessibilityHint="">
+        {inner}
+      </TouchableOpacity>
+    )
+  }
+
+  return (
+    <Link href={to} asAnchor anchorNoUnderline>
+      <View
+        style={[
+          pal.border,
+          {paddingVertical: 16, paddingHorizontal: 12},
+          style,
+        ]}>
+        <Text type="md" style={[pal.text]}>
+          {label}
+        </Text>
+      </View>
+    </Link>
+  )
+}
+
+export function SearchProfileCard({
+  profile,
   moderation,
 }: {
   profile: AppBskyActorDefs.ProfileViewBasic
-  style: ViewStyle
   moderation: ProfileModeration
 }) {
   const pal = usePalette('default')
@@ -50,9 +100,7 @@ export function SearchResultCard({
       <View
         style={[
           pal.border,
-          style,
           {
-            borderTopWidth: 1,
             flexDirection: 'row',
             alignItems: 'center',
             gap: 12,
@@ -147,6 +195,11 @@ export function DesktopSearch() {
     navigation.dispatch(StackActions.push('Search', {q: query}))
   }, [query, navigation, setSearchResults])
 
+  const queryMaybeHandle = React.useMemo(() => {
+    const match = MATCH_HANDLE.exec(query)
+    return match && match[1]
+  }, [query])
+
   return (
     <View style={[styles.container, pal.view]}>
       <View
@@ -169,6 +222,9 @@ export function DesktopSearch() {
             accessibilityRole="search"
             accessibilityLabel={_(msg`Search`)}
             accessibilityHint=""
+            autoCorrect={false}
+            autoComplete="off"
+            autoCapitalize="none"
           />
           {query ? (
             <View style={styles.cancelBtn}>
@@ -176,7 +232,7 @@ export function DesktopSearch() {
                 onPress={onPressCancelSearch}
                 accessibilityRole="button"
                 accessibilityLabel={_(msg`Cancel search`)}
-                accessibilityHint="Exits inputting search query"
+                accessibilityHint={_(msg`Exits inputting search query`)}
                 onAccessibilityEscape={onPressCancelSearch}>
                 <Text type="lg" style={[pal.link]}>
                   <Trans>Cancel</Trans>
@@ -195,22 +251,26 @@ export function DesktopSearch() {
             </View>
           ) : (
             <>
-              {searchResults.length ? (
-                searchResults.map((item, i) => (
-                  <SearchResultCard
-                    key={item.did}
-                    profile={item}
-                    moderation={moderateProfile(item, moderationOpts)}
-                    style={i === 0 ? {borderTopWidth: 0} : {}}
-                  />
-                ))
-              ) : (
-                <View>
-                  <Text style={[pal.textLight, styles.noResults]}>
-                    <Trans>No results found for {query}</Trans>
-                  </Text>
-                </View>
-              )}
+              <SearchLinkCard
+                label={_(msg`Search for "${query}"`)}
+                to={`/search?q=${encodeURIComponent(query)}`}
+                style={{borderBottomWidth: 1}}
+              />
+
+              {queryMaybeHandle ? (
+                <SearchLinkCard
+                  label={_(msg`Go to @${queryMaybeHandle}`)}
+                  to={`/profile/${queryMaybeHandle}`}
+                />
+              ) : null}
+
+              {searchResults.map(item => (
+                <SearchProfileCard
+                  key={item.did}
+                  profile={item}
+                  moderation={moderateProfile(item, moderationOpts)}
+                />
+              ))}
             </>
           )}
         </View>