about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/components/Lists.tsx13
-rw-r--r--src/components/forms/DateField/index.android.tsx39
-rw-r--r--src/components/forms/DateField/index.tsx20
-rw-r--r--src/lib/hooks/useInitialNumToRender.ts11
-rw-r--r--src/lib/strings/url-helpers.ts12
-rw-r--r--src/screens/Hashtag.tsx11
-rw-r--r--src/state/queries/preferences/const.ts2
-rw-r--r--src/state/queries/preferences/index.ts2
-rw-r--r--src/view/com/posts/Feed.tsx4
-rw-r--r--src/view/com/util/Views.d.ts4
-rw-r--r--src/view/com/util/Views.web.tsx11
-rw-r--r--src/view/com/util/forms/DateInput.tsx32
-rw-r--r--src/view/shell/desktop/LeftNav.tsx2
13 files changed, 99 insertions, 64 deletions
diff --git a/src/components/Lists.tsx b/src/components/Lists.tsx
index 9778545d2..c879f9411 100644
--- a/src/components/Lists.tsx
+++ b/src/components/Lists.tsx
@@ -1,6 +1,7 @@
 import React from 'react'
 import {atoms as a, useBreakpoints, useTheme} from '#/alf'
 import {View} from 'react-native'
+import {CenteredView} from 'view/com/util/Views'
 import {Loader} from '#/components/Loader'
 import {Trans, msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
@@ -145,7 +146,7 @@ export function ListMaybePlaceholder({
 }) {
   const navigation = useNavigation<NavigationProp>()
   const t = useTheme()
-  const {gtMobile} = useBreakpoints()
+  const {gtMobile, gtTablet} = useBreakpoints()
   const {_} = useLingui()
 
   const canGoBack = navigation.canGoBack()
@@ -168,14 +169,16 @@ export function ListMaybePlaceholder({
   if (!isEmpty) return null
 
   return (
-    <View
+    <CenteredView
       style={[
         a.flex_1,
         a.align_center,
-        !gtMobile ? [a.justify_between, a.border_t] : a.gap_5xl,
+        !gtMobile ? a.justify_between : a.gap_5xl,
         t.atoms.border_contrast_low,
         {paddingTop: 175, paddingBottom: 110},
-      ]}>
+      ]}
+      sideBorders={gtMobile}
+      topBorder={!gtTablet}>
       {isLoading ? (
         <View style={[a.w_full, a.align_center, {top: 100}]}>
           <Loader size="xl" />
@@ -244,6 +247,6 @@ export function ListMaybePlaceholder({
           </View>
         </>
       )}
-    </View>
+    </CenteredView>
   )
 }
diff --git a/src/components/forms/DateField/index.android.tsx b/src/components/forms/DateField/index.android.tsx
index cddb643d6..451810a5e 100644
--- a/src/components/forms/DateField/index.android.tsx
+++ b/src/components/forms/DateField/index.android.tsx
@@ -1,8 +1,5 @@
 import React from 'react'
 import {View, Pressable} from 'react-native'
-import DateTimePicker, {
-  BaseProps as DateTimePickerProps,
-} from '@react-native-community/datetimepicker'
 
 import {useTheme, atoms} from '#/alf'
 import {Text} from '#/components/Typography'
@@ -15,6 +12,8 @@ import {
   localizeDate,
   toSimpleDateString,
 } from '#/components/forms/DateField/utils'
+import DatePicker from 'react-native-date-picker'
+import {isAndroid} from 'platform/detection'
 
 export * as utils from '#/components/forms/DateField/utils'
 export const Label = TextField.Label
@@ -38,20 +37,20 @@ export function DateField({
   const {chromeFocus, chromeError, chromeErrorHover} =
     TextField.useSharedInputStyles()
 
-  const onChangeInternal = React.useCallback<
-    Required<DateTimePickerProps>['onChange']
-  >(
-    (_event, date) => {
+  const onChangeInternal = React.useCallback(
+    (date: Date) => {
       setOpen(false)
 
-      if (date) {
-        const formatted = toSimpleDateString(date)
-        onChangeDate(formatted)
-      }
+      const formatted = toSimpleDateString(date)
+      onChangeDate(formatted)
     },
     [onChangeDate, setOpen],
   )
 
+  const onCancel = React.useCallback(() => {
+    setOpen(false)
+  }, [])
+
   return (
     <View style={[atoms.relative, atoms.w_full]}>
       <Pressable
@@ -89,18 +88,18 @@ export function DateField({
       </Pressable>
 
       {open && (
-        <DateTimePicker
+        <DatePicker
+          modal={isAndroid}
+          open={isAndroid}
+          theme={t.name === 'light' ? 'light' : 'dark'}
+          date={new Date(value)}
+          onConfirm={onChangeInternal}
+          onCancel={onCancel}
+          mode="date"
+          testID={`${testID}-datepicker`}
           aria-label={label}
           accessibilityLabel={label}
           accessibilityHint={undefined}
-          testID={`${testID}-datepicker`}
-          mode="date"
-          timeZoneName={'Etc/UTC'}
-          display="spinner"
-          // @ts-ignore applies in iOS only -prf
-          themeVariant={t.name === 'light' ? 'light' : 'dark'}
-          value={new Date(value)}
-          onChange={onChangeInternal}
         />
       )}
     </View>
diff --git a/src/components/forms/DateField/index.tsx b/src/components/forms/DateField/index.tsx
index e65936e0e..49e47a01e 100644
--- a/src/components/forms/DateField/index.tsx
+++ b/src/components/forms/DateField/index.tsx
@@ -1,13 +1,11 @@
 import React from 'react'
 import {View} from 'react-native'
-import DateTimePicker, {
-  DateTimePickerEvent,
-} from '@react-native-community/datetimepicker'
 
 import {useTheme, atoms} from '#/alf'
 import * as TextField from '#/components/forms/TextField'
 import {toSimpleDateString} from '#/components/forms/DateField/utils'
 import {DateFieldProps} from '#/components/forms/DateField/types'
+import DatePicker from 'react-native-date-picker'
 
 export * as utils from '#/components/forms/DateField/utils'
 export const Label = TextField.Label
@@ -28,7 +26,7 @@ export function DateField({
   const t = useTheme()
 
   const onChangeInternal = React.useCallback(
-    (event: DateTimePickerEvent, date: Date | undefined) => {
+    (date: Date | undefined) => {
       if (date) {
         const formatted = toSimpleDateString(date)
         onChangeDate(formatted)
@@ -39,17 +37,15 @@ export function DateField({
 
   return (
     <View style={[atoms.relative, atoms.w_full]}>
-      <DateTimePicker
+      <DatePicker
+        theme={t.name === 'light' ? 'light' : 'dark'}
+        date={new Date(value)}
+        onDateChange={onChangeInternal}
+        mode="date"
+        testID={`${testID}-datepicker`}
         aria-label={label}
         accessibilityLabel={label}
         accessibilityHint={undefined}
-        testID={`${testID}-datepicker`}
-        mode="date"
-        timeZoneName={'Etc/UTC'}
-        display="spinner"
-        themeVariant={t.name === 'light' ? 'light' : 'dark'}
-        value={new Date(value)}
-        onChange={onChangeInternal}
       />
     </View>
   )
diff --git a/src/lib/hooks/useInitialNumToRender.ts b/src/lib/hooks/useInitialNumToRender.ts
new file mode 100644
index 000000000..942f0404a
--- /dev/null
+++ b/src/lib/hooks/useInitialNumToRender.ts
@@ -0,0 +1,11 @@
+import React from 'react'
+import {Dimensions} from 'react-native'
+
+const MIN_POST_HEIGHT = 100
+
+export function useInitialNumToRender(minItemHeight: number = MIN_POST_HEIGHT) {
+  return React.useMemo(() => {
+    const screenHeight = Dimensions.get('window').height
+    return Math.ceil(screenHeight / minItemHeight) + 1
+  }, [minItemHeight])
+}
diff --git a/src/lib/strings/url-helpers.ts b/src/lib/strings/url-helpers.ts
index ba2cdb39b..7729e4a38 100644
--- a/src/lib/strings/url-helpers.ts
+++ b/src/lib/strings/url-helpers.ts
@@ -148,6 +148,11 @@ export function feedUriToHref(url: string): string {
 export function linkRequiresWarning(uri: string, label: string) {
   const labelDomain = labelToDomain(label)
 
+  // If the uri started with a / we know it is internal.
+  if (uri.startsWith('/')) {
+    return false
+  }
+
   let urip
   try {
     urip = new URL(uri)
@@ -156,9 +161,12 @@ export function linkRequiresWarning(uri: string, label: string) {
   }
 
   const host = urip.hostname.toLowerCase()
-
   // Hosts that end with bsky.app or bsky.social should be trusted by default.
-  if (host.endsWith('bsky.app') || host.endsWith('bsky.social')) {
+  if (
+    host.endsWith('bsky.app') ||
+    host.endsWith('bsky.social') ||
+    host.endsWith('blueskyweb.xyz')
+  ) {
     // if this is a link to internal content,
     // warn if it represents itself as a URL to another app
     return !!labelDomain && labelDomain !== host && isPossiblyAUrl(labelDomain)
diff --git a/src/screens/Hashtag.tsx b/src/screens/Hashtag.tsx
index 09a1f2824..f1b817370 100644
--- a/src/screens/Hashtag.tsx
+++ b/src/screens/Hashtag.tsx
@@ -1,6 +1,5 @@
 import React from 'react'
 import {ListRenderItemInfo, Pressable} from 'react-native'
-import {atoms as a, useBreakpoints} from '#/alf'
 import {useFocusEffect} from '@react-navigation/native'
 import {useSetMinimalShellMode} from 'state/shell'
 import {ViewHeader} from 'view/com/util/ViewHeader'
@@ -19,11 +18,11 @@ import {List} from 'view/com/util/List'
 import {msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 import {sanitizeHandle} from 'lib/strings/handles'
-import {CenteredView} from 'view/com/util/Views'
 import {ArrowOutOfBox_Stroke2_Corner0_Rounded} from '#/components/icons/ArrowOutOfBox'
 import {shareUrl} from 'lib/sharing'
 import {HITSLOP_10} from 'lib/constants'
 import {isNative} from 'platform/detection'
+import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender'
 
 const renderItem = ({item}: ListRenderItemInfo<PostView>) => {
   return <Post post={item} />
@@ -38,8 +37,8 @@ export default function HashtagScreen({
 }: NativeStackScreenProps<CommonNavigatorParams, 'Hashtag'>) {
   const {tag, author} = route.params
   const setMinimalShellMode = useSetMinimalShellMode()
-  const {gtMobile} = useBreakpoints()
   const {_} = useLingui()
+  const initialNumToRender = useInitialNumToRender()
   const [isPTR, setIsPTR] = React.useState(false)
 
   const fullTag = React.useMemo(() => {
@@ -103,7 +102,7 @@ export default function HashtagScreen({
   }, [isFetching, hasNextPage, error, fetchNextPage])
 
   return (
-    <CenteredView style={a.flex_1} sideBorders={gtMobile}>
+    <>
       <ViewHeader
         title={headerTitle}
         subtitle={author ? _(msg`From @${sanitizedAuthor}`) : undefined}
@@ -157,8 +156,10 @@ export default function HashtagScreen({
               onRetry={fetchNextPage}
             />
           }
+          initialNumToRender={initialNumToRender}
+          windowSize={11}
         />
       )}
-    </CenteredView>
+    </>
   )
 }
diff --git a/src/state/queries/preferences/const.ts b/src/state/queries/preferences/const.ts
index 25d284998..53c9e482a 100644
--- a/src/state/queries/preferences/const.ts
+++ b/src/state/queries/preferences/const.ts
@@ -7,7 +7,7 @@ import {DEFAULT_LOGGED_OUT_LABEL_PREFERENCES} from '#/state/queries/preferences/
 export const DEFAULT_HOME_FEED_PREFS: UsePreferencesQueryResponse['feedViewPrefs'] =
   {
     hideReplies: false,
-    hideRepliesByUnfollowed: false,
+    hideRepliesByUnfollowed: true,
     hideRepliesByLikeCount: 0,
     hideReposts: false,
     hideQuotePosts: false,
diff --git a/src/state/queries/preferences/index.ts b/src/state/queries/preferences/index.ts
index 07198de77..37ef10ae0 100644
--- a/src/state/queries/preferences/index.ts
+++ b/src/state/queries/preferences/index.ts
@@ -169,7 +169,7 @@ export function usePreferencesSetBirthDateMutation() {
 
   return useMutation<void, unknown, {birthDate: Date}>({
     mutationFn: async ({birthDate}: {birthDate: Date}) => {
-      await getAgent().setPersonalDetails({birthDate})
+      await getAgent().setPersonalDetails({birthDate: birthDate.toISOString()})
       // triggers a refetch
       await queryClient.invalidateQueries({
         queryKey: preferencesQueryKey,
diff --git a/src/view/com/posts/Feed.tsx b/src/view/com/posts/Feed.tsx
index 54d8aa224..cd3e98785 100644
--- a/src/view/com/posts/Feed.tsx
+++ b/src/view/com/posts/Feed.tsx
@@ -32,6 +32,7 @@ import {msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 import {DiscoverFallbackHeader} from './DiscoverFallbackHeader'
 import {FALLBACK_MARKER_POST} from '#/lib/api/feed/home'
+import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender'
 
 const LOADING_ITEM = {_reactKey: '__loading__'}
 const EMPTY_FEED_ITEM = {_reactKey: '__empty__'}
@@ -84,6 +85,7 @@ let Feed = ({
   const {_} = useLingui()
   const queryClient = useQueryClient()
   const {currentAccount} = useSession()
+  const initialNumToRender = useInitialNumToRender()
   const [isPTRing, setIsPTRing] = React.useState(false)
   const checkForNewRef = React.useRef<(() => void) | null>(null)
   const lastFetchRef = React.useRef<number>(Date.now())
@@ -327,6 +329,8 @@ let Feed = ({
         desktopFixedHeight={
           desktopFixedHeightOffset ? desktopFixedHeightOffset : true
         }
+        initialNumToRender={initialNumToRender}
+        windowSize={11}
       />
     </View>
   )
diff --git a/src/view/com/util/Views.d.ts b/src/view/com/util/Views.d.ts
index 6a90cc229..16713921f 100644
--- a/src/view/com/util/Views.d.ts
+++ b/src/view/com/util/Views.d.ts
@@ -5,4 +5,6 @@ export function CenteredView({
   style,
   sideBorders,
   ...props
-}: React.PropsWithChildren<ViewProps & {sideBorders?: boolean}>)
+}: React.PropsWithChildren<
+  ViewProps & {sideBorders?: boolean; topBorder?: boolean}
+>)
diff --git a/src/view/com/util/Views.web.tsx b/src/view/com/util/Views.web.tsx
index db3b9de0d..ae165077c 100644
--- a/src/view/com/util/Views.web.tsx
+++ b/src/view/com/util/Views.web.tsx
@@ -32,8 +32,11 @@ interface AddedProps {
 export function CenteredView({
   style,
   sideBorders,
+  topBorder,
   ...props
-}: React.PropsWithChildren<ViewProps & {sideBorders?: boolean}>) {
+}: React.PropsWithChildren<
+  ViewProps & {sideBorders?: boolean; topBorder?: boolean}
+>) {
   const pal = usePalette('default')
   const {isMobile} = useWebMediaQueries()
   if (!isMobile) {
@@ -46,6 +49,12 @@ export function CenteredView({
     })
     style = addStyle(style, pal.border)
   }
+  if (topBorder) {
+    style = addStyle(style, {
+      borderTopWidth: 1,
+    })
+    style = addStyle(style, pal.border)
+  }
   return <View style={style} {...props} />
 }
 
diff --git a/src/view/com/util/forms/DateInput.tsx b/src/view/com/util/forms/DateInput.tsx
index c5f0afc8f..0104562aa 100644
--- a/src/view/com/util/forms/DateInput.tsx
+++ b/src/view/com/util/forms/DateInput.tsx
@@ -1,8 +1,5 @@
 import React, {useState, useCallback} from 'react'
 import {StyleProp, StyleSheet, TextStyle, View, ViewStyle} from 'react-native'
-import DateTimePicker, {
-  DateTimePickerEvent,
-} from '@react-native-community/datetimepicker'
 import {
   FontAwesomeIcon,
   FontAwesomeIconStyle,
@@ -14,6 +11,7 @@ import {TypographyVariant} from 'lib/ThemeContext'
 import {useTheme} from 'lib/ThemeContext'
 import {usePalette} from 'lib/hooks/usePalette'
 import {getLocales} from 'expo-localization'
+import DatePicker from 'react-native-date-picker'
 
 const LOCALE = getLocales()[0]
 
@@ -43,11 +41,9 @@ export function DateInput(props: Props) {
   }, [props.handleAsUTC])
 
   const onChangeInternal = useCallback(
-    (event: DateTimePickerEvent, date: Date | undefined) => {
+    (date: Date) => {
       setShow(false)
-      if (date) {
-        props.onChange(date)
-      }
+      props.onChange(date)
     },
     [setShow, props],
   )
@@ -56,6 +52,10 @@ export function DateInput(props: Props) {
     setShow(true)
   }, [setShow])
 
+  const onCancel = useCallback(() => {
+    setShow(false)
+  }, [])
+
   return (
     <View>
       {isAndroid && (
@@ -80,15 +80,17 @@ export function DateInput(props: Props) {
         </Button>
       )}
       {(isIOS || show) && (
-        <DateTimePicker
-          testID={props.testID ? `${props.testID}-datepicker` : undefined}
+        <DatePicker
+          timeZoneOffsetInMinutes={0}
+          modal={isAndroid}
+          open={isAndroid}
+          theme={theme.colorScheme}
+          date={props.value}
+          onDateChange={onChangeInternal}
+          onConfirm={onChangeInternal}
+          onCancel={onCancel}
           mode="date"
-          timeZoneName={props.handleAsUTC ? 'Etc/UTC' : undefined}
-          display="spinner"
-          // @ts-ignore applies in iOS only -prf
-          themeVariant={theme.colorScheme}
-          value={props.value}
-          onChange={onChangeInternal}
+          testID={props.testID ? `${props.testID}-datepicker` : undefined}
           accessibilityLabel={props.accessibilityLabel}
           accessibilityHint={props.accessibilityHint}
           accessibilityLabelledBy={props.accessibilityLabelledBy}
diff --git a/src/view/shell/desktop/LeftNav.tsx b/src/view/shell/desktop/LeftNav.tsx
index def0333c7..c56ba941e 100644
--- a/src/view/shell/desktop/LeftNav.tsx
+++ b/src/view/shell/desktop/LeftNav.tsx
@@ -391,7 +391,7 @@ export function DesktopLeftNav() {
               <FontAwesomeIcon
                 icon="hand"
                 style={pal.text as FontAwesomeIconStyle}
-                size={isDesktop ? 20 : 26}
+                size={isDesktop ? 23 : 26}
               />
             }
             label={_(msg`Moderation`)}