diff --git a/assets/icons/floppyDisk_stroke2_corner0_rounded.svg b/assets/icons/floppyDisk_stroke2_corner0_rounded.svg
new file mode 100644
index 0000000000..b9a42f7594
--- /dev/null
+++ b/assets/icons/floppyDisk_stroke2_corner0_rounded.svg
@@ -0,0 +1 @@
+
diff --git a/bskyweb/templates/base.html b/bskyweb/templates/base.html
index 4c02805e3b..8eb78fffd4 100644
--- a/bskyweb/templates/base.html
+++ b/bskyweb/templates/base.html
@@ -40,7 +40,6 @@
}
html {
background-color: white;
- scrollbar-gutter: stable both-edges;
}
@media (prefers-color-scheme: dark) {
html {
@@ -76,9 +75,15 @@
top: 50%;
transform: translateX(-50%) translateY(-50%) translateY(-50px);
}
- /* We need this style to prevent web dropdowns from shifting the display when opening */
+ /**
+ * We need these styles to prevent shifting due to scrollbar show/hide on
+ * OSs that have them enabled by default. This also handles cases where the
+ * screen wouldn't otherwise scroll, and therefore hide the scrollbar and
+ * shift the content, by forcing the page to show a scrollbar.
+ */
body {
width: 100%;
+ overflow-y: scroll;
}
diff --git a/package.json b/package.json
index 6372dd5f5b..6c35d169ed 100644
--- a/package.json
+++ b/package.json
@@ -193,6 +193,7 @@
"react-native-web": "~0.19.11",
"react-native-web-webview": "^1.0.2",
"react-native-webview": "13.10.2",
+ "react-remove-scroll-bar": "^2.3.6",
"react-responsive": "^9.0.2",
"react-textarea-autosize": "^8.5.3",
"rn-fetch-blob": "^0.12.0",
diff --git a/src/alf/atoms.ts b/src/alf/atoms.ts
index 1f08eb7e1b..0870c57679 100644
--- a/src/alf/atoms.ts
+++ b/src/alf/atoms.ts
@@ -1,7 +1,8 @@
import {Platform, StyleProp, StyleSheet, ViewStyle} from 'react-native'
import * as tokens from '#/alf/tokens'
-import {ios, native, web} from '#/alf/util/platform'
+import {ios, native, platform, web} from '#/alf/util/platform'
+import * as Layout from '#/components/Layout'
export const atoms = {
debug: {
@@ -21,6 +22,9 @@ export const atoms = {
relative: {
position: 'relative',
},
+ sticky: web({
+ position: 'sticky',
+ }),
inset_0: {
top: 0,
left: 0,
@@ -941,4 +945,20 @@ export const atoms = {
transitionTimingFunction: 'cubic-bezier(0.17, 0.73, 0.14, 1)',
transitionDuration: '100ms',
}),
+
+ /**
+ * {@link Layout.SCROLLBAR_OFFSET}
+ */
+ scrollbar_offset: platform({
+ web: {
+ transform: [
+ {
+ translateX: Layout.SCROLLBAR_OFFSET,
+ },
+ ],
+ },
+ native: {
+ transform: [],
+ },
+ }) as {transform: Exclude},
} as const
diff --git a/src/alf/index.tsx b/src/alf/index.tsx
index 5d08722ff4..a96803c561 100644
--- a/src/alf/index.tsx
+++ b/src/alf/index.tsx
@@ -20,6 +20,7 @@ export * from '#/alf/types'
export * from '#/alf/util/flatten'
export * from '#/alf/util/platform'
export * from '#/alf/util/themeSelector'
+export * from '#/alf/util/useGutterStyles'
export type Alf = {
themeName: ThemeName
diff --git a/src/alf/util/useGutterStyles.ts b/src/alf/util/useGutterStyles.ts
new file mode 100644
index 0000000000..64b246fdd2
--- /dev/null
+++ b/src/alf/util/useGutterStyles.ts
@@ -0,0 +1,21 @@
+import React from 'react'
+
+import {atoms as a, useBreakpoints, ViewStyleProp} from '#/alf'
+
+export function useGutterStyles({
+ top,
+ bottom,
+}: {
+ top?: boolean
+ bottom?: boolean
+} = {}) {
+ const {gtMobile} = useBreakpoints()
+ return React.useMemo(() => {
+ return [
+ a.px_lg,
+ top && a.pt_md,
+ bottom && a.pb_md,
+ gtMobile && [a.px_xl, top && a.pt_lg, bottom && a.pb_lg],
+ ]
+ }, [gtMobile, top, bottom])
+}
diff --git a/src/components/Dialog/index.web.tsx b/src/components/Dialog/index.web.tsx
index 6b92eee3e0..e45133dc5a 100644
--- a/src/components/Dialog/index.web.tsx
+++ b/src/components/Dialog/index.web.tsx
@@ -12,6 +12,7 @@ import {useLingui} from '@lingui/react'
import {DismissableLayer} from '@radix-ui/react-dismissable-layer'
import {useFocusGuards} from '@radix-ui/react-focus-guards'
import {FocusScope} from '@radix-ui/react-focus-scope'
+import {RemoveScrollBar} from 'react-remove-scroll-bar'
import {logger} from '#/logger'
import {useDialogStateControlContext} from '#/state/dialogs'
@@ -103,6 +104,7 @@ export function Outer({
{isOpen && (
+ & {
- disableTopPadding?: boolean
- style?: StyleProp
-}): React.ReactNode => {
- const {top} = useSafeAreaInsets()
- const context = useMemo(
- () => ({
- withinScreen: true,
- topPaddingDisabled: disableTopPadding,
- withinScrollView: false,
- }),
- [disableTopPadding],
- )
- return (
-
-
-
- )
-}
-Screen = React.memo(Screen)
-export {Screen}
-
-let Header = (
- props: React.ComponentProps,
-): React.ReactNode => {
- const {withinScrollView} = useContext(LayoutContext)
- if (!withinScrollView) {
- return (
-
-
-
- )
- } else {
- return
- }
-}
-Header = React.memo(Header)
-export {Header}
-
-let Content = ({
- style,
- contentContainerStyle,
- ...props
-}: React.ComponentProps & {
- style?: StyleProp
- contentContainerStyle?: StyleProp
-}): React.ReactNode => {
- const context = useContext(LayoutContext)
- const newContext = useMemo(
- () => ({...context, withinScrollView: true}),
- [context],
- )
- return (
-
-
-
- )
-}
-Content = React.memo(Content)
-export {Content}
diff --git a/src/components/Layout/Header/index.tsx b/src/components/Layout/Header/index.tsx
new file mode 100644
index 0000000000..a35a095371
--- /dev/null
+++ b/src/components/Layout/Header/index.tsx
@@ -0,0 +1,199 @@
+import {createContext, useCallback, useContext} from 'react'
+import {GestureResponderEvent, View} from 'react-native'
+import {msg} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+import {useNavigation} from '@react-navigation/native'
+
+import {HITSLOP_30} from '#/lib/constants'
+import {NavigationProp} from '#/lib/routes/types'
+import {isIOS} from '#/platform/detection'
+import {useSetDrawerOpen} from '#/state/shell'
+import {
+ atoms as a,
+ platform,
+ TextStyleProp,
+ useBreakpoints,
+ useGutterStyles,
+ useTheme,
+} from '#/alf'
+import {Button, ButtonIcon, ButtonProps} from '#/components/Button'
+import {ArrowLeft_Stroke2_Corner0_Rounded as ArrowLeft} from '#/components/icons/Arrow'
+import {Menu_Stroke2_Corner0_Rounded as Menu} from '#/components/icons/Menu'
+import {
+ BUTTON_VISUAL_ALIGNMENT_OFFSET,
+ HEADER_SLOT_SIZE,
+} from '#/components/Layout/const'
+import {ScrollbarOffsetContext} from '#/components/Layout/context'
+import {Text} from '#/components/Typography'
+
+export function Outer({
+ children,
+ noBottomBorder,
+}: {
+ children: React.ReactNode
+ noBottomBorder?: boolean
+}) {
+ const t = useTheme()
+ const gutter = useGutterStyles()
+ const {gtMobile} = useBreakpoints()
+ const {isWithinOffsetView} = useContext(ScrollbarOffsetContext)
+
+ return (
+
+ {children}
+
+ )
+}
+
+const AlignmentContext = createContext<'platform' | 'left'>('platform')
+
+export function Content({
+ children,
+ align = 'platform',
+}: {
+ children?: React.ReactNode
+ align?: 'platform' | 'left'
+}) {
+ return (
+
+
+ {children}
+
+
+ )
+}
+
+export function Slot({children}: {children?: React.ReactNode}) {
+ return (
+
+ {children}
+
+ )
+}
+
+export function BackButton({onPress, style, ...props}: Partial) {
+ const {_} = useLingui()
+ const navigation = useNavigation()
+
+ const onPressBack = useCallback(
+ (evt: GestureResponderEvent) => {
+ onPress?.(evt)
+ if (evt.defaultPrevented) return
+ if (navigation.canGoBack()) {
+ navigation.goBack()
+ } else {
+ navigation.navigate('Home')
+ }
+ },
+ [onPress, navigation],
+ )
+
+ return (
+
+
+
+ )
+}
+
+export function MenuButton() {
+ const {_} = useLingui()
+ const setDrawerOpen = useSetDrawerOpen()
+ const {gtMobile} = useBreakpoints()
+
+ const onPress = useCallback(() => {
+ setDrawerOpen(true)
+ }, [setDrawerOpen])
+
+ return gtMobile ? null : (
+
+
+
+ )
+}
+
+export function TitleText({
+ children,
+ style,
+}: {children: React.ReactNode} & TextStyleProp) {
+ const {gtMobile} = useBreakpoints()
+ const align = useContext(AlignmentContext)
+ return (
+
+ {children}
+
+ )
+}
+
+export function SubtitleText({children}: {children: React.ReactNode}) {
+ const t = useTheme()
+ const align = useContext(AlignmentContext)
+ return (
+
+ {children}
+
+ )
+}
diff --git a/src/components/Layout/README.md b/src/components/Layout/README.md
new file mode 100644
index 0000000000..1bcc3489ec
--- /dev/null
+++ b/src/components/Layout/README.md
@@ -0,0 +1,172 @@
+# Layout
+
+This directory contains our core layout components. Use these when creating new
+screens, or when supplementing other components with functionality like
+centering.
+
+## Usage
+
+If we aren't talking about the `shell` components, layouts on individual screens
+look like more or less like this:
+
+```tsx
+
+ ...
+ ...
+
+```
+
+I'll map these words to real components.
+
+### `Layout.Screen`
+
+Provides the "Outer" functionality for a screen, like taking up the full height
+of the screen. **All screens should be wrapped with this component,** probably
+as the outermost component.
+
+> [!NOTE]
+> On web, `Layout.Screen` also provides the side borders on our central content
+> column. These borders are fixed position, 1px outside our center column width
+> of 600px.
+>
+> What this effectively means is that _nothing inside the center content column
+> needs (or should) define left/right borders._ That is now handled in one
+> place: within `Layout.Screen`.
+
+### `Layout.Header.*`
+
+The `Layout.Header` component actually contains multiple sub-components. Use
+this to compose different versions of the header. The most basic version looks
+like this:
+
+```tsx
+
+ {/* or */}
+
+
+ Account
+
+ {/* Optional subtitle */}
+ Settings for @esb.lol
+
+
+
+
+```
+
+Note the additional `Slot` component. This is here to keep the header balanced
+and provide correct spacing on all platforms. The `Slot` is 34px wide, which
+matches the `BackButton` and `MenuButton`.
+
+> If anyone has better ideas, I'm all ears, but this was simple and the small
+> amount of boilerplate is only incurred when creating a new screen, which is
+> infrequent.
+
+It can also function as a "slot" for a button positioned on the right side. See
+the `Hashtag` screen for an example, abbreviated below:
+
+```tsx
+
+
+
+```
+
+If you need additional customization, simply use the components that are helpful
+and create new ones as needed. A good example is the `SavedFeeds` screen, which
+looks roughly like this:
+
+```tsx
+
+
+
+ {/* Override to align content to the left, making room for the button */}
+
+ Edit My Feeds
+
+
+ {/* Custom button, wider than 34px */}
+
+
+```
+
+> [!TIP]
+> The `Header` should be _outside_ the `Content` component in order to be
+> fixed on scroll on native. Placing it inside will make it scroll with the rest
+> of the page.
+
+### `Layout.Content`
+
+This provides the "Content" functionality for a screen. This component is
+actually an `Animated.ScrollView`, and accepts props for that component. It
+provides a little default styling as well. On web, it also _centers the content
+inside our center content column of 600px_.
+
+> [!NOTE]
+> What about flatlists or pagers? Those components are not colocated here (yet).
+> But those components serve the same purpose of "Content".
+
+## Examples
+
+The most basic layout available to us looks like this:
+
+```tsx
+
+
+ {/* or */}
+
+
+ Account
+
+ {/* Optional subtitle */}
+ Settings for @esb.lol
+
+
+
+
+
+
+ ...
+
+
+```
+
+**For `List` views,** you'd sub in `List` for `Layout.Content` and it will
+function the same. See `Feeds` screen for an example.
+
+**For `Pager` views,** including `PagerWithHeader`, do the same. See `Hashtag`
+screen for an example.
+
+## Utilities
+
+### `Layout.Center`
+
+This component behaves like our old `CenteredView` component.
+
+### `Layout.SCROLLBAR_OFFSET` and `Layout.SCROLLBAR_OFFSET_POSITIVE`
+
+Provide a pre-configured CSS vars for use when aligning fixed position elements.
+More on this below.
+
+## Scrollbar gutter handling
+
+Operating systems allow users to configure if their browser _always_ shows
+scrollbars not. Some OSs also don't allow configuration.
+
+The presence of scrollbars affects layout, particularly fixed position elements.
+Browsers support `scrollbar-gutter`, but each behaves differently. Our approach
+is to use the default `scrollbar-gutter: auto`. Basically, we start from a clean
+slate.
+
+This handling becomes particularly thorny when we need to lock scroll, like when
+opening a dialog or dropdown. Radix uses the library `react-remove-scroll`
+internally, which in turn depends on
+[`react-remove-scroll-bar`](https://github.com/theKashey/react-remove-scroll-bar).
+We've opted to rely on this transient dependency. This library adds some utility
+classes and CSS vars to the page when scroll is locked.
+
+**It is this CSS variable that we use in `SCROLLBAR_OFFSET` values.** This
+ensures that elements do not shift relative to the screen when opening a
+dropdown or dialog.
+
+These styles are applied where needed and we should have very little need of
+adjusting them often.
diff --git a/src/components/Layout/const.ts b/src/components/Layout/const.ts
new file mode 100644
index 0000000000..11825d323c
--- /dev/null
+++ b/src/components/Layout/const.ts
@@ -0,0 +1,16 @@
+export const SCROLLBAR_OFFSET =
+ 'calc(-1 * var(--removed-body-scroll-bar-size, 0px) / 2)' as any
+export const SCROLLBAR_OFFSET_POSITIVE =
+ 'calc(var(--removed-body-scroll-bar-size, 0px) / 2)' as any
+
+/**
+ * Useful for visually aligning icons within header buttons with the elements
+ * below them on the screen. Apply positively or negatively depending on side
+ * of the screen you're on.
+ */
+export const BUTTON_VISUAL_ALIGNMENT_OFFSET = 3
+
+/**
+ * Corresponds to the width of a small square or round button
+ */
+export const HEADER_SLOT_SIZE = 34
diff --git a/src/components/Layout/context.ts b/src/components/Layout/context.ts
new file mode 100644
index 0000000000..8e0c5445e8
--- /dev/null
+++ b/src/components/Layout/context.ts
@@ -0,0 +1,5 @@
+import React from 'react'
+
+export const ScrollbarOffsetContext = React.createContext({
+ isWithinOffsetView: false,
+})
diff --git a/src/components/Layout/index.tsx b/src/components/Layout/index.tsx
new file mode 100644
index 0000000000..d08505fbfd
--- /dev/null
+++ b/src/components/Layout/index.tsx
@@ -0,0 +1,188 @@
+import React, {useContext, useMemo} from 'react'
+import {StyleSheet, View, ViewProps, ViewStyle} from 'react-native'
+import {StyleProp} from 'react-native'
+import {
+ KeyboardAwareScrollView,
+ KeyboardAwareScrollViewProps,
+} from 'react-native-keyboard-controller'
+import Animated, {
+ AnimatedScrollViewProps,
+ useAnimatedProps,
+} from 'react-native-reanimated'
+import {useSafeAreaInsets} from 'react-native-safe-area-context'
+
+import {isWeb} from '#/platform/detection'
+import {useShellLayout} from '#/state/shell/shell-layout'
+import {atoms as a, useBreakpoints, useTheme, web} from '#/alf'
+import {ScrollbarOffsetContext} from '#/components/Layout/context'
+
+export * from '#/components/Layout/const'
+export * as Header from '#/components/Layout/Header'
+
+export type ScreenProps = React.ComponentProps & {
+ style?: StyleProp
+}
+
+/**
+ * Outermost component of every screen
+ */
+export const Screen = React.memo(function Screen({
+ style,
+ ...props
+}: ScreenProps) {
+ const {top} = useSafeAreaInsets()
+ return (
+ <>
+ {isWeb && }
+
+ >
+ )
+})
+
+export type ContentProps = AnimatedScrollViewProps & {
+ style?: StyleProp
+ contentContainerStyle?: StyleProp
+}
+
+/**
+ * Default scroll view for simple pages
+ */
+export const Content = React.memo(function Content({
+ children,
+ style,
+ contentContainerStyle,
+ ...props
+}: ContentProps) {
+ const {footerHeight} = useShellLayout()
+ const animatedProps = useAnimatedProps(() => {
+ return {
+ scrollIndicatorInsets: {
+ bottom: footerHeight.get(),
+ top: 0,
+ right: 1,
+ },
+ } satisfies AnimatedScrollViewProps
+ })
+
+ return (
+
+ {isWeb ? (
+ // @ts-ignore web only -esb
+
{children}
+ ) : (
+ children
+ )}
+
+ )
+})
+
+const scrollViewStyles = StyleSheet.create({
+ common: {
+ width: '100%',
+ },
+ contentContainer: {
+ paddingBottom: 100,
+ },
+})
+
+export type KeyboardAwareContentProps = KeyboardAwareScrollViewProps & {
+ children: React.ReactNode
+ contentContainerStyle?: StyleProp
+}
+
+/**
+ * Default scroll view for simple pages.
+ *
+ * BE SURE TO TEST THIS WHEN USING, it's untested as of writing this comment.
+ */
+export const KeyboardAwareContent = React.memo(function LayoutScrollView({
+ children,
+ style,
+ contentContainerStyle,
+ ...props
+}: KeyboardAwareContentProps) {
+ return (
+
+ {isWeb ?
{children}
: children}
+
+ )
+})
+
+/**
+ * Utility component to center content within the screen
+ */
+export const Center = React.memo(function LayoutContent({
+ children,
+ style,
+ ...props
+}: ViewProps) {
+ const {isWithinOffsetView} = useContext(ScrollbarOffsetContext)
+ const {gtMobile} = useBreakpoints()
+ const ctx = useMemo(() => ({isWithinOffsetView: true}), [])
+ return (
+
+
+ {children}
+
+
+ )
+})
+
+/**
+ * Only used within `Layout.Screen`, not for reuse
+ */
+const WebCenterBorders = React.memo(function LayoutContent() {
+ const t = useTheme()
+ const {gtMobile} = useBreakpoints()
+ return gtMobile ? (
+
+ ) : null
+})
diff --git a/src/components/LikedByList.tsx b/src/components/LikedByList.tsx
index a83f982589..b369bd76e6 100644
--- a/src/components/LikedByList.tsx
+++ b/src/components/LikedByList.tsx
@@ -12,8 +12,14 @@ import {ProfileCardWithFollowBtn} from '#/view/com/profile/ProfileCard'
import {List} from '#/view/com/util/List'
import {ListFooter, ListMaybePlaceholder} from '#/components/Lists'
-function renderItem({item}: {item: GetLikes.Like}) {
- return
+function renderItem({item, index}: {item: GetLikes.Like; index: number}) {
+ return (
+
+ )
}
function keyExtractor(item: GetLikes.Like) {
@@ -81,6 +87,8 @@ export function LikedByList({uri}: {uri: string}) {
)}
errorMessage={cleanError(resolveError || error)}
onRetry={isError ? refetch : undefined}
+ topBorder={false}
+ sideBorders={false}
/>
)
}
@@ -103,6 +111,7 @@ export function LikedByList({uri}: {uri: string}) {
onEndReachedThreshold={3}
initialNumToRender={initialNumToRender}
windowSize={11}
+ sideBorders={false}
/>
)
}
diff --git a/src/components/Lists.tsx b/src/components/Lists.tsx
index 16bd6a9eab..2d7b13b25c 100644
--- a/src/components/Lists.tsx
+++ b/src/components/Lists.tsx
@@ -109,38 +109,6 @@ function ListFooterMaybeError({
)
}
-export function ListHeaderDesktop({
- title,
- subtitle,
-}: {
- title: string
- subtitle?: string
-}) {
- const {gtTablet} = useBreakpoints()
- const t = useTheme()
-
- if (!gtTablet) return null
-
- return (
-
- {title}
- {subtitle ? (
-
- {subtitle}
-
- ) : undefined}
-
- )
-}
-
let ListMaybePlaceholder = ({
isLoading,
noEmpty,
@@ -154,7 +122,7 @@ let ListMaybePlaceholder = ({
onGoBack,
hideBackButton,
sideBorders,
- topBorder = true,
+ topBorder = false,
}: {
isLoading: boolean
noEmpty?: boolean
diff --git a/src/components/dms/MessagesListHeader.tsx b/src/components/dms/MessagesListHeader.tsx
index 6c3bbf2161..acffa0c2ba 100644
--- a/src/components/dms/MessagesListHeader.tsx
+++ b/src/components/dms/MessagesListHeader.tsx
@@ -65,25 +65,23 @@ export let MessagesListHeader = ({
a.pr_lg,
a.py_sm,
]}>
- {!gtTablet && (
-
-
-
- )}
+
+
+
{profile && moderation && blockInfo ? (
{
- if (!isWeb || !isLockActive) {
- return
- }
- incrementRefCount()
- return () => decrementRefCount()
- })
-}
diff --git a/src/screens/Deactivated.tsx b/src/screens/Deactivated.tsx
index 36b96cacd7..4fcb42854a 100644
--- a/src/screens/Deactivated.tsx
+++ b/src/screens/Deactivated.tsx
@@ -17,13 +17,13 @@ import {
} from '#/state/session'
import {useSetMinimalShellMode} from '#/state/shell'
import {useLoggedOutViewControls} from '#/state/shell/logged-out'
-import {ScrollView} from '#/view/com/util/Views'
import {Logo} from '#/view/icons/Logo'
import {atoms as a, useTheme} from '#/alf'
import {AccountList} from '#/components/AccountList'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import {Divider} from '#/components/Divider'
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
+import * as Layout from '#/components/Layout'
import {Loader} from '#/components/Loader'
import {Text} from '#/components/Typography'
@@ -104,24 +104,17 @@ export function Deactivated() {
}, [_, agent, setPending, setError, queryClient])
return (
-
-
+
-
+
@@ -218,7 +211,7 @@ export function Deactivated() {
>
)}
-
+
)
}
diff --git a/src/screens/Hashtag.tsx b/src/screens/Hashtag.tsx
index adf5f00801..a0fc3707cb 100644
--- a/src/screens/Hashtag.tsx
+++ b/src/screens/Hashtag.tsx
@@ -1,5 +1,5 @@
import React from 'react'
-import {ListRenderItemInfo, Pressable, View} from 'react-native'
+import {ListRenderItemInfo, View} from 'react-native'
import {PostView} from '@atproto/api/dist/client/types/app/bsky/feed/defs'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
@@ -13,16 +13,15 @@ import {shareUrl} from '#/lib/sharing'
import {cleanError} from '#/lib/strings/errors'
import {sanitizeHandle} from '#/lib/strings/handles'
import {enforceLen} from '#/lib/strings/helpers'
-import {isNative, isWeb} from '#/platform/detection'
import {useSearchPostsQuery} from '#/state/queries/search-posts'
import {useSetDrawerSwipeDisabled, useSetMinimalShellMode} from '#/state/shell'
import {Pager} from '#/view/com/pager/Pager'
import {TabBar} from '#/view/com/pager/TabBar'
import {Post} from '#/view/com/post/Post'
import {List} from '#/view/com/util/List'
-import {ViewHeader} from '#/view/com/util/ViewHeader'
-import {CenteredView} from '#/view/com/util/Views'
-import {ArrowOutOfBox_Stroke2_Corner0_Rounded} from '#/components/icons/ArrowOutOfBox'
+import {atoms as a, web} from '#/alf'
+import {Button, ButtonIcon} from '#/components/Button'
+import {ArrowOutOfBox_Stroke2_Corner0_Rounded as Share} from '#/components/icons/ArrowOutOfBox'
import * as Layout from '#/components/Layout'
import {ListFooter, ListMaybePlaceholder} from '#/components/Lists'
@@ -110,46 +109,36 @@ export default function HashtagScreen({
return (
-
- (
-
-
-
- )
- : undefined
- }
- />
-
+
+
+
+ {headerTitle}
+ {author && (
+
+ {_(msg`From @${sanitizedAuthor}`)}
+
+ )}
+
+
+
+
+ (
-
+ section.title)} {...props} />
-
+
)}
initialPage={0}>
{sections.map((section, i) => (
diff --git a/src/screens/Messages/ChatList.tsx b/src/screens/Messages/ChatList.tsx
index 4f2bd251f6..1a87a2ac59 100644
--- a/src/screens/Messages/ChatList.tsx
+++ b/src/screens/Messages/ChatList.tsx
@@ -16,8 +16,6 @@ import {MESSAGE_SCREEN_POLL_INTERVAL} from '#/state/messages/convo/const'
import {useMessagesEventBus} from '#/state/messages/events'
import {useListConvosQuery} from '#/state/queries/messages/list-converations'
import {List} from '#/view/com/util/List'
-import {ViewHeader} from '#/view/com/util/ViewHeader'
-import {CenteredView} from '#/view/com/util/Views'
import {atoms as a, useBreakpoints, useTheme, web} from '#/alf'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import {DialogControlProps, useDialogControl} from '#/components/Dialog'
@@ -49,7 +47,6 @@ export function MessagesScreen({navigation, route}: Props) {
const {_} = useLingui()
const t = useTheme()
const newChatControl = useDialogControl()
- const {gtMobile} = useBreakpoints()
const pushToConversation = route.params?.pushToConversation
// Whenever we have `pushToConversation` set, it means we pressed a notification for a chat without being on
@@ -81,21 +78,6 @@ export function MessagesScreen({navigation, route}: Props) {
}, [messagesBus, isActive]),
)
- const renderButton = useCallback(() => {
- return (
-
-
-
- )
- }, [_, t])
-
const initialNumToRender = useInitialNumToRender({minItemHeight: 80})
const [isPTRing, setIsPTRing] = useState(false)
@@ -144,28 +126,11 @@ export function MessagesScreen({navigation, route}: Props) {
[navigation],
)
- const onNavigateToSettings = useCallback(() => {
- navigation.navigate('MessagesSettings')
- }, [navigation])
-
if (conversations.length < 1) {
return (
-
- {gtMobile ? (
-
- ) : (
-
- )}
-
+
+
{isLoading ? (
@@ -227,7 +192,7 @@ export function MessagesScreen({navigation, route}: Props) {
)}
>
)}
-
+
{!isLoading && !isError && (
@@ -238,14 +203,7 @@ export function MessagesScreen({navigation, route}: Props) {
return (
- {!gtMobile && (
-
- )}
+
- }
ListFooterComponent={
)
}
-function DesktopHeader({
- newChatControl,
- onNavigateToSettings,
-}: {
- newChatControl: DialogControlProps
- onNavigateToSettings: () => void
-}) {
- const t = useTheme()
+function Header({newChatControl}: {newChatControl: DialogControlProps}) {
const {_} = useLingui()
- const {gtMobile, gtTablet} = useBreakpoints()
+ const {gtMobile} = useBreakpoints()
- if (!gtMobile) {
- return null
- }
+ const settingsLink = (
+
+
+
+ )
return (
-
-
- Messages
-
-
-
- {gtTablet && (
-
- )}
-
-
+
+ {gtMobile ? (
+ <>
+
+
+ Messages
+
+
+
+
+ {settingsLink}
+
+
+ >
+ ) : (
+ <>
+
+
+
+ Messages
+
+
+ {settingsLink}
+ >
+ )}
+
)
}
diff --git a/src/screens/Messages/Conversation.tsx b/src/screens/Messages/Conversation.tsx
index a2157d2b9c..b8b0bfe0d3 100644
--- a/src/screens/Messages/Conversation.tsx
+++ b/src/screens/Messages/Conversation.tsx
@@ -17,7 +17,6 @@ import {useCurrentConvoId} from '#/state/messages/current-convo-id'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {useProfileQuery} from '#/state/queries/profile'
import {useSetMinimalShellMode} from '#/state/shell'
-import {CenteredView} from '#/view/com/util/Views'
import {MessagesList} from '#/screens/Messages/components/MessagesList'
import {atoms as a, useBreakpoints, useTheme, web} from '#/alf'
import {useDialogControl} from '#/components/Dialog'
@@ -97,7 +96,7 @@ function Inner() {
if (convoState.status === ConvoStatus.Error) {
return (
-
+ convoState.error.retry()}
sideBorders={false}
/>
-
+
)
}
return (
-
+
{!readyToShow && }
{moderationOpts && recipient ? (
@@ -140,7 +139,7 @@ function Inner() {
)}
-
+
)
}
diff --git a/src/screens/Messages/Settings.tsx b/src/screens/Messages/Settings.tsx
index 50b1c4cc98..f37e7a9ba1 100644
--- a/src/screens/Messages/Settings.tsx
+++ b/src/screens/Messages/Settings.tsx
@@ -10,8 +10,6 @@ import {useUpdateActorDeclaration} from '#/state/queries/messages/actor-declarat
import {useProfileQuery} from '#/state/queries/profile'
import {useSession} from '#/state/session'
import * as Toast from '#/view/com/util/Toast'
-import {ViewHeader} from '#/view/com/util/ViewHeader'
-import {ScrollView} from '#/view/com/util/Views'
import {atoms as a} from '#/alf'
import {Admonition} from '#/components/Admonition'
import {Divider} from '#/components/Divider'
@@ -57,8 +55,16 @@ export function MessagesSettingsScreen({}: Props) {
return (
-
-
+
+
+
+
+ Chat Settings
+
+
+
+
+ Allow new messages from
@@ -142,7 +148,7 @@ export function MessagesSettingsScreen({}: Props) {
>
)}
-
+
)
}
diff --git a/src/screens/Moderation/index.tsx b/src/screens/Moderation/index.tsx
index 5f340cd560..6b4dd06bcc 100644
--- a/src/screens/Moderation/index.tsx
+++ b/src/screens/Moderation/index.tsx
@@ -1,6 +1,5 @@
-import React from 'react'
+import {Fragment, useCallback} from 'react'
import {Linking, View} from 'react-native'
-import {useSafeAreaFrame} from 'react-native-safe-area-context'
import {LABELS} from '@atproto/api'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
@@ -19,8 +18,6 @@ import {
import {isNonConfigurableModerationAuthority} from '#/state/session/additional-moderation-authorities'
import {useSetMinimalShellMode} from '#/state/shell'
import {ViewHeader} from '#/view/com/util/ViewHeader'
-import {CenteredView} from '#/view/com/util/Views'
-import {ScrollView} from '#/view/com/util/Views'
import {atoms as a, useBreakpoints, useTheme, ViewStyleProp} from '#/alf'
import {Button, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog'
@@ -37,6 +34,7 @@ import {Person_Stroke2_Corner0_Rounded as Person} from '#/components/icons/Perso
import * as LabelingService from '#/components/LabelingServiceCard'
import * as Layout from '#/components/Layout'
import {InlineLinkText, Link} from '#/components/Link'
+import {ListMaybePlaceholder} from '#/components/Lists'
import {Loader} from '#/components/Loader'
import {GlobalLabelPreference} from '#/components/moderation/LabelPreference'
import {Text} from '#/components/Typography'
@@ -75,35 +73,22 @@ function ErrorState({error}: {error: string}) {
export function ModerationScreen(
_props: NativeStackScreenProps,
) {
- const t = useTheme()
const {_} = useLingui()
const {
isLoading: isPreferencesLoading,
error: preferencesError,
data: preferences,
} = usePreferencesQuery()
- const {gtMobile} = useBreakpoints()
- const {height} = useSafeAreaFrame()
const isLoading = isPreferencesLoading
const error = preferencesError
return (
-
-
-
+
+
{isLoading ? (
-
-
-
+
) : error || !preferences ? (
)}
-
+
)
}
@@ -169,7 +154,7 @@ export function ModerationScreenInner({
} = useMyLabelersQuery()
useFocusEffect(
- React.useCallback(() => {
+ useCallback(() => {
setMinimalShellMode(false)
}, [setMinimalShellMode]),
)
@@ -183,7 +168,7 @@ export function ModerationScreenInner({
const ageNotSet = !preferences.userAge
const isUnderage = (preferences.userAge || 0) < 18
- const onToggleAdultContentEnabled = React.useCallback(
+ const onToggleAdultContentEnabled = useCallback(
async (selected: boolean) => {
try {
await setAdultContentPref({
@@ -201,13 +186,7 @@ export function ModerationScreenInner({
const disabledOnIOS = isIOS && !adultContentEnabled
return (
-
+ Moderation tools
@@ -420,7 +399,7 @@ export function ModerationScreenInner({
{labelers.map((labeler, i) => {
return (
-
+
{i !== 0 && }
{state => (
@@ -457,12 +436,12 @@ export function ModerationScreenInner({
)}
-
+
)
})}
)}
-
-
+
+
)
}
diff --git a/src/screens/Onboarding/Layout.tsx b/src/screens/Onboarding/Layout.tsx
index 54821532c3..059cdfd5cd 100644
--- a/src/screens/Onboarding/Layout.tsx
+++ b/src/screens/Onboarding/Layout.tsx
@@ -1,13 +1,11 @@
import React from 'react'
-import {View} from 'react-native'
-import Animated from 'react-native-reanimated'
+import {ScrollView, View} from 'react-native'
import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {isWeb} from '#/platform/detection'
import {useOnboardingDispatch} from '#/state/shell'
-import {ScrollView} from '#/view/com/util/Views'
import {Context} from '#/screens/Onboarding/state'
import {
atoms as a,
@@ -36,7 +34,7 @@ export function Layout({children}: React.PropsWithChildren<{}>) {
const {gtMobile} = useBreakpoints()
const onboardDispatch = useOnboardingDispatch()
const {state, dispatch} = React.useContext(Context)
- const scrollview = React.useRef(null)
+ const scrollview = React.useRef(null)
const prevActiveStep = React.useRef(state.activeStep)
React.useEffect(() => {
diff --git a/src/screens/Post/PostLikedBy.tsx b/src/screens/Post/PostLikedBy.tsx
index 6fc485f34b..d35d332432 100644
--- a/src/screens/Post/PostLikedBy.tsx
+++ b/src/screens/Post/PostLikedBy.tsx
@@ -5,13 +5,10 @@ import {useFocusEffect} from '@react-navigation/native'
import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
import {makeRecordUri} from '#/lib/strings/url-helpers'
-import {isWeb} from '#/platform/detection'
import {useSetMinimalShellMode} from '#/state/shell'
import {PostLikedBy as PostLikedByComponent} from '#/view/com/post-thread/PostLikedBy'
import {ViewHeader} from '#/view/com/util/ViewHeader'
-import {CenteredView} from '#/view/com/util/Views'
import * as Layout from '#/components/Layout'
-import {ListHeaderDesktop} from '#/components/Lists'
type Props = NativeStackScreenProps
export const PostLikedByScreen = ({route}: Props) => {
@@ -28,11 +25,8 @@ export const PostLikedByScreen = ({route}: Props) => {
return (
-
-
-
-
-
+
+
)
}
diff --git a/src/screens/Post/PostQuotes.tsx b/src/screens/Post/PostQuotes.tsx
index 71dd8ad8d7..2cd6be8793 100644
--- a/src/screens/Post/PostQuotes.tsx
+++ b/src/screens/Post/PostQuotes.tsx
@@ -11,7 +11,6 @@ import {PostQuotes as PostQuotesComponent} from '#/view/com/post-thread/PostQuot
import {ViewHeader} from '#/view/com/util/ViewHeader'
import {CenteredView} from '#/view/com/util/Views'
import * as Layout from '#/components/Layout'
-import {ListHeaderDesktop} from '#/components/Lists'
type Props = NativeStackScreenProps
export const PostQuotesScreen = ({route}: Props) => {
@@ -29,7 +28,6 @@ export const PostQuotesScreen = ({route}: Props) => {
return (
-
diff --git a/src/screens/Post/PostRepostedBy.tsx b/src/screens/Post/PostRepostedBy.tsx
index c1e8b29878..304e708081 100644
--- a/src/screens/Post/PostRepostedBy.tsx
+++ b/src/screens/Post/PostRepostedBy.tsx
@@ -11,7 +11,6 @@ import {PostRepostedBy as PostRepostedByComponent} from '#/view/com/post-thread/
import {ViewHeader} from '#/view/com/util/ViewHeader'
import {CenteredView} from '#/view/com/util/Views'
import * as Layout from '#/components/Layout'
-import {ListHeaderDesktop} from '#/components/Lists'
type Props = NativeStackScreenProps
export const PostRepostedByScreen = ({route}: Props) => {
@@ -29,7 +28,6 @@ export const PostRepostedByScreen = ({route}: Props) => {
return (
-
diff --git a/src/screens/Profile/KnownFollowers.tsx b/src/screens/Profile/KnownFollowers.tsx
index 7e396c350f..d6dd15c698 100644
--- a/src/screens/Profile/KnownFollowers.tsx
+++ b/src/screens/Profile/KnownFollowers.tsx
@@ -15,14 +15,22 @@ import {ProfileCardWithFollowBtn} from '#/view/com/profile/ProfileCard'
import {List} from '#/view/com/util/List'
import {ViewHeader} from '#/view/com/util/ViewHeader'
import * as Layout from '#/components/Layout'
-import {
- ListFooter,
- ListHeaderDesktop,
- ListMaybePlaceholder,
-} from '#/components/Lists'
+import {ListFooter, ListMaybePlaceholder} from '#/components/Lists'
-function renderItem({item}: {item: AppBskyActorDefs.ProfileViewBasic}) {
- return
+function renderItem({
+ item,
+ index,
+}: {
+ item: AppBskyActorDefs.ProfileViewBasic
+ index: number
+}) {
+ return (
+
+ )
}
function keyExtractor(item: AppBskyActorDefs.ProfileViewBasic) {
@@ -93,6 +101,7 @@ export const ProfileKnownFollowersScreen = ({route}: Props) => {
if (followers.length < 1) {
return (
+ {
emptyMessage={_(msg`You don't follow any users who follow @${name}.`)}
errorMessage={cleanError(resolveError || error)}
onRetry={isError ? refetch : undefined}
+ topBorder={false}
+ sideBorders={false}
/>
)
@@ -116,9 +127,6 @@ export const ProfileKnownFollowersScreen = ({route}: Props) => {
onRefresh={onRefresh}
onEndReached={onEndReached}
onEndReachedThreshold={4}
- ListHeaderComponent={
-
- }
ListFooterComponent={
{
desktopFixedHeight
initialNumToRender={initialNumToRender}
windowSize={11}
+ sideBorders={false}
/>
)
diff --git a/src/screens/Profile/Sections/Labels.tsx b/src/screens/Profile/Sections/Labels.tsx
index 67c827d901..6c76d7b153 100644
--- a/src/screens/Profile/Sections/Labels.tsx
+++ b/src/screens/Profile/Sections/Labels.tsx
@@ -15,10 +15,11 @@ import {isLabelerSubscribed, lookupLabelValueDefinition} from '#/lib/moderation'
import {useScrollHandlers} from '#/lib/ScrollContext'
import {isNative} from '#/platform/detection'
import {ListRef} from '#/view/com/util/List'
-import {CenteredView, ScrollView} from '#/view/com/util/Views'
+import {ScrollView} from '#/view/com/util/Views'
import {atoms as a, useTheme} from '#/alf'
import {Divider} from '#/components/Divider'
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
+import * as Layout from '#/components/Layout'
import {Loader} from '#/components/Loader'
import {LabelerLabelPreference} from '#/components/moderation/LabelPreference'
import {Text} from '#/components/Typography'
@@ -75,7 +76,7 @@ export const ProfileLabelsSection = React.forwardRef<
}, [isFocused, scrollElRef, setScrollViewTag])
return (
-
+
{isLabelerLoading ? (
@@ -95,7 +96,7 @@ export const ProfileLabelsSection = React.forwardRef<
headerHeight={headerHeight}
/>
)}
-
+
)
})
diff --git a/src/screens/Settings/AboutSettings.tsx b/src/screens/Settings/AboutSettings.tsx
index 8019a20f90..02976bb3ca 100644
--- a/src/screens/Settings/AboutSettings.tsx
+++ b/src/screens/Settings/AboutSettings.tsx
@@ -21,7 +21,15 @@ export function AboutSettingsScreen({}: Props) {
return (
-
+
+
+
+
+ About
+
+
+
+
-
+
+
+
+
+ Accessibility
+
+
+
+
diff --git a/src/screens/Settings/AccountSettings.tsx b/src/screens/Settings/AccountSettings.tsx
index 2495a0f2f9..634c9d3f78 100644
--- a/src/screens/Settings/AccountSettings.tsx
+++ b/src/screens/Settings/AccountSettings.tsx
@@ -38,7 +38,15 @@ export function AccountSettingsScreen({}: Props) {
return (
-
+
+
+
+
+ Account
+
+
+
+
diff --git a/src/screens/Settings/AppIconSettings.tsx b/src/screens/Settings/AppIconSettings.tsx
index 1dd87d45f4..18fcd5e305 100644
--- a/src/screens/Settings/AppIconSettings.tsx
+++ b/src/screens/Settings/AppIconSettings.tsx
@@ -1,7 +1,7 @@
import React from 'react'
import {Alert, View} from 'react-native'
import {Image} from 'expo-image'
-import {msg} from '@lingui/macro'
+import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import * as AppIcon from '@mozzius/expo-dynamic-app-icon'
import {NativeStackScreenProps} from '@react-navigation/native-stack'
@@ -20,7 +20,15 @@ export function AppIconSettingsScreen({}: Props) {
return (
-
+
+
+
+
+ App Icon
+
+
+
+ Defaults
diff --git a/src/screens/Settings/AppPasswords.tsx b/src/screens/Settings/AppPasswords.tsx
index 1ea0bd1b3d..630d26ba78 100644
--- a/src/screens/Settings/AppPasswords.tsx
+++ b/src/screens/Settings/AppPasswords.tsx
@@ -44,7 +44,15 @@ export function AppPasswordsScreen({}: Props) {
return (
-
+
+
+
+
+ App Passwords
+
+
+
+
{error ? (
-
+
+
+
+
+ Appearance
+
+
+
+
-
+
+
+
+
+ Content & Media
+
+
+
+
export function ExternalMediaPreferencesScreen({}: Props) {
- const {_} = useLingui()
return (
-
+
+
+
+
+ External Media Preferences
+
+
+
+
diff --git a/src/screens/Settings/FollowingFeedPreferences.tsx b/src/screens/Settings/FollowingFeedPreferences.tsx
index 089491dd0f..ea9455ab1a 100644
--- a/src/screens/Settings/FollowingFeedPreferences.tsx
+++ b/src/screens/Settings/FollowingFeedPreferences.tsx
@@ -46,7 +46,15 @@ export function FollowingFeedPreferencesScreen({}: Props) {
return (
-
+
+
+
+
+ Following Feed Preferences
+
+
+
+
diff --git a/src/screens/Settings/LanguageSettings.tsx b/src/screens/Settings/LanguageSettings.tsx
index a44e2fcec7..096f925669 100644
--- a/src/screens/Settings/LanguageSettings.tsx
+++ b/src/screens/Settings/LanguageSettings.tsx
@@ -64,7 +64,15 @@ export function LanguageSettingsScreen({}: Props) {
return (
-
+
+
+
+
+ Languages
+
+
+
+
diff --git a/src/screens/Settings/NotificationSettings.tsx b/src/screens/Settings/NotificationSettings.tsx
index c5f7078c48..1c77b31489 100644
--- a/src/screens/Settings/NotificationSettings.tsx
+++ b/src/screens/Settings/NotificationSettings.tsx
@@ -33,7 +33,15 @@ export function NotificationSettingsScreen({}: Props) {
return (
-
+
+
+
+
+ Notification Settings
+
+
+
+
{isQueryError ? (
-
+
+
+
+
+ Privacy and Security
+
+
+
+
diff --git a/src/screens/Settings/Settings.tsx b/src/screens/Settings/Settings.tsx
index 126a1bc88e..7a4ad6f204 100644
--- a/src/screens/Settings/Settings.tsx
+++ b/src/screens/Settings/Settings.tsx
@@ -73,7 +73,15 @@ export function SettingsScreen({}: Props) {
return (
-
+
+
+
+
+ Settings
+
+
+
+
-
+
+
+
+
+ Thread Preferences
+
+
+
+
diff --git a/src/screens/SignupQueued.tsx b/src/screens/SignupQueued.tsx
index ed261f29ea..f1c36a69c3 100644
--- a/src/screens/SignupQueued.tsx
+++ b/src/screens/SignupQueued.tsx
@@ -1,5 +1,5 @@
import React from 'react'
-import {Modal, View} from 'react-native'
+import {Modal, ScrollView, View} from 'react-native'
import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {StatusBar} from 'expo-status-bar'
import {msg, plural, Trans} from '@lingui/macro'
@@ -9,7 +9,6 @@ import {logger} from '#/logger'
import {isIOS, isWeb} from '#/platform/detection'
import {isSignupQueued, useAgent, useSessionApi} from '#/state/session'
import {useOnboardingDispatch} from '#/state/shell'
-import {ScrollView} from '#/view/com/util/Views'
import {Logo} from '#/view/icons/Logo'
import {atoms as a, native, useBreakpoints, useTheme, web} from '#/alf'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
diff --git a/src/screens/StarterPack/Wizard/index.tsx b/src/screens/StarterPack/Wizard/index.tsx
index b0d71b9294..b42b753e36 100644
--- a/src/screens/StarterPack/Wizard/index.tsx
+++ b/src/screens/StarterPack/Wizard/index.tsx
@@ -1,5 +1,5 @@
import React from 'react'
-import {Keyboard, TouchableOpacity, View} from 'react-native'
+import {Keyboard, View} from 'react-native'
import {KeyboardAwareScrollView} from 'react-native-keyboard-controller'
import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {Image} from 'expo-image'
@@ -10,13 +10,12 @@ import {
ModerationOpts,
} from '@atproto/api'
import {GeneratorView} from '@atproto/api/dist/client/types/app/bsky/feed/defs'
-import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {msg, Plural, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useFocusEffect, useNavigation} from '@react-navigation/native'
import {NativeStackScreenProps} from '@react-navigation/native-stack'
-import {HITSLOP_10, STARTER_PACK_MAX_SIZE} from '#/lib/constants'
+import {STARTER_PACK_MAX_SIZE} from '#/lib/constants'
import {useEnableKeyboardControllerScreen} from '#/lib/hooks/useEnableKeyboardController'
import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name'
import {CommonNavigatorParams, NavigationProp} from '#/lib/routes/types'
@@ -29,7 +28,7 @@ import {
parseStarterPackUri,
} from '#/lib/strings/starter-pack'
import {logger} from '#/logger'
-import {isAndroid, isNative, isWeb} from '#/platform/detection'
+import {isNative} from '#/platform/detection'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {useAllListMembersQuery} from '#/state/queries/list-members'
import {useProfileQuery} from '#/state/queries/profile'
@@ -147,7 +146,6 @@ function WizardInner({
}) {
const navigation = useNavigation()
const {_} = useLingui()
- const t = useTheme()
const setMinimalShellMode = useSetMinimalShellMode()
const [state, dispatch] = useWizardState()
const {currentAccount} = useSession()
@@ -283,45 +281,24 @@ function WizardInner({
return (
-
-
- {
- if (state.currentStep === 'Details') {
- navigation.pop()
- } else {
- dispatch({type: 'Back'})
- }
- }}>
-
-
-
-
- {currUiStrings.header}
-
-
-
+
+ {
+ if (state.currentStep !== 'Details') {
+ evt.preventDefault()
+ dispatch({type: 'Back'})
+ }
+ }}
+ />
+
+
+ {currUiStrings.header}
+
+
+
+
{state.currentStep === 'Details' ? (
@@ -463,17 +440,17 @@ function Footer({
You and
-
+
{getName(items[1] /* [0] is self, skip it */)}{' '}
are included in your starter pack
) : items.length > 2 ? (
-
+
{getName(items[1] /* [0] is self, skip it */)},{' '}
-
+
{getName(items[2])},{' '}
and{' '}
@@ -504,29 +481,29 @@ function Footer({
{
items.length === 1 ? (
-
+
{getName(items[0])}
{' '}
is included in your starter pack
) : items.length === 2 ? (
-
+
{getName(items[0])}
{' '}
and
-
+
{getName(items[1])}{' '}
are included in your starter pack
) : items.length > 2 ? (
-
+
{getName(items[0])},{' '}
-
+
{getName(items[1])},{' '}
and{' '}
diff --git a/src/view/com/feeds/FeedPage.tsx b/src/view/com/feeds/FeedPage.tsx
index 44e90a5519..fa5a620bf6 100644
--- a/src/view/com/feeds/FeedPage.tsx
+++ b/src/view/com/feeds/FeedPage.tsx
@@ -108,7 +108,7 @@ export function FeedPage({
}, [scrollToTop, feed, queryClient, setHasNew])
return (
-
+
} else {
return
@@ -40,98 +41,43 @@ function HomeHeaderLayoutDesktopAndTablet({
const {hasSession} = useSession()
const {_} = useLingui()
const kawaii = useKawaiiMode()
+ const gutter = useGutterStyles()
return (
<>
{hasSession && (
-
+
-
+ style={[a.flex_row, a.align_center, a.pt_md, gutter, t.atoms.bg]}>
+
+
+
+
+
+
+
-
-
-
-
-
+
)}
{tabBarAnchor}
- {
- headerHeight.set(e.nativeEvent.layout.height)
- }}
- style={[
- t.atoms.bg,
- t.atoms.border_contrast_low,
- styles.bar,
- styles.tabBar,
- headerMinimalShellTransform,
- ]}>
- {children}
-
+
+ {
+ headerHeight.set(e.nativeEvent.layout.height)
+ }}
+ style={[headerMinimalShellTransform]}>
+ {children}
+
+
>
)
}
-
-const styles = StyleSheet.create({
- bar: {
- // @ts-ignore Web only
- left: 'calc(50% - 300px)',
- width: 600,
- borderLeftWidth: 1,
- borderRightWidth: 1,
- },
- topBar: {
- flexDirection: 'row',
- justifyContent: 'space-between',
- alignItems: 'center',
- paddingHorizontal: 18,
- paddingTop: 16,
- paddingBottom: 8,
- },
- tabBar: {
- // @ts-ignore Web only
- position: 'sticky',
- top: 0,
- flexDirection: 'column',
- alignItems: 'center',
- borderLeftWidth: 1,
- borderRightWidth: 1,
- zIndex: 1,
- },
-})
diff --git a/src/view/com/home/HomeHeaderLayoutMobile.tsx b/src/view/com/home/HomeHeaderLayoutMobile.tsx
index 8323960924..e48c2cc893 100644
--- a/src/view/com/home/HomeHeaderLayoutMobile.tsx
+++ b/src/view/com/home/HomeHeaderLayoutMobile.tsx
@@ -1,25 +1,22 @@
import React from 'react'
-import {StyleSheet, TouchableOpacity, View} from 'react-native'
+import {View} from 'react-native'
import Animated from 'react-native-reanimated'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {HITSLOP_10} from '#/lib/constants'
+import {PressableScale} from '#/lib/custom-animations/PressableScale'
+import {useHaptics} from '#/lib/haptics'
import {useMinimalShellHeaderTransform} from '#/lib/hooks/useMinimalShellTransform'
-import {usePalette} from '#/lib/hooks/usePalette'
-import {isWeb} from '#/platform/detection'
+import {emitSoftReset} from '#/state/events'
import {useSession} from '#/state/session'
-import {useSetDrawerOpen} from '#/state/shell/drawer-open'
import {useShellLayout} from '#/state/shell/shell-layout'
import {Logo} from '#/view/icons/Logo'
-import {atoms} from '#/alf'
-import {useTheme} from '#/alf'
-import {atoms as a} from '#/alf'
-import {ColorPalette_Stroke2_Corner0_Rounded as ColorPalette} from '#/components/icons/ColorPalette'
+import {atoms as a, useTheme} from '#/alf'
+import {ButtonIcon} from '#/components/Button'
import {Hashtag_Stroke2_Corner0_Rounded as FeedsIcon} from '#/components/icons/Hashtag'
-import {Menu_Stroke2_Corner0_Rounded as Menu} from '#/components/icons/Menu'
+import * as Layout from '#/components/Layout'
import {Link} from '#/components/Link'
-import {IS_DEV} from '#/env'
export function HomeHeaderLayoutMobile({
children,
@@ -28,58 +25,50 @@ export function HomeHeaderLayoutMobile({
tabBarAnchor: JSX.Element | null | undefined
}) {
const t = useTheme()
- const pal = usePalette('default')
const {_} = useLingui()
- const setDrawerOpen = useSetDrawerOpen()
const {headerHeight} = useShellLayout()
const headerMinimalShellTransform = useMinimalShellHeaderTransform()
const {hasSession} = useSession()
-
- const onPressAvi = React.useCallback(() => {
- setDrawerOpen(true)
- }, [setDrawerOpen])
+ const playHaptic = useHaptics()
return (
{
headerHeight.set(e.nativeEvent.layout.height)
}}>
-
-
-
-
-
+
+
+
+
+
+
+ {
+ emitSoftReset()
+ }}
+ onPressIn={() => {
+ playHaptic('Heavy')
+ }}
+ onPressOut={() => {
+ playHaptic('Light')
+ }}>
+
+
-
-
-
-
- {IS_DEV && (
- <>
-
-
-
- >
- )}
+
+
{hasSession && (
-
+
)}
-
-
+
+
{children}
)
}
-
-const styles = StyleSheet.create({
- tabBar: {
- // @ts-ignore web-only
- position: isWeb ? 'fixed' : 'absolute',
- zIndex: 1,
- left: 0,
- right: 0,
- top: 0,
- flexDirection: 'column',
- },
- topBar: {
- flexDirection: 'row',
- justifyContent: 'space-between',
- alignItems: 'center',
- paddingHorizontal: 16,
- paddingVertical: 5,
- width: '100%',
- minHeight: 46,
- },
- title: {
- fontSize: 21,
- },
-})
diff --git a/src/view/com/lightbox/Lightbox.web.tsx b/src/view/com/lightbox/Lightbox.web.tsx
index f9b147b297..f6b6223ce2 100644
--- a/src/view/com/lightbox/Lightbox.web.tsx
+++ b/src/view/com/lightbox/Lightbox.web.tsx
@@ -15,8 +15,8 @@ import {
} from '@fortawesome/react-native-fontawesome'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
+import {RemoveScrollBar} from 'react-remove-scroll-bar'
-import {useWebBodyScrollLock} from '#/lib/hooks/useWebBodyScrollLock'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {colors, s} from '#/lib/styles'
import {useLightbox, useLightboxControls} from '#/state/lightbox'
@@ -28,7 +28,6 @@ export function Lightbox() {
const {activeLightbox} = useLightbox()
const {closeLightbox} = useLightboxControls()
const isActive = !!activeLightbox
- useWebBodyScrollLock(isActive)
if (!isActive) {
return null
@@ -37,11 +36,14 @@ export function Lightbox() {
const initialIndex = activeLightbox.index
const imgs = activeLightbox.images
return (
-
+ <>
+
+
+ >
)
}
diff --git a/src/view/com/lists/MyLists.tsx b/src/view/com/lists/MyLists.tsx
index 363dd100dd..17327fd9ae 100644
--- a/src/view/com/lists/MyLists.tsx
+++ b/src/view/com/lists/MyLists.tsx
@@ -15,7 +15,6 @@ import {usePalette} from '#/lib/hooks/usePalette'
import {cleanError} from '#/lib/strings/errors'
import {s} from '#/lib/styles'
import {logger} from '#/logger'
-import {isWeb} from '#/platform/detection'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {MyListsFilter, useMyListsQuery} from '#/state/queries/my-lists'
import {EmptyState} from '#/view/com/util/EmptyState'
@@ -110,7 +109,7 @@ export function MyLists({
) : (
)}
@@ -160,8 +157,8 @@ export function MyLists({
onRefresh={onRefresh}
contentContainerStyle={[s.contentContainer]}
removeClippedSubviews={true}
- // @ts-ignore our .web version only -prf
desktopFixedHeight
+ sideBorders={false}
/>
)}
diff --git a/src/view/com/modals/Modal.web.tsx b/src/view/com/modals/Modal.web.tsx
index 8d93c21b4d..0c49c87716 100644
--- a/src/view/com/modals/Modal.web.tsx
+++ b/src/view/com/modals/Modal.web.tsx
@@ -1,8 +1,8 @@
import {StyleSheet, TouchableWithoutFeedback, View} from 'react-native'
import Animated, {FadeIn, FadeOut} from 'react-native-reanimated'
+import {RemoveScrollBar} from 'react-remove-scroll-bar'
import {usePalette} from '#/lib/hooks/usePalette'
-import {useWebBodyScrollLock} from '#/lib/hooks/useWebBodyScrollLock'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import type {Modal as ModalIface} from '#/state/modals'
import {useModalControls, useModals} from '#/state/modals'
@@ -22,7 +22,6 @@ import * as VerifyEmailModal from './VerifyEmail'
export function ModalsContainer() {
const {isModalActive, activeModals} = useModals()
- useWebBodyScrollLock(isModalActive)
if (!isModalActive) {
return null
@@ -30,6 +29,7 @@ export function ModalsContainer() {
return (
<>
+
{activeModals.map((modal, i) => (
))}
diff --git a/src/view/com/notifications/Feed.tsx b/src/view/com/notifications/Feed.tsx
index bd39ddd843..9871455a17 100644
--- a/src/view/com/notifications/Feed.tsx
+++ b/src/view/com/notifications/Feed.tsx
@@ -10,7 +10,6 @@ import {useLingui} from '@lingui/react'
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
import {usePalette} from '#/lib/hooks/usePalette'
-import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {cleanError} from '#/lib/strings/errors'
import {s} from '#/lib/styles'
import {logger} from '#/logger'
@@ -22,7 +21,6 @@ import {ErrorMessage} from '#/view/com/util/error/ErrorMessage'
import {List, ListRef} from '#/view/com/util/List'
import {NotificationFeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
import {LoadMoreRetryBtn} from '#/view/com/util/LoadMoreRetryBtn'
-import {CenteredView} from '#/view/com/util/Views'
import {FeedItem} from './FeedItem'
const EMPTY_FEED_ITEM = {_reactKey: '__empty__'}
@@ -46,7 +44,6 @@ export function Feed({
const [isPTRing, setIsPTRing] = React.useState(false)
const pal = usePalette('default')
- const {isTabletOrMobile} = useWebMediaQueries()
const {_} = useLingui()
const moderationOpts = useModerationOpts()
@@ -133,11 +130,7 @@ export function Feed({
)
} else if (item === LOADING_ITEM) {
return (
-
+
)
@@ -146,11 +139,11 @@ export function Feed({
)
},
- [moderationOpts, isTabletOrMobile, _, onPressRetryLoadMore, pal.border],
+ [moderationOpts, _, onPressRetryLoadMore, pal.border],
)
const FeedFooter = React.useCallback(
@@ -168,12 +161,10 @@ export function Feed({
return (
{error && (
-
-
-
+
)}
void
tabBarAnchor?: JSX.Element | null | undefined
}): React.ReactNode => {
- const pal = usePalette('default')
- const {isMobile} = useWebMediaQueries()
return (
<>
-
- {renderHeader?.()}
-
+ {renderHeader?.()}
{tabBarAnchor}
-
+ ])}>
-
+
>
)
}
@@ -180,33 +169,6 @@ function PagerItem({
})
}
-const styles = StyleSheet.create({
- headerContainerDesktop: {
- marginHorizontal: 'auto',
- width: 600,
- borderLeftWidth: 1,
- borderRightWidth: 1,
- },
- tabBarContainer: {
- // @ts-ignore web-only
- position: 'sticky',
- top: 0,
- zIndex: 1,
- },
- tabBarContainerDesktop: {
- marginHorizontal: 'auto',
- width: 600,
- borderLeftWidth: 1,
- borderRightWidth: 1,
- },
- tabBarContainerMobile: {
- paddingHorizontal: 0,
- },
- loadingHeader: {
- borderColor: 'transparent',
- },
-})
-
function toArray(v: T | T[]): T[] {
if (Array.isArray(v)) {
return v
diff --git a/src/view/com/post-thread/PostLikedBy.tsx b/src/view/com/post-thread/PostLikedBy.tsx
index 4c0d973a91..b9051a9c6d 100644
--- a/src/view/com/post-thread/PostLikedBy.tsx
+++ b/src/view/com/post-thread/PostLikedBy.tsx
@@ -6,7 +6,6 @@ import {useLingui} from '@lingui/react'
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
import {cleanError} from '#/lib/strings/errors'
import {logger} from '#/logger'
-import {isWeb} from '#/platform/detection'
import {useLikedByQuery} from '#/state/queries/post-liked-by'
import {useResolveUriQuery} from '#/state/queries/resolve-uri'
import {ProfileCardWithFollowBtn} from '#/view/com/profile/ProfileCard'
@@ -18,7 +17,7 @@ function renderItem({item, index}: {item: GetLikes.Like; index: number}) {
)
}
@@ -88,6 +87,7 @@ export function PostLikedBy({uri}: {uri: string}) {
)}
errorMessage={cleanError(resolveError || error)}
sideBorders={false}
+ topBorder={false}
/>
)
}
@@ -108,7 +108,6 @@ export function PostLikedBy({uri}: {uri: string}) {
onRetry={fetchNextPage}
/>
}
- // @ts-ignore our .web version only -prf
desktopFixedHeight
initialNumToRender={initialNumToRender}
windowSize={11}
diff --git a/src/view/com/post-thread/PostQuotes.tsx b/src/view/com/post-thread/PostQuotes.tsx
index 10a51166c7..a22000b969 100644
--- a/src/view/com/post-thread/PostQuotes.tsx
+++ b/src/view/com/post-thread/PostQuotes.tsx
@@ -11,7 +11,6 @@ import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
import {moderatePost_wrapped as moderatePost} from '#/lib/moderatePost_wrapped'
import {cleanError} from '#/lib/strings/errors'
import {logger} from '#/logger'
-import {isWeb} from '#/platform/detection'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {usePostQuotesQuery} from '#/state/queries/post-quotes'
import {useResolveUriQuery} from '#/state/queries/resolve-uri'
@@ -30,7 +29,7 @@ function renderItem({
}
index: number
}) {
- return
+ return
}
function keyExtractor(item: {
diff --git a/src/view/com/post-thread/PostRepostedBy.tsx b/src/view/com/post-thread/PostRepostedBy.tsx
index dfaa697804..2143bd9c27 100644
--- a/src/view/com/post-thread/PostRepostedBy.tsx
+++ b/src/view/com/post-thread/PostRepostedBy.tsx
@@ -12,8 +12,20 @@ import {ProfileCardWithFollowBtn} from '#/view/com/profile/ProfileCard'
import {List} from '#/view/com/util/List'
import {ListFooter, ListMaybePlaceholder} from '#/components/Lists'
-function renderItem({item}: {item: ActorDefs.ProfileViewBasic}) {
- return
+function renderItem({
+ item,
+ index,
+}: {
+ item: ActorDefs.ProfileViewBasic
+ index: number
+}) {
+ return (
+
+ )
}
function keyExtractor(item: ActorDefs.ProfileViewBasic) {
diff --git a/src/view/com/post-thread/PostThread.tsx b/src/view/com/post-thread/PostThread.tsx
index a101493959..0cdccff590 100644
--- a/src/view/com/post-thread/PostThread.tsx
+++ b/src/view/com/post-thread/PostThread.tsx
@@ -32,7 +32,6 @@ import {usePreferencesQuery} from '#/state/queries/preferences'
import {useSession} from '#/state/session'
import {useComposerControls} from '#/state/shell'
import {useMergedThreadgateHiddenReplies} from '#/state/threadgate-hidden-replies'
-import {CenteredView} from '#/view/com/util/Views'
import {atoms as a, useTheme} from '#/alf'
import {ListFooter, ListMaybePlaceholder} from '#/components/Lists'
import {Text} from '#/components/Typography'
@@ -484,7 +483,7 @@ export function PostThread({uri}: {uri: string | undefined}) {
}
return (
-
+ <>
{showHeader && (
)}
-
+ >
)
}
diff --git a/src/view/com/profile/ProfileFollowers.tsx b/src/view/com/profile/ProfileFollowers.tsx
index 60a7a5e316..3c04769292 100644
--- a/src/view/com/profile/ProfileFollowers.tsx
+++ b/src/view/com/profile/ProfileFollowers.tsx
@@ -6,7 +6,6 @@ import {useLingui} from '@lingui/react'
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
import {cleanError} from '#/lib/strings/errors'
import {logger} from '#/logger'
-import {isWeb} from '#/platform/detection'
import {useProfileFollowersQuery} from '#/state/queries/profile-followers'
import {useResolveDidQuery} from '#/state/queries/resolve-uri'
import {useSession} from '#/state/session'
@@ -25,7 +24,7 @@ function renderItem({
)
}
diff --git a/src/view/com/profile/ProfileFollows.tsx b/src/view/com/profile/ProfileFollows.tsx
index 572b0b9f41..1cd65c74c0 100644
--- a/src/view/com/profile/ProfileFollows.tsx
+++ b/src/view/com/profile/ProfileFollows.tsx
@@ -6,7 +6,6 @@ import {useLingui} from '@lingui/react'
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
import {cleanError} from '#/lib/strings/errors'
import {logger} from '#/logger'
-import {isWeb} from '#/platform/detection'
import {useProfileFollowsQuery} from '#/state/queries/profile-follows'
import {useResolveDidQuery} from '#/state/queries/resolve-uri'
import {useSession} from '#/state/session'
@@ -25,7 +24,7 @@ function renderItem({
)
}
diff --git a/src/view/com/profile/ProfileSubpageHeader.tsx b/src/view/com/profile/ProfileSubpageHeader.tsx
index 0e25fe5e61..cd11611a86 100644
--- a/src/view/com/profile/ProfileSubpageHeader.tsx
+++ b/src/view/com/profile/ProfileSubpageHeader.tsx
@@ -1,29 +1,24 @@
import React from 'react'
-import {Pressable, StyleSheet, View} from 'react-native'
+import {Pressable, View} from 'react-native'
import {MeasuredDimensions, runOnJS, runOnUI} from 'react-native-reanimated'
-import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useNavigation} from '@react-navigation/native'
-import {BACK_HITSLOP} from '#/lib/constants'
import {measureHandle, useHandleRef} from '#/lib/hooks/useHandleRef'
import {usePalette} from '#/lib/hooks/usePalette'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {makeProfileLink} from '#/lib/routes/links'
import {NavigationProp} from '#/lib/routes/types'
import {sanitizeHandle} from '#/lib/strings/handles'
-import {isNative} from '#/platform/detection'
import {emitSoftReset} from '#/state/events'
import {useLightboxControls} from '#/state/lightbox'
-import {useSetDrawerOpen} from '#/state/shell'
-import {Menu_Stroke2_Corner0_Rounded as Menu} from '#/components/icons/Menu'
+import {TextLink} from '#/view/com/util/Link'
+import {LoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
+import {Text} from '#/view/com/util/text/Text'
+import {UserAvatar, UserAvatarType} from '#/view/com/util/UserAvatar'
import {StarterPack} from '#/components/icons/StarterPack'
-import {TextLink} from '../util/Link'
-import {LoadingPlaceholder} from '../util/LoadingPlaceholder'
-import {Text} from '../util/text/Text'
-import {UserAvatar, UserAvatarType} from '../util/UserAvatar'
-import {CenteredView} from '../util/Views'
+import * as Layout from '#/components/Layout'
export function ProfileSubpageHeader({
isLoading,
@@ -48,7 +43,6 @@ export function ProfileSubpageHeader({
| undefined
avatarType: UserAvatarType | 'starter-pack'
}>) {
- const setDrawerOpen = useSetDrawerOpen()
const navigation = useNavigation()
const {_} = useLingui()
const {isMobile} = useWebMediaQueries()
@@ -57,18 +51,6 @@ export function ProfileSubpageHeader({
const canGoBack = navigation.canGoBack()
const aviRef = useHandleRef()
- const onPressBack = React.useCallback(() => {
- if (navigation.canGoBack()) {
- navigation.goBack()
- } else {
- navigation.navigate('Home')
- }
- }, [navigation])
-
- const onPressMenu = React.useCallback(() => {
- setDrawerOpen(true)
- }, [setDrawerOpen])
-
const _openLightbox = React.useCallback(
(uri: string, thumbRect: MeasuredDimensions | null) => {
openLightbox({
@@ -106,42 +88,17 @@ export function ProfileSubpageHeader({
}, [_openLightbox, avatar, aviRef])
return (
-
- {isMobile && (
-
-
- {canGoBack ? (
-
- ) : (
-
- )}
-
-
- {children}
-
- )}
+ <>
+
+ {canGoBack ? (
+
+ ) : (
+
+ )}
+
+ {children}
+
+
)}
- {!isMobile && (
-
- {children}
-
- )}
-
+ >
)
}
-
-const styles = StyleSheet.create({
- backBtn: {
- width: 20,
- height: 30,
- },
- backBtnWide: {
- width: 20,
- height: 30,
- marginRight: 4,
- },
- backIcon: {
- marginTop: 6,
- },
-})
diff --git a/src/view/com/util/List.web.tsx b/src/view/com/util/List.web.tsx
index f112d2d0a4..18f7d6fa7e 100644
--- a/src/view/com/util/List.web.tsx
+++ b/src/view/com/util/List.web.tsx
@@ -4,10 +4,9 @@ import {ReanimatedScrollEvent} from 'react-native-reanimated/lib/typescript/hook
import {batchedUpdates} from '#/lib/batchedUpdates'
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
-import {usePalette} from '#/lib/hooks/usePalette'
-import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {useScrollHandlers} from '#/lib/ScrollContext'
import {addStyle} from '#/lib/styles'
+import * as Layout from '#/components/Layout'
export type ListMethods = any // TODO: Better types.
export type ListProps = Omit<
@@ -24,6 +23,9 @@ export type ListProps = Omit<
desktopFixedHeight?: number | boolean
// Web only prop to contain the scroll to the container rather than the window
disableFullWindowScroll?: boolean
+ /**
+ * @deprecated Should be using Layout components
+ */
sideBorders?: boolean
}
export type ListRef = React.MutableRefObject // TODO: Better types.
@@ -56,20 +58,11 @@ function ListImpl(
renderItem,
extraData,
style,
- sideBorders = true,
...props
}: ListProps,
ref: React.Ref,
) {
const contextScrollHandlers = useScrollHandlers()
- const pal = usePalette('default')
- const {isMobile} = useWebMediaQueries()
- if (!isMobile) {
- contentContainerStyle = addStyle(
- contentContainerStyle,
- styles.containerScroll,
- )
- }
const isEmpty = !data || data.length === 0
@@ -326,53 +319,53 @@ function ListImpl(
styles.parentTreeVisibilityDetector
}
/>
-
-
- {onStartReached && !isEmpty && (
-
+
+
- )}
- {headerComponent}
- {isEmpty
- ? emptyComponent
- : (data as Array)?.map((item, index) => {
- const key = keyExtractor!(item, index)
- return (
-
- key={key}
- item={item}
- index={index}
- renderItem={renderItem}
- extraData={extraData}
- onItemSeen={onItemSeen}
- />
- )
- })}
- {onEndReached && !isEmpty && (
-
- )}
- {footerComponent}
-
+ {onStartReached && !isEmpty && (
+
+ )}
+ {headerComponent}
+ {isEmpty
+ ? emptyComponent
+ : (data as Array)?.map((item, index) => {
+ const key = keyExtractor!(item, index)
+ return (
+
+ key={key}
+ item={item}
+ index={index}
+ renderItem={renderItem}
+ extraData={extraData}
+ onItemSeen={onItemSeen}
+ />
+ )
+ })}
+ {onEndReached && !isEmpty && (
+
+ )}
+ {footerComponent}
+
+
)
}
@@ -558,16 +551,6 @@ export const List = memo(React.forwardRef(ListImpl)) as (
// https://stackoverflow.com/questions/7944460/detect-safari-browser
const styles = StyleSheet.create({
- sideBorders: {
- borderLeftWidth: 1,
- borderRightWidth: 1,
- },
- containerScroll: {
- width: '100%',
- maxWidth: 600,
- marginLeft: 'auto',
- marginRight: 'auto',
- },
minHeightViewport: {
// @ts-ignore web only
minHeight: '100vh',
diff --git a/src/view/com/util/LoadingScreen.tsx b/src/view/com/util/LoadingScreen.tsx
index 5d2aeb38ff..1086c9d17d 100644
--- a/src/view/com/util/LoadingScreen.tsx
+++ b/src/view/com/util/LoadingScreen.tsx
@@ -1,14 +1,17 @@
import {ActivityIndicator, View} from 'react-native'
import {s} from '#/lib/styles'
-import {CenteredView} from './Views'
+import * as Layout from '#/components/Layout'
+/**
+ * @deprecated use Layout compoenents directly
+ */
export function LoadingScreen() {
return (
-
+
-
+
)
}
diff --git a/src/view/com/util/SimpleViewHeader.tsx b/src/view/com/util/SimpleViewHeader.tsx
deleted file mode 100644
index 78b66a9296..0000000000
--- a/src/view/com/util/SimpleViewHeader.tsx
+++ /dev/null
@@ -1,114 +0,0 @@
-import React from 'react'
-import {
- StyleProp,
- StyleSheet,
- TouchableOpacity,
- View,
- ViewStyle,
-} from 'react-native'
-import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
-import {useNavigation} from '@react-navigation/native'
-
-import {usePalette} from '#/lib/hooks/usePalette'
-import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
-import {NavigationProp} from '#/lib/routes/types'
-import {isWeb} from '#/platform/detection'
-import {useSetDrawerOpen} from '#/state/shell'
-import {Menu_Stroke2_Corner0_Rounded as Menu} from '#/components/icons/Menu'
-import {CenteredView} from './Views'
-
-const BACK_HITSLOP = {left: 20, top: 20, right: 50, bottom: 20}
-
-export function SimpleViewHeader({
- showBackButton = true,
- style,
- children,
-}: React.PropsWithChildren<{
- showBackButton?: boolean
- style?: StyleProp
-}>) {
- const pal = usePalette('default')
- const setDrawerOpen = useSetDrawerOpen()
- const navigation = useNavigation()
- const {isMobile} = useWebMediaQueries()
- const canGoBack = navigation.canGoBack()
-
- const onPressBack = React.useCallback(() => {
- if (navigation.canGoBack()) {
- navigation.goBack()
- } else {
- navigation.navigate('Home')
- }
- }, [navigation])
-
- const onPressMenu = React.useCallback(() => {
- setDrawerOpen(true)
- }, [setDrawerOpen])
-
- const Container = isMobile ? View : CenteredView
- return (
-
- {showBackButton ? (
-
- {canGoBack ? (
-
- ) : (
-
- )}
-
- ) : null}
- {children}
-
- )
-}
-
-const styles = StyleSheet.create({
- header: {
- flexDirection: 'row',
- alignItems: 'center',
- paddingHorizontal: 18,
- paddingVertical: 12,
- width: '100%',
- },
- headerMobile: {
- paddingHorizontal: 12,
- paddingVertical: 10,
- },
- headerWeb: {
- // @ts-ignore web-only
- position: 'sticky',
- top: 0,
- zIndex: 1,
- },
- backBtn: {
- width: 30,
- height: 30,
- },
- backBtnWide: {
- width: 30,
- height: 30,
- paddingLeft: 4,
- marginRight: 4,
- },
- backIcon: {
- marginTop: 6,
- },
-})
diff --git a/src/view/com/util/ViewHeader.tsx b/src/view/com/util/ViewHeader.tsx
index 1d4cf8ff07..2d413f7825 100644
--- a/src/view/com/util/ViewHeader.tsx
+++ b/src/view/com/util/ViewHeader.tsx
@@ -1,271 +1,27 @@
-import React from 'react'
-import {StyleSheet, TouchableOpacity, View} from 'react-native'
-import Animated from 'react-native-reanimated'
-import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
-import {msg} from '@lingui/macro'
-import {useLingui} from '@lingui/react'
-import {useNavigation} from '@react-navigation/native'
-
-import {useMinimalShellHeaderTransform} from '#/lib/hooks/useMinimalShellTransform'
-import {usePalette} from '#/lib/hooks/usePalette'
-import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
-import {NavigationProp} from '#/lib/routes/types'
-import {useSetDrawerOpen} from '#/state/shell'
-import {useTheme} from '#/alf'
-import {Menu_Stroke2_Corner0_Rounded as Menu} from '#/components/icons/Menu'
-import {Text} from './text/Text'
-import {CenteredView} from './Views'
-
-const BACK_HITSLOP = {left: 20, top: 20, right: 50, bottom: 20}
+import {Header} from '#/components/Layout'
+/**
+ * Legacy ViewHeader component. Use Layout.Header going forward.
+ *
+ * @deprecated
+ */
export function ViewHeader({
title,
- subtitle,
- canGoBack,
- showBackButton = true,
- hideOnScroll,
- showOnDesktop,
- showBorder,
renderButton,
}: {
title: string
subtitle?: string
- canGoBack?: boolean
- showBackButton?: boolean
- hideOnScroll?: boolean
showOnDesktop?: boolean
showBorder?: boolean
renderButton?: () => JSX.Element
}) {
- const pal = usePalette('default')
- const {_} = useLingui()
- const setDrawerOpen = useSetDrawerOpen()
- const navigation = useNavigation()
- const {isDesktop, isTablet} = useWebMediaQueries()
- const t = useTheme()
-
- const onPressBack = React.useCallback(() => {
- if (navigation.canGoBack()) {
- navigation.goBack()
- } else {
- navigation.navigate('Home')
- }
- }, [navigation])
-
- const onPressMenu = React.useCallback(() => {
- setDrawerOpen(true)
- }, [setDrawerOpen])
-
- if (isDesktop) {
- if (showOnDesktop) {
- return (
-
- )
- }
- return null
- } else {
- if (typeof canGoBack === 'undefined') {
- canGoBack = navigation.canGoBack()
- }
-
- return (
-
-
-
- {showBackButton ? (
-
- {canGoBack ? (
-
- ) : !isTablet ? (
-
- ) : null}
-
- ) : null}
-
-
- {title}
-
-
- {renderButton ? (
- renderButton()
- ) : showBackButton ? (
-
- ) : null}
-
- {subtitle ? (
-
-
- {subtitle}
-
-
- ) : undefined}
-
-
- )
- }
-}
-
-function DesktopWebHeader({
- title,
- subtitle,
- renderButton,
- showBorder = true,
-}: {
- title: string
- subtitle?: string
- renderButton?: () => JSX.Element
- showBorder?: boolean
-}) {
- const pal = usePalette('default')
- const t = useTheme()
return (
-
-
-
-
- {title}
-
-
- {renderButton?.()}
-
- {subtitle ? (
-
-
-
- {subtitle}
-
-
-
- ) : null}
-
+
+
+
+ {title}
+
+ {renderButton?.() ?? null}
+
)
}
-
-function Container({
- children,
- hideOnScroll,
- showBorder,
-}: {
- children: React.ReactNode
- hideOnScroll: boolean
- showBorder?: boolean
-}) {
- const pal = usePalette('default')
- const headerMinimalShellTransform = useMinimalShellHeaderTransform()
-
- if (!hideOnScroll) {
- return (
-
- {children}
-
- )
- }
- return (
-
- {children}
-
- )
-}
-
-const styles = StyleSheet.create({
- header: {
- flexDirection: 'row',
- paddingHorizontal: 12,
- paddingVertical: 6,
- width: '100%',
- },
- headerFloating: {
- position: 'absolute',
- top: 0,
- width: '100%',
- },
- desktopHeader: {
- paddingVertical: 12,
- maxWidth: 600,
- marginLeft: 'auto',
- marginRight: 'auto',
- },
- border: {
- borderBottomWidth: StyleSheet.hairlineWidth,
- },
- titleContainer: {
- marginLeft: 'auto',
- marginRight: 'auto',
- alignItems: 'center',
- },
- title: {
- fontWeight: '600',
- },
- subtitle: {
- fontSize: 13,
- },
- subtitleDesktop: {
- fontSize: 15,
- },
- backBtn: {
- width: 30,
- height: 30,
- },
- backBtnWide: {
- width: 30,
- height: 30,
- paddingLeft: 4,
- marginRight: 4,
- },
- backIcon: {
- marginTop: 6,
- },
-})
diff --git a/src/view/com/util/Views.tsx b/src/view/com/util/Views.tsx
index 0d3f637947..c9ba0728cc 100644
--- a/src/view/com/util/Views.tsx
+++ b/src/view/com/util/Views.tsx
@@ -15,9 +15,16 @@ export type FlatList_INTERNAL = Omit<
FlatListComponent>,
'CellRendererComponent'
>
+
+/**
+ * @deprecated use `Layout` components
+ */
export const ScrollView = Animated.ScrollView
export type ScrollView = typeof Animated.ScrollView
+/**
+ * @deprecated use `Layout` components
+ */
export const CenteredView = forwardRef<
View,
React.PropsWithChildren<
diff --git a/src/view/com/util/Views.web.tsx b/src/view/com/util/Views.web.tsx
index 1f030b408c..e64b0ce9a2 100644
--- a/src/view/com/util/Views.web.tsx
+++ b/src/view/com/util/Views.web.tsx
@@ -31,10 +31,12 @@ interface AddedProps {
desktopFixedHeight?: boolean | number
}
+/**
+ * @deprecated use `Layout` components
+ */
export const CenteredView = React.forwardRef(function CenteredView(
{
style,
- sideBorders,
topBorder,
...props
}: React.PropsWithChildren<
@@ -47,13 +49,6 @@ export const CenteredView = React.forwardRef(function CenteredView(
if (!isMobile) {
style = addStyle(style, styles.container)
}
- if (sideBorders && !isMobile) {
- style = addStyle(style, {
- borderLeftWidth: StyleSheet.hairlineWidth,
- borderRightWidth: StyleSheet.hairlineWidth,
- })
- style = addStyle(style, pal.border)
- }
if (topBorder) {
style = addStyle(style, {
borderTopWidth: 1,
@@ -75,7 +70,6 @@ export const FlatList_INTERNAL = React.forwardRef(function FlatListImpl(
>,
ref: React.Ref>,
) {
- const pal = usePalette('default')
const {isMobile} = useWebMediaQueries()
if (!isMobile) {
contentContainerStyle = addStyle(
@@ -123,11 +117,7 @@ export const FlatList_INTERNAL = React.forwardRef(function FlatListImpl(
return (
(
)
})
+/**
+ * @deprecated use `Layout` components
+ */
export const ScrollView = React.forwardRef(function ScrollViewImpl(
{contentContainerStyle, ...props}: React.PropsWithChildren,
ref: React.Ref,
) {
- const pal = usePalette('default')
-
const {isMobile} = useWebMediaQueries()
if (!isMobile) {
contentContainerStyle = addStyle(
@@ -150,11 +141,7 @@ export const ScrollView = React.forwardRef(function ScrollViewImpl(
}
return (
- {showHeader && isMobile && }
+ {showHeader && isMobile && (
+
+ )}
@@ -102,7 +103,7 @@ type FlatlistSlice =
export function FeedsScreen(_props: Props) {
const pal = usePalette('default')
const {openComposer} = useComposerControls()
- const {isMobile, isTabletOrDesktop} = useWebMediaQueries()
+ const {isMobile} = useWebMediaQueries()
const [query, setQuery] = React.useState('')
const [isPTR, setIsPTR] = React.useState(false)
const {
@@ -374,22 +375,6 @@ export function FeedsScreen(_props: Props) {
isUserSearching,
])
- const renderHeaderBtn = React.useCallback(() => {
- return (
-
-
-
- )
- }, [pal, _])
-
const searchBarIndex = items.findIndex(
item => item.type === 'popularFeedsHeader',
)
@@ -430,36 +415,7 @@ export function FeedsScreen(_props: Props) {
)
} else if (item.type === 'savedFeedsHeader') {
- return (
- <>
- {!isMobile && (
-
-
- Feeds
-
-
-
-
-
- )}
-
- >
- )
+ return
} else if (item.type === 'savedFeedNoResults') {
return (
- {isMobile && (
-
- )}
+
+
+
+
+
+ Feeds
+
+
+
+
+
+
+
+
- item.key}
- contentContainerStyle={styles.contentContainer}
- renderItem={renderItem}
- refreshing={isPTR}
- onRefresh={isUserSearching ? undefined : onPullToRefresh}
- initialNumToRender={10}
- onEndReached={onEndReached}
- // @ts-ignore our .web version only -prf
- desktopFixedHeight
- scrollIndicatorInsets={{right: 1}}
- keyboardShouldPersistTaps="handled"
- keyboardDismissMode="on-drag"
- />
+ item.key}
+ contentContainerStyle={styles.contentContainer}
+ renderItem={renderItem}
+ refreshing={isPTR}
+ onRefresh={isUserSearching ? undefined : onPullToRefresh}
+ initialNumToRender={10}
+ onEndReached={onEndReached}
+ desktopFixedHeight
+ keyboardShouldPersistTaps="handled"
+ keyboardDismissMode="on-drag"
+ sideBorders={false}
+ />
+
{hasSession && (
-
+ My Feeds
@@ -754,7 +719,7 @@ function FeedsAboutHeader() {
size="lg"
/>
-
+ Discover New Feeds
@@ -769,9 +734,6 @@ function FeedsAboutHeader() {
}
const styles = StyleSheet.create({
- list: {
- height: '100%',
- },
contentContainer: {
paddingBottom: 100,
},
diff --git a/src/view/screens/Lists.tsx b/src/view/screens/Lists.tsx
index f654f2bd93..99abf06039 100644
--- a/src/view/screens/Lists.tsx
+++ b/src/view/screens/Lists.tsx
@@ -1,33 +1,26 @@
import React from 'react'
-import {StyleSheet, View} from 'react-native'
import {AtUri} from '@atproto/api'
-import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useFocusEffect, useNavigation} from '@react-navigation/native'
import {useEmail} from '#/lib/hooks/useEmail'
-import {usePalette} from '#/lib/hooks/usePalette'
-import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
import {NavigationProp} from '#/lib/routes/types'
-import {s} from '#/lib/styles'
import {useModalControls} from '#/state/modals'
import {useSetMinimalShellMode} from '#/state/shell'
import {MyLists} from '#/view/com/lists/MyLists'
-import {Button} from '#/view/com/util/forms/Button'
-import {SimpleViewHeader} from '#/view/com/util/SimpleViewHeader'
-import {Text} from '#/view/com/util/text/Text'
+import {atoms as a} from '#/alf'
+import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import {useDialogControl} from '#/components/Dialog'
import {VerifyEmailDialog} from '#/components/dialogs/VerifyEmailDialog'
+import {PlusLarge_Stroke2_Corner0_Rounded as PlusIcon} from '#/components/icons/Plus'
import * as Layout from '#/components/Layout'
type Props = NativeStackScreenProps
export function ListsScreen({}: Props) {
const {_} = useLingui()
- const pal = usePalette('default')
const setMinimalShellMode = useSetMinimalShellMode()
- const {isMobile} = useWebMediaQueries()
const navigation = useNavigation()
const {openModal} = useModalControls()
const {needsEmailVerification} = useEmail()
@@ -62,43 +55,30 @@ export function ListsScreen({}: Props) {
return (
-
-
-
- User Lists
-
-
+
+
+
+
+ Lists
+
+ Public, shareable lists which can drive feeds.
-
-
-
-
-
-
-
+
+
+
+
+
-
+
Blocked accounts cannot reply in your threads, mention you, or
@@ -120,7 +115,7 @@ export function ModerationBlockedAccounts({}: Props) {
{isEmpty ? (
-
+
{isError ? (
)}
-
+
)
}
const styles = StyleSheet.create({
- container: {
- flex: 1,
- paddingBottom: 100,
- },
- containerDesktop: {
- borderLeftWidth: 1,
- borderRightWidth: 1,
- paddingBottom: 0,
- },
title: {
textAlign: 'center',
marginTop: 12,
diff --git a/src/view/screens/ModerationModlists.tsx b/src/view/screens/ModerationModlists.tsx
index c623c5376f..0ef4d43896 100644
--- a/src/view/screens/ModerationModlists.tsx
+++ b/src/view/screens/ModerationModlists.tsx
@@ -1,33 +1,26 @@
import React from 'react'
-import {View} from 'react-native'
import {AtUri} from '@atproto/api'
-import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useFocusEffect, useNavigation} from '@react-navigation/native'
import {useEmail} from '#/lib/hooks/useEmail'
-import {usePalette} from '#/lib/hooks/usePalette'
-import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
import {NavigationProp} from '#/lib/routes/types'
-import {s} from '#/lib/styles'
import {useModalControls} from '#/state/modals'
import {useSetMinimalShellMode} from '#/state/shell'
import {MyLists} from '#/view/com/lists/MyLists'
-import {Button} from '#/view/com/util/forms/Button'
-import {SimpleViewHeader} from '#/view/com/util/SimpleViewHeader'
-import {Text} from '#/view/com/util/text/Text'
+import {atoms as a} from '#/alf'
+import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import {useDialogControl} from '#/components/Dialog'
import {VerifyEmailDialog} from '#/components/dialogs/VerifyEmailDialog'
+import {PlusLarge_Stroke2_Corner0_Rounded as PlusIcon} from '#/components/icons/Plus'
import * as Layout from '#/components/Layout'
type Props = NativeStackScreenProps
export function ModerationModlistsScreen({}: Props) {
const {_} = useLingui()
- const pal = usePalette('default')
const setMinimalShellMode = useSetMinimalShellMode()
- const {isMobile} = useWebMediaQueries()
const navigation = useNavigation()
const {openModal} = useModalControls()
const {needsEmailVerification} = useEmail()
@@ -62,39 +55,32 @@ export function ModerationModlistsScreen({}: Props) {
return (
-
-
-
+
+
+
+ Moderation Lists
-
-
+
+
Public, shareable lists of users to mute or block in bulk.
-
-
-
-
-
-
-
+
+
+
+
+
-
-
+
+
Muted accounts have their posts removed from your feed and from your
@@ -119,7 +114,7 @@ export function ModerationMutedAccounts({}: Props) {
{isEmpty ? (
-
+
{isError ? (
)}
-
+
)
}
const styles = StyleSheet.create({
- container: {
- flex: 1,
- paddingBottom: 100,
- },
- containerDesktop: {
- borderLeftWidth: 1,
- borderRightWidth: 1,
- paddingBottom: 0,
- },
title: {
textAlign: 'center',
marginTop: 12,
diff --git a/src/view/screens/Notifications.tsx b/src/view/screens/Notifications.tsx
index 531d10a7f8..35591f2700 100644
--- a/src/view/screens/Notifications.tsx
+++ b/src/view/screens/Notifications.tsx
@@ -1,4 +1,4 @@
-import React, {useCallback} from 'react'
+import React from 'react'
import {View} from 'react-native'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
@@ -6,7 +6,6 @@ import {useFocusEffect, useIsFocused} from '@react-navigation/native'
import {useQueryClient} from '@tanstack/react-query'
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
-import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {ComposeIcon2} from '#/lib/icons'
import {
NativeStackScreenProps,
@@ -14,7 +13,7 @@ import {
} from '#/lib/routes/types'
import {s} from '#/lib/styles'
import {logger} from '#/logger'
-import {isNative} from '#/platform/detection'
+import {isNative, isWeb} from '#/platform/detection'
import {emitSoftReset, listenSoftReset} from '#/state/events'
import {RQKEY as NOTIFS_RQKEY} from '#/state/queries/notifications/feed'
import {
@@ -29,28 +28,25 @@ import {FAB} from '#/view/com/util/fab/FAB'
import {ListMethods} from '#/view/com/util/List'
import {LoadLatestBtn} from '#/view/com/util/load-latest/LoadLatestBtn'
import {MainScrollProvider} from '#/view/com/util/MainScrollProvider'
-import {ViewHeader} from '#/view/com/util/ViewHeader'
-import {CenteredView} from '#/view/com/util/Views'
-import {atoms as a, useTheme} from '#/alf'
-import {Button} from '#/components/Button'
+import {atoms as a, useBreakpoints, useTheme} from '#/alf'
+import {Button, ButtonIcon} from '#/components/Button'
import {SettingsGear2_Stroke2_Corner0_Rounded as SettingsIcon} from '#/components/icons/SettingsGear2'
import * as Layout from '#/components/Layout'
import {Link} from '#/components/Link'
import {Loader} from '#/components/Loader'
-import {Text} from '#/components/Typography'
type Props = NativeStackScreenProps<
NotificationsTabNavigatorParams,
'Notifications'
>
export function NotificationsScreen({route: {params}}: Props) {
+ const t = useTheme()
+ const {gtTablet} = useBreakpoints()
const {_} = useLingui()
const setMinimalShellMode = useSetMinimalShellMode()
const [isScrolledDown, setIsScrolledDown] = React.useState(false)
const [isLoadingLatest, setIsLoadingLatest] = React.useState(false)
const scrollElRef = React.useRef(null)
- const t = useTheme()
- const {isDesktop} = useWebMediaQueries()
const queryClient = useQueryClient()
const unreadNotifs = useUnreadNotifications()
const unreadApi = useUnreadNotificationsApi()
@@ -110,121 +106,77 @@ export function NotificationsScreen({route: {params}}: Props) {
return listenSoftReset(onPressLoadLatest)
}, [onPressLoadLatest, isScreenFocused])
- const renderButton = useCallback(() => {
- return (
-
-
-
- )
- }, [_, t])
-
- const ListHeaderComponent = React.useCallback(() => {
- if (isDesktop) {
- return (
-
+ return (
+
+
+
+
-
- {isLoadingLatest ? : <>>}
- {renderButton()}
-
-
- )
- }
- return <>>
- }, [isDesktop, t, hasNew, renderButton, _, isLoadingLatest])
+
+
+
+
+
+
+
- const renderHeaderSpinner = React.useCallback(() => {
- return (
-
- {isLoadingLatest ? : <>>}
- {renderButton()}
-
- )
- }, [renderButton, isLoadingLatest])
-
- return (
-
-
-
+
-
-
-
- {(isScrolledDown || hasNew) && (
-
- )}
- openComposer({})}
- icon={}
- accessibilityRole="button"
- accessibilityLabel={_(msg`New post`)}
- accessibilityHint=""
+
+ {(isScrolledDown || hasNew) && (
+
-
+ )}
+ openComposer({})}
+ icon={}
+ accessibilityRole="button"
+ accessibilityLabel={_(msg`New post`)}
+ accessibilityHint=""
+ />
)
}
diff --git a/src/view/screens/PostThread.tsx b/src/view/screens/PostThread.tsx
index c183569b74..1bad9b6cdf 100644
--- a/src/view/screens/PostThread.tsx
+++ b/src/view/screens/PostThread.tsx
@@ -1,12 +1,10 @@
import React from 'react'
-import {View} from 'react-native'
import {useFocusEffect} from '@react-navigation/native'
import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
import {makeRecordUri} from '#/lib/strings/url-helpers'
import {useSetMinimalShellMode} from '#/state/shell'
import {PostThread as PostThreadComponent} from '#/view/com/post-thread/PostThread'
-import {atoms as a} from '#/alf'
import * as Layout from '#/components/Layout'
type Props = NativeStackScreenProps
@@ -24,9 +22,7 @@ export function PostThreadScreen({route}: Props) {
return (
-
-
-
+
)
}
diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx
index 677fe09f47..6a9b6f7f26 100644
--- a/src/view/screens/Profile.tsx
+++ b/src/view/screens/Profile.tsx
@@ -40,11 +40,9 @@ import {PagerWithHeader} from '#/view/com/pager/PagerWithHeader'
import {ErrorScreen} from '#/view/com/util/error/ErrorScreen'
import {FAB} from '#/view/com/util/fab/FAB'
import {ListRef} from '#/view/com/util/List'
-import {CenteredView} from '#/view/com/util/Views'
import {ProfileHeader, ProfileHeaderLoading} from '#/screens/Profile/Header'
import {ProfileFeedSection} from '#/screens/Profile/Sections/Feed'
import {ProfileLabelsSection} from '#/screens/Profile/Sections/Labels'
-import {web} from '#/alf'
import * as Layout from '#/components/Layout'
import {ScreenHider} from '#/components/moderation/ScreenHider'
import {ProfileStarterPacks} from '#/components/StarterPack/ProfileStarterPacks'
@@ -116,9 +114,9 @@ function ProfileScreenInner({route}: Props) {
// Most pushes will happen here, since we will have only placeholder data
if (isLoadingDid || isLoadingProfile || starterPacksQuery.isLoading) {
return (
-
+
-
+
)
}
if (resolveError || profileError) {
diff --git a/src/view/screens/ProfileFeed.tsx b/src/view/screens/ProfileFeed.tsx
index b34f0f1b01..63469ef4fb 100644
--- a/src/view/screens/ProfileFeed.tsx
+++ b/src/view/screens/ProfileFeed.tsx
@@ -49,7 +49,6 @@ import {LoadLatestBtn} from '#/view/com/util/load-latest/LoadLatestBtn'
import {LoadingScreen} from '#/view/com/util/LoadingScreen'
import {Text} from '#/view/com/util/text/Text'
import * as Toast from '#/view/com/util/Toast'
-import {CenteredView} from '#/view/com/util/Views'
import {atoms as a, useTheme} from '#/alf'
import {Button as NewButton, ButtonText} from '#/components/Button'
import {useRichText} from '#/components/hooks/useRichText'
@@ -98,7 +97,7 @@ export function ProfileFeedScreen(props: Props) {
if (error) {
return (
-
+ Could not load feed
@@ -120,7 +119,7 @@ export function ProfileFeedScreen(props: Props) {
-
+
)
}
@@ -394,7 +393,7 @@ export function ProfileFeedScreenInner({
])
return (
-
+ <>
)}
-
+ >
)
}
diff --git a/src/view/screens/ProfileFollowers.tsx b/src/view/screens/ProfileFollowers.tsx
index 9fa98cb1a8..90c0a57f97 100644
--- a/src/view/screens/ProfileFollowers.tsx
+++ b/src/view/screens/ProfileFollowers.tsx
@@ -10,7 +10,6 @@ import {ProfileFollowers as ProfileFollowersComponent} from '#/view/com/profile/
import {ViewHeader} from '#/view/com/util/ViewHeader'
import {CenteredView} from '#/view/com/util/Views'
import * as Layout from '#/components/Layout'
-import {ListHeaderDesktop} from '#/components/Lists'
type Props = NativeStackScreenProps
export const ProfileFollowersScreen = ({route}: Props) => {
@@ -27,7 +26,6 @@ export const ProfileFollowersScreen = ({route}: Props) => {
return (
-
diff --git a/src/view/screens/ProfileFollows.tsx b/src/view/screens/ProfileFollows.tsx
index 483ee93ecc..134f799937 100644
--- a/src/view/screens/ProfileFollows.tsx
+++ b/src/view/screens/ProfileFollows.tsx
@@ -10,7 +10,6 @@ import {ProfileFollows as ProfileFollowsComponent} from '#/view/com/profile/Prof
import {ViewHeader} from '#/view/com/util/ViewHeader'
import {CenteredView} from '#/view/com/util/Views'
import * as Layout from '#/components/Layout'
-import {ListHeaderDesktop} from '#/components/Lists'
type Props = NativeStackScreenProps
export const ProfileFollowsScreen = ({route}: Props) => {
@@ -27,7 +26,6 @@ export const ProfileFollowsScreen = ({route}: Props) => {
return (
-
diff --git a/src/view/screens/ProfileList.tsx b/src/view/screens/ProfileList.tsx
index cb333befa4..a927526ad3 100644
--- a/src/view/screens/ProfileList.tsx
+++ b/src/view/screens/ProfileList.tsx
@@ -69,7 +69,6 @@ import {LoadLatestBtn} from '#/view/com/util/load-latest/LoadLatestBtn'
import {LoadingScreen} from '#/view/com/util/LoadingScreen'
import {Text} from '#/view/com/util/text/Text'
import * as Toast from '#/view/com/util/Toast'
-import {CenteredView} from '#/view/com/util/Views'
import {ListHiddenScreen} from '#/screens/List/ListHiddenScreen'
import {atoms as a, useTheme} from '#/alf'
import {useDialogControl} from '#/components/Dialog'
@@ -107,20 +106,20 @@ function ProfileListScreenInner(props: Props) {
if (resolveError) {
return (
-
+
-
+
)
}
if (listError) {
return (
-
+
-
+
)
}
@@ -1010,7 +1009,6 @@ function ErrorScreen({error}: {error: string}) {
pal.view,
pal.border,
{
- marginTop: 10,
paddingHorizontal: 18,
paddingVertical: 14,
borderTopWidth: StyleSheet.hairlineWidth,
diff --git a/src/view/screens/SavedFeeds.tsx b/src/view/screens/SavedFeeds.tsx
index 3c04ec36fe..1b4c84a604 100644
--- a/src/view/screens/SavedFeeds.tsx
+++ b/src/view/screens/SavedFeeds.tsx
@@ -25,13 +25,12 @@ import {FeedSourceCard} from '#/view/com/feeds/FeedSourceCard'
import {TextLink} from '#/view/com/util/Link'
import {Text} from '#/view/com/util/text/Text'
import * as Toast from '#/view/com/util/Toast'
-import {ViewHeader} from '#/view/com/util/ViewHeader'
-import {CenteredView, ScrollView} from '#/view/com/util/Views'
import {NoFollowingFeed} from '#/screens/Feeds/NoFollowingFeed'
import {NoSavedFeedsOfAnyType} from '#/screens/Feeds/NoSavedFeedsOfAnyType'
import {atoms as a, useTheme} from '#/alf'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import {FilterTimeline_Stroke2_Corner0_Rounded as FilterTimeline} from '#/components/icons/FilterTimeline'
+import {FloppyDisk_Stroke2_Corner0_Rounded as SaveIcon} from '#/components/icons/FloppyDisk'
import * as Layout from '#/components/Layout'
import {Loader} from '#/components/Loader'
@@ -51,7 +50,7 @@ function SavedFeedsInner({
}) {
const pal = usePalette('default')
const {_} = useLingui()
- const {isMobile, isTabletOrDesktop, isDesktop} = useWebMediaQueries()
+ const {isMobile, isDesktop} = useWebMediaQueries()
const setMinimalShellMode = useSetMinimalShellMode()
const {mutateAsync: overwriteSavedFeeds, isPending: isOverwritePending} =
useOverwriteSavedFeedsMutation()
@@ -88,136 +87,128 @@ function SavedFeedsInner({
}
}, [_, overwriteSavedFeeds, currentFeeds, navigation])
- const renderHeaderBtn = React.useCallback(() => {
- return (
-
- )
- }, [_, isDesktop, onSaveChanges, hasUnsavedChanges, isOverwritePending])
-
return (
-
-
-
- {noSavedFeedsOfAnyType && (
-
-
+
+
+
+
+ Feeds
+
+
+
+
+
+
+ {noSavedFeedsOfAnyType && (
+
+
+
+ )}
+
+
+
+ Pinned Feeds
+
+
+
+ {preferences ? (
+ !pinnedFeeds.length ? (
+
+
+ You don't have any pinned feeds.
+
- )}
-
-
-
- Pinned Feeds
-
-
-
- {preferences ? (
- !pinnedFeeds.length ? (
-
-
- You don't have any pinned feeds.
-
-
- ) : (
- pinnedFeeds.map(f => (
-
- ))
- )
) : (
-
- )}
+ pinnedFeeds.map(f => (
+
+ ))
+ )
+ ) : (
+
+ )}
- {noFollowingFeed && (
-
-
+ {noFollowingFeed && (
+
+
+
+ )}
+
+
+
+ Saved Feeds
+
+
+ {preferences ? (
+ !unpinnedFeeds.length ? (
+
+
+ You don't have any saved feeds.
+
- )}
-
-
-
- Saved Feeds
-
-
- {preferences ? (
- !unpinnedFeeds.length ? (
-
-
- You don't have any saved feeds.
-
-
- ) : (
- unpinnedFeeds.map(f => (
-
- ))
- )
) : (
-
- )}
+ unpinnedFeeds.map(f => (
+
+ ))
+ )
+ ) : (
+
+ )}
-
-
-
- Feeds are custom algorithms that users build with a little
- coding expertise.{' '}
- {' '}
- for more information.
-
-
-
-
-
-
+
+
+
+ Feeds are custom algorithms that users build with a little coding
+ expertise.{' '}
+ {' '}
+ for more information.
+
+
+
+
)
}
@@ -456,7 +447,6 @@ const styles = StyleSheet.create({
},
footerText: {
paddingHorizontal: 26,
- paddingTop: 22,
- paddingBottom: 100,
+ paddingVertical: 22,
},
})
diff --git a/src/view/screens/Search/Search.tsx b/src/view/screens/Search/Search.tsx
index 0518bc5064..0871458c9a 100644
--- a/src/view/screens/Search/Search.tsx
+++ b/src/view/screens/Search/Search.tsx
@@ -55,7 +55,6 @@ import {ProfileCardWithFollowBtn} from '#/view/com/profile/ProfileCard'
import {Link} from '#/view/com/util/Link'
import {List} from '#/view/com/util/List'
import {Text} from '#/view/com/util/text/Text'
-import {CenteredView, ScrollView} from '#/view/com/util/Views'
import {Explore} from '#/view/screens/Search/Explore'
import {SearchLinkCard, SearchProfileCard} from '#/view/shell/desktop/Search'
import {makeSearchQuery, parseSearchQuery} from '#/screens/Search/utils'
@@ -68,63 +67,46 @@ import {Menu_Stroke2_Corner0_Rounded as Menu} from '#/components/icons/Menu'
import * as Layout from '#/components/Layout'
function Loader() {
- const pal = usePalette('default')
- const {isMobile} = useWebMediaQueries()
return (
-
-
-
+
+
+
+
+
)
}
function EmptyState({message, error}: {message: string; error?: string}) {
const pal = usePalette('default')
- const {isMobile} = useWebMediaQueries()
return (
-
-
- {message}
+
+
+
+ {message}
- {error && (
- <>
-
+ {error && (
+ <>
+
-
- Error: {error}
-
- >
- )}
+
+ Error: {error}
+
+ >
+ )}
+
-
+
)
}
@@ -224,7 +206,7 @@ let SearchScreenPostResults = ({
if (item.type === 'post') {
return
} else {
- return
+ return null
}
}}
keyExtractor={item => item.key}
@@ -550,19 +532,13 @@ let SearchScreenInner = ({
(
- section.title)} {...props} />
-
+
)}
initialPage={0}>
{sections.map((section, i) => (
@@ -572,7 +548,7 @@ let SearchScreenInner = ({
) : hasSession ? (
) : (
-
+
-
+
)
}
SearchScreenInner = React.memo(SearchScreenInner)
@@ -650,7 +626,7 @@ export function SearchScreen(
* Arbitrary sizing, so guess and check, used for sticky header alignment and
* sizing.
*/
- const headerHeight = 64 + (showFilters ? 40 : 0)
+ const headerHeight = 60 + (showFilters ? 40 : 0)
useFocusEffect(
useNonReactiveCallback(() => {
@@ -861,73 +837,79 @@ export function SearchScreen(
return (
-
-
- {!gtMobile && (
-
- )}
-
-
-
- {showAutocomplete && (
-
- )}
-
-
- {showFilters && (
-
-
-
+ ]}>
+
+
+
+ {!gtMobile && (
+
+ )}
+
+
+
+ {showAutocomplete && (
+
+ )}
+
+ {showFilters && (
+
+
+
+
+
+ )}
- )}
-
+
+
) : (
-
))}
-
+
)}
>
)
@@ -1042,17 +1021,12 @@ function SearchHistory({
onRemoveItemClick: (item: string) => void
onRemoveProfileClick: (profile: AppBskyActorDefs.ProfileViewBasic) => void
}) {
- const {isTabletOrDesktop, isMobile} = useWebMediaQueries()
+ const {isMobile} = useWebMediaQueries()
const pal = usePalette('default')
const {_} = useLingui()
return (
-
+
{(searchHistory.length > 0 || selectedProfiles.length > 0) && (
@@ -1152,7 +1126,7 @@ function SearchHistory({
)}
-
+
)
}
diff --git a/src/view/shell/Composer.web.tsx b/src/view/shell/Composer.web.tsx
index 9f407248a3..47a86ed248 100644
--- a/src/view/shell/Composer.web.tsx
+++ b/src/view/shell/Composer.web.tsx
@@ -3,8 +3,8 @@ import {StyleSheet, View} from 'react-native'
import {DismissableLayer} from '@radix-ui/react-dismissable-layer'
import {useFocusGuards} from '@radix-ui/react-focus-guards'
import {FocusScope} from '@radix-ui/react-focus-scope'
+import {RemoveScrollBar} from 'react-remove-scroll-bar'
-import {useWebBodyScrollLock} from '#/lib/hooks/useWebBodyScrollLock'
import {useModals} from '#/state/modals'
import {ComposerOpts, useComposerState} from '#/state/shell/composer'
import {
@@ -20,8 +20,6 @@ export function Composer({}: {winHeight: number}) {
const state = useComposerState()
const isActive = !!state
- useWebBodyScrollLock(isActive)
-
// rendering
// =
@@ -29,7 +27,12 @@ export function Composer({}: {winHeight: number}) {
return
}
- return
+ return (
+ <>
+
+
+ >
+ )
}
function Inner({state}: {state: ComposerOpts}) {
diff --git a/src/view/shell/desktop/LeftNav.tsx b/src/view/shell/desktop/LeftNav.tsx
index 0af80854cf..7c2ccd958a 100644
--- a/src/view/shell/desktop/LeftNav.tsx
+++ b/src/view/shell/desktop/LeftNav.tsx
@@ -1,9 +1,6 @@
import React from 'react'
-import {StyleSheet, TouchableOpacity, View} from 'react-native'
-import {
- FontAwesomeIcon,
- FontAwesomeIconStyle,
-} from '@fortawesome/react-native-fontawesome'
+import {StyleSheet, View} from 'react-native'
+import {FontAwesomeIconStyle} from '@fortawesome/react-native-fontawesome'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {
@@ -14,9 +11,9 @@ import {
import {usePalette} from '#/lib/hooks/usePalette'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
-import {getCurrentRoute, isStateAtTabRoot, isTab} from '#/lib/routes/helpers'
+import {getCurrentRoute, isTab} from '#/lib/routes/helpers'
import {makeProfileLink} from '#/lib/routes/links'
-import {CommonNavigatorParams, NavigationProp} from '#/lib/routes/types'
+import {CommonNavigatorParams} from '#/lib/routes/types'
import {isInvalidHandle} from '#/lib/strings/handles'
import {emitSoftReset} from '#/state/events'
import {useFetchHandle} from '#/state/queries/handle'
@@ -101,47 +98,6 @@ function ProfileCard() {
)
}
-const HIDDEN_BACK_BNT_ROUTES = ['StarterPackWizard', 'StarterPackEdit']
-
-function BackBtn() {
- const {isTablet} = useWebMediaQueries()
- const pal = usePalette('default')
- const navigation = useNavigation()
- const {_} = useLingui()
- const shouldShow = useNavigationState(
- state =>
- !isStateAtTabRoot(state) &&
- !HIDDEN_BACK_BNT_ROUTES.includes(getCurrentRoute(state).name),
- )
-
- const onPressBack = React.useCallback(() => {
- if (navigation.canGoBack()) {
- navigation.goBack()
- } else {
- navigation.navigate('Home')
- }
- }, [navigation])
-
- if (!shouldShow || isTablet) {
- return <>>
- }
- return (
-
-
-
- )
-}
-
interface NavItemProps {
count?: string
href: string
@@ -220,35 +176,44 @@ function NavItem({count, href, icon, iconFilled, label}: NavItemProps) {
]}>
{isCurrent ? iconFilled : icon}
{typeof count === 'string' && count ? (
-
- {count}
-
+
+ {count}
+
+
) : null}
{gtTablet && (
@@ -366,9 +331,9 @@ export function DesktopLeftNav() {
{hasSession ? (
@@ -381,8 +346,6 @@ export function DesktopLeftNav() {
{hasSession && (
<>
-
-
+
{routeName === 'Search' ? (
@@ -122,8 +122,13 @@ const styles = StyleSheet.create({
// @ts-ignore web only
position: 'fixed',
// @ts-ignore web only
- left: 'calc(50vw + 300px + 20px)',
- width: 300,
+ left: '50%',
+ transform: [
+ {
+ translateX: 300,
+ },
+ ...a.scrollbar_offset.transform,
+ ],
maxHeight: '100%',
overflowY: 'auto',
},
diff --git a/src/view/shell/index.web.tsx b/src/view/shell/index.web.tsx
index f554373562..8c30813ab5 100644
--- a/src/view/shell/index.web.tsx
+++ b/src/view/shell/index.web.tsx
@@ -3,10 +3,10 @@ import {StyleSheet, TouchableWithoutFeedback, View} from 'react-native'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useNavigation} from '@react-navigation/native'
+import {RemoveScrollBar} from 'react-remove-scroll-bar'
import {useColorSchemeStyle} from '#/lib/hooks/useColorSchemeStyle'
import {useIntentHandler} from '#/lib/hooks/useIntentHandler'
-import {useWebBodyScrollLock} from '#/lib/hooks/useWebBodyScrollLock'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {NavigationProp} from '#/lib/routes/types'
import {colors} from '#/lib/styles'
@@ -34,7 +34,6 @@ function ShellInner() {
const {_} = useLingui()
const showDrawer = !isDesktop && isDrawerOpen
- useWebBodyScrollLock(showDrawer)
useComposerKeyboardShortcut()
useIntentHandler()
@@ -58,31 +57,34 @@ function ShellInner() {
{showDrawer && (
- {
- // Only close if press happens outside of the drawer
- if (ev.target === ev.currentTarget) {
- setDrawerOpen(false)
- }
- }}
- accessibilityLabel={_(msg`Close navigation footer`)}
- accessibilityHint={_(msg`Closes bottom navigation bar`)}>
-
-
-
+ <>
+
+ {
+ // Only close if press happens outside of the drawer
+ if (ev.target === ev.currentTarget) {
+ setDrawerOpen(false)
+ }
+ }}
+ accessibilityLabel={_(msg`Close navigation footer`)}
+ accessibilityHint={_(msg`Closes bottom navigation bar`)}>
+
+
+
+
-
-
+
+ >
)}
>
)
diff --git a/web/index.html b/web/index.html
index 28b3a3e3d7..293f366adc 100644
--- a/web/index.html
+++ b/web/index.html
@@ -45,7 +45,6 @@
}
html {
background-color: white;
- scrollbar-gutter: stable both-edges;
}
@media (prefers-color-scheme: dark) {
html {
@@ -81,9 +80,15 @@
top: 50%;
transform: translateX(-50%) translateY(-50%) translateY(-50px);
}
- /* We need this style to prevent web dropdowns from shifting the display when opening */
+ /**
+ * We need these styles to prevent shifting due to scrollbar show/hide on
+ * OSs that have them enabled by default. This also handles cases where the
+ * screen wouldn't otherwise scroll, and therefore hide the scrollbar and
+ * shift the content, by forcing the page to show a scrollbar.
+ */
body {
width: 100%;
+ overflow-y: scroll;
}
diff --git a/yarn.lock b/yarn.lock
index 474ef2f8ba..e62dcb97ff 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -17616,16 +17616,7 @@ string-natural-compare@^3.0.1:
resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4"
integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==
-"string-width-cjs@npm:string-width@^4.2.0":
- version "4.2.3"
- resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
- integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
- dependencies:
- emoji-regex "^8.0.0"
- is-fullwidth-code-point "^3.0.0"
- strip-ansi "^6.0.1"
-
-string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
+"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -17725,7 +17716,7 @@ string_decoder@~1.1.1:
dependencies:
safe-buffer "~5.1.0"
-"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
+"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -17739,13 +17730,6 @@ strip-ansi@^5.0.0, strip-ansi@^5.2.0:
dependencies:
ansi-regex "^4.1.0"
-strip-ansi@^6.0.0, strip-ansi@^6.0.1:
- version "6.0.1"
- resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
- integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
- dependencies:
- ansi-regex "^5.0.1"
-
strip-ansi@^7.0.1:
version "7.1.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
@@ -19068,7 +19052,7 @@ wordwrap@^1.0.0:
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==
-"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
+"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
@@ -19086,15 +19070,6 @@ wrap-ansi@^6.2.0:
string-width "^4.1.0"
strip-ansi "^6.0.0"
-wrap-ansi@^7.0.0:
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
- integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
- dependencies:
- ansi-styles "^4.0.0"
- string-width "^4.1.0"
- strip-ansi "^6.0.0"
-
wrap-ansi@^8.0.1, wrap-ansi@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"