diff options
Diffstat (limited to 'src/view')
60 files changed, 746 insertions, 452 deletions
diff --git a/src/view/com/auth/onboarding/WelcomeDesktop.tsx b/src/view/com/auth/onboarding/WelcomeDesktop.tsx index e63693443..7b7555ace 100644 --- a/src/view/com/auth/onboarding/WelcomeDesktop.tsx +++ b/src/view/com/auth/onboarding/WelcomeDesktop.tsx @@ -16,9 +16,7 @@ type Props = { export const WelcomeDesktop = observer(({next}: Props) => { const pal = usePalette('default') - const horizontal = useMediaQuery({ - query: '(min-width: 1230px)', - }) + const horizontal = useMediaQuery({minWidth: 1300}) const title = ( <> <Text diff --git a/src/view/com/auth/onboarding/WelcomeMobile.tsx b/src/view/com/auth/onboarding/WelcomeMobile.tsx index eb72de836..0f627ad0b 100644 --- a/src/view/com/auth/onboarding/WelcomeMobile.tsx +++ b/src/view/com/auth/onboarding/WelcomeMobile.tsx @@ -7,7 +7,6 @@ import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {Button} from 'view/com/util/forms/Button' import {observer} from 'mobx-react-lite' import {ViewHeader} from 'view/com/util/ViewHeader' -import {isDesktopWeb} from 'platform/detection' type Props = { next: () => void @@ -95,7 +94,7 @@ export const WelcomeMobile = observer(({next, skip}: Props) => { const styles = StyleSheet.create({ container: { flex: 1, - marginBottom: isDesktopWeb ? 30 : 60, + marginBottom: 60, marginHorizontal: 16, justifyContent: 'space-between', }, diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx index c801c47bf..8ed0bb378 100644 --- a/src/view/com/composer/Composer.tsx +++ b/src/view/com/composer/Composer.tsx @@ -37,9 +37,10 @@ import {toShortUrl} from 'lib/strings/url-helpers' import {SelectPhotoBtn} from './photos/SelectPhotoBtn' import {OpenCameraBtn} from './photos/OpenCameraBtn' import {usePalette} from 'lib/hooks/usePalette' -import QuoteEmbed from '../util/post-embeds/QuoteEmbed' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {useExternalLinkFetch} from './useExternalLinkFetch' -import {isDesktopWeb, isAndroid, isIOS} from 'platform/detection' +import {isWeb, isNative, isAndroid, isIOS} from 'platform/detection' +import QuoteEmbed from '../util/post-embeds/QuoteEmbed' import {GalleryModel} from 'state/models/media/gallery' import {Gallery} from './photos/Gallery' import {MAX_GRAPHEME_LENGTH} from 'lib/constants' @@ -61,6 +62,7 @@ export const ComposePost = observer(function ComposePost({ }: Props) { const {track} = useAnalytics() const pal = usePalette('default') + const {isDesktop, isMobile} = useWebMediaQueries() const store = useStores() const textInput = useRef<TextInputRef>(null) const [isKeyboardVisible] = useIsKeyboardVisible({iosUseWillEvents: true}) @@ -99,9 +101,9 @@ export const ComposePost = observer(function ComposePost({ () => ({ paddingBottom: isAndroid || (isIOS && !isKeyboardVisible) ? insets.bottom : 0, - paddingTop: isAndroid ? insets.top : isDesktopWeb ? 0 : 15, + paddingTop: isAndroid ? insets.top : isMobile ? 15 : 0, }), - [insets, isKeyboardVisible], + [insets, isKeyboardVisible, isMobile], ) const onPressCancel = useCallback(() => { @@ -143,7 +145,7 @@ export const ComposePost = observer(function ComposePost({ [onPressCancel], ) useEffect(() => { - if (isDesktopWeb) { + if (isWeb) { window.addEventListener('keydown', onEscape) return () => window.removeEventListener('keydown', onEscape) } @@ -240,7 +242,7 @@ export const ComposePost = observer(function ComposePost({ behavior={Platform.OS === 'ios' ? 'padding' : 'height'} style={styles.outer}> <View style={[s.flex1, viewStyles]} aria-modal accessibilityViewIsModal> - <View style={styles.topbar}> + <View style={[styles.topbar, isDesktop && styles.topbarDesktop]}> <TouchableOpacity testID="composerDiscardButton" onPress={onPressCancel} @@ -334,7 +336,12 @@ export const ComposePost = observer(function ComposePost({ </View> ) : undefined} - <View style={[pal.border, styles.textInputLayout]}> + <View + style={[ + pal.border, + styles.textInputLayout, + isNative && styles.textInputLayoutMobile, + ]}> <UserAvatar avatar={store.me.avatar} size={50} /> <TextInput ref={textInput} @@ -362,7 +369,7 @@ export const ComposePost = observer(function ComposePost({ /> )} {quote ? ( - <View style={s.mt5}> + <View style={[s.mt5, isWeb && s.mb10]}> <QuoteEmbed quote={quote} /> </View> ) : undefined} @@ -395,7 +402,7 @@ export const ComposePost = observer(function ComposePost({ <OpenCameraBtn gallery={gallery} /> </> ) : null} - {isDesktopWeb ? <EmojiPickerButton /> : null} + {isDesktop ? <EmojiPickerButton /> : null} <View style={s.flex1} /> <SelectLangBtn /> <CharProgress count={graphemeLength} /> @@ -414,11 +421,14 @@ const styles = StyleSheet.create({ topbar: { flexDirection: 'row', alignItems: 'center', - paddingTop: isDesktopWeb ? 10 : undefined, - paddingBottom: isDesktopWeb ? 10 : 4, + paddingBottom: 4, paddingHorizontal: 20, height: 55, }, + topbarDesktop: { + paddingTop: 10, + paddingBottom: 10, + }, postBtn: { borderRadius: 20, paddingHorizontal: 20, @@ -465,11 +475,13 @@ const styles = StyleSheet.create({ paddingHorizontal: 15, }, textInputLayout: { - flex: isDesktopWeb ? undefined : 1, flexDirection: 'row', borderTopWidth: 1, paddingTop: 16, }, + textInputLayoutMobile: { + flex: 1, + }, replyToLayout: { flexDirection: 'row', borderTopWidth: 1, diff --git a/src/view/com/composer/Prompt.tsx b/src/view/com/composer/Prompt.tsx index 98a10b0f5..01fd7d1fa 100644 --- a/src/view/com/composer/Prompt.tsx +++ b/src/view/com/composer/Prompt.tsx @@ -4,11 +4,12 @@ import {UserAvatar} from '../util/UserAvatar' import {Text} from '../util/text/Text' import {usePalette} from 'lib/hooks/usePalette' import {useStores} from 'state/index' -import {isDesktopWeb} from 'platform/detection' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' export function ComposePrompt({onPressCompose}: {onPressCompose: () => void}) { const store = useStores() const pal = usePalette('default') + const {isDesktop} = useWebMediaQueries() return ( <TouchableOpacity testID="replyPromptBtn" @@ -22,7 +23,7 @@ export function ComposePrompt({onPressCompose}: {onPressCompose: () => void}) { type="xl" style={[ pal.text, - isDesktopWeb ? styles.labelDesktopWeb : styles.labelMobile, + isDesktop ? styles.labelDesktopWeb : styles.labelMobile, ]}> Write your reply </Text> diff --git a/src/view/com/composer/photos/Gallery.tsx b/src/view/com/composer/photos/Gallery.tsx index 6dba2f011..d5465f79a 100644 --- a/src/view/com/composer/photos/Gallery.tsx +++ b/src/view/com/composer/photos/Gallery.tsx @@ -7,10 +7,10 @@ import {s, colors} from 'lib/styles' import {StyleSheet, TouchableOpacity, View} from 'react-native' import {Image} from 'expo-image' import {Text} from 'view/com/util/text/Text' -import {isDesktopWeb} from 'platform/detection' import {openAltTextModal} from 'lib/media/alt-text' import {useStores} from 'state/index' import {usePalette} from 'lib/hooks/usePalette' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' interface Props { gallery: GalleryModel @@ -19,13 +19,14 @@ interface Props { export const Gallery = observer(function ({gallery}: Props) { const store = useStores() const pal = usePalette('default') + const {isMobile} = useWebMediaQueries() let side: number if (gallery.size === 1) { side = 250 } else { - side = (isDesktopWeb ? 560 : 350) / gallery.size + side = (isMobile ? 350 : 560) / gallery.size } const imageStyle = { @@ -33,14 +34,14 @@ export const Gallery = observer(function ({gallery}: Props) { width: side, } - const isOverflow = !isDesktopWeb && gallery.size > 2 + const isOverflow = isMobile && gallery.size > 2 const altTextControlStyle = isOverflow ? { left: 4, bottom: 4, } - : isDesktopWeb && gallery.size < 3 + : !isMobile && gallery.size < 3 ? { left: 8, top: 8, @@ -60,7 +61,7 @@ export const Gallery = observer(function ({gallery}: Props) { right: 4, gap: 4, } - : isDesktopWeb && gallery.size < 3 + : !isMobile && gallery.size < 3 ? { top: 8, right: 8, diff --git a/src/view/com/lists/ListItems.tsx b/src/view/com/lists/ListItems.tsx index 7f2173d78..d611bc504 100644 --- a/src/view/com/lists/ListItems.tsx +++ b/src/view/com/lists/ListItems.tsx @@ -22,9 +22,9 @@ import {TextLink} from '../util/Link' import {ListModel} from 'state/models/content/list' import {useAnalytics} from 'lib/analytics/analytics' import {usePalette} from 'lib/hooks/usePalette' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {useStores} from 'state/index' import {s} from 'lib/styles' -import {isDesktopWeb} from 'platform/detection' import {ListActions} from './ListActions' import {makeProfileLink} from 'lib/routes/links' import {sanitizeHandle} from 'lib/strings/handles' @@ -283,6 +283,7 @@ const ListHeader = observer( }) => { const pal = usePalette('default') const store = useStores() + const {isDesktop} = useWebMediaQueries() const descriptionRT = React.useMemo( () => list?.description && @@ -318,7 +319,7 @@ const ListHeader = observer( richText={descriptionRT} /> )} - {isDesktopWeb && ( + {isDesktop && ( <ListActions isOwner={isOwner} muted={list.viewer?.muted} @@ -334,7 +335,8 @@ const ListHeader = observer( <UserAvatar type="list" avatar={list.avatar} size={64} /> </View> </View> - <View style={[styles.fakeSelector, pal.border]}> + <View + style={{flexDirection: 'row', paddingHorizontal: isDesktop ? 16 : 6}}> <View style={[styles.fakeSelectorItem, {borderColor: pal.colors.link}]}> <Text type="md-medium" style={[pal.text]}> @@ -365,10 +367,6 @@ const styles = StyleSheet.create({ gap: 8, marginTop: 12, }, - fakeSelector: { - flexDirection: 'row', - paddingHorizontal: isDesktopWeb ? 16 : 6, - }, fakeSelectorItem: { paddingHorizontal: 12, paddingBottom: 8, diff --git a/src/view/com/modals/AddAppPasswords.tsx b/src/view/com/modals/AddAppPasswords.tsx index 6117924ae..2a8672131 100644 --- a/src/view/com/modals/AddAppPasswords.tsx +++ b/src/view/com/modals/AddAppPasswords.tsx @@ -5,7 +5,7 @@ import {Button} from '../util/forms/Button' import {s} from 'lib/styles' import {useStores} from 'state/index' import {usePalette} from 'lib/hooks/usePalette' -import {isDesktopWeb} from 'platform/detection' +import {isNative} from 'platform/detection' import { FontAwesomeIcon, FontAwesomeIconStyle, @@ -205,7 +205,7 @@ export function Component({}: {}) { const styles = StyleSheet.create({ container: { flex: 1, - paddingBottom: isDesktopWeb ? 0 : 50, + paddingBottom: isNative ? 50 : 0, paddingHorizontal: 16, }, textInputWrapper: { diff --git a/src/view/com/modals/AltImage.tsx b/src/view/com/modals/AltImage.tsx index e1145a0fe..c084e84a3 100644 --- a/src/view/com/modals/AltImage.tsx +++ b/src/view/com/modals/AltImage.tsx @@ -18,7 +18,7 @@ import {useTheme} from 'lib/ThemeContext' import {Text} from '../util/text/Text' import LinearGradient from 'react-native-linear-gradient' import {useStores} from 'state/index' -import {isDesktopWeb, isAndroid} from 'platform/detection' +import {isAndroid, isWeb} from 'platform/detection' import {ImageModel} from 'state/models/media/image' export const snapPoints = ['fullscreen'] @@ -35,7 +35,7 @@ export function Component({image}: Props) { const windim = useWindowDimensions() const imageStyles = useMemo<ImageStyle>(() => { - const maxWidth = isDesktopWeb ? 450 : windim.width + const maxWidth = isWeb ? 450 : windim.width if (image.height > image.width) { return { resizeMode: 'contain', @@ -137,12 +137,12 @@ const styles = StyleSheet.create({ flex: 1, height: '100%', width: '100%', - paddingVertical: isDesktopWeb ? 0 : 18, + paddingVertical: isWeb ? 0 : 18, }, scrollContainer: { flex: 1, height: '100%', - paddingHorizontal: isDesktopWeb ? 0 : 12, + paddingHorizontal: isWeb ? 0 : 12, }, scrollInner: { gap: 12, diff --git a/src/view/com/modals/Confirm.tsx b/src/view/com/modals/Confirm.tsx index f9bc0de14..270177182 100644 --- a/src/view/com/modals/Confirm.tsx +++ b/src/view/com/modals/Confirm.tsx @@ -11,7 +11,7 @@ import {s, colors} from 'lib/styles' import {ErrorMessage} from '../util/error/ErrorMessage' import {cleanError} from 'lib/strings/errors' import {usePalette} from 'lib/hooks/usePalette' -import {isDesktopWeb} from 'platform/detection' +import {isWeb} from 'platform/detection' import type {ConfirmModal} from 'state/models/ui/shell' export const snapPoints = ['50%'] @@ -96,7 +96,7 @@ const styles = StyleSheet.create({ container: { flex: 1, padding: 10, - paddingBottom: isDesktopWeb ? 0 : 60, + paddingBottom: isWeb ? 0 : 60, }, title: { textAlign: 'center', diff --git a/src/view/com/modals/ContentFilteringSettings.tsx b/src/view/com/modals/ContentFilteringSettings.tsx index f39351feb..588b21353 100644 --- a/src/view/com/modals/ContentFilteringSettings.tsx +++ b/src/view/com/modals/ContentFilteringSettings.tsx @@ -11,13 +11,15 @@ import {TextLink} from '../util/Link' import {ToggleButton} from '../util/forms/ToggleButton' import {usePalette} from 'lib/hooks/usePalette' import {CONFIGURABLE_LABEL_GROUPS} from 'lib/labeling/const' -import {isDesktopWeb, isIOS} from 'platform/detection' +import {isIOS} from 'platform/detection' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import * as Toast from '../util/Toast' export const snapPoints = ['90%'] export const Component = observer(({}: {}) => { const store = useStores() + const {isMobile} = useWebMediaQueries() const pal = usePalette('default') React.useEffect(() => { @@ -88,9 +90,14 @@ export const Component = observer(({}: {}) => { <ContentLabelPref group="hate" /> <ContentLabelPref group="spam" /> <ContentLabelPref group="impersonation" /> - <View style={styles.bottomSpacer} /> + <View style={{height: isMobile ? 60 : 0}} /> </ScrollView> - <View style={[styles.btnContainer, pal.borderDark]}> + <View + style={[ + styles.btnContainer, + isMobile && styles.btnContainerMobile, + pal.borderDark, + ]}> <Pressable testID="sendReportBtn" onPress={onPressDone} @@ -259,14 +266,13 @@ const styles = StyleSheet.create({ flex: 1, paddingHorizontal: 10, }, - bottomSpacer: { - height: isDesktopWeb ? 0 : 60, - }, btnContainer: { paddingTop: 10, paddingHorizontal: 10, - paddingBottom: isDesktopWeb ? 0 : 40, - borderTopWidth: isDesktopWeb ? 0 : 1, + }, + btnContainerMobile: { + paddingBottom: 40, + borderTopWidth: 1, }, contentLabelPref: { diff --git a/src/view/com/modals/CreateOrEditMuteList.tsx b/src/view/com/modals/CreateOrEditMuteList.tsx index 09048b5db..3f3cfc5f0 100644 --- a/src/view/com/modals/CreateOrEditMuteList.tsx +++ b/src/view/com/modals/CreateOrEditMuteList.tsx @@ -22,8 +22,8 @@ import {UserAvatar} from '../util/UserAvatar' import {usePalette} from 'lib/hooks/usePalette' import {useTheme} from 'lib/ThemeContext' import {useAnalytics} from 'lib/analytics/analytics' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {cleanError, isNetworkError} from 'lib/strings/errors' -import {isDesktopWeb} from 'platform/detection' const MAX_NAME = 64 // todo const MAX_DESCRIPTION = 300 // todo @@ -38,6 +38,7 @@ export function Component({ list?: ListModel }) { const store = useStores() + const {isMobile} = useWebMediaQueries() const [error, setError] = useState<string>('') const pal = usePalette('default') const theme = useTheme() @@ -130,7 +131,12 @@ export function Component({ return ( <KeyboardAvoidingView behavior="height"> <ScrollView - style={[pal.view, styles.container]} + style={[ + pal.view, + { + paddingHorizontal: isMobile ? 16 : 0, + }, + ]} testID="createOrEditMuteListModal"> <Text style={[styles.title, pal.text]}> {list ? 'Edit Mute List' : 'New Mute List'} @@ -226,9 +232,6 @@ export function Component({ } const styles = StyleSheet.create({ - container: { - paddingHorizontal: isDesktopWeb ? 0 : 16, - }, title: { textAlign: 'center', fontWeight: 'bold', diff --git a/src/view/com/modals/DeleteAccount.tsx b/src/view/com/modals/DeleteAccount.tsx index b4933a1f2..98482457c 100644 --- a/src/view/com/modals/DeleteAccount.tsx +++ b/src/view/com/modals/DeleteAccount.tsx @@ -13,10 +13,10 @@ import {useStores} from 'state/index' import {s, colors, gradients} from 'lib/styles' import {usePalette} from 'lib/hooks/usePalette' import {useTheme} from 'lib/ThemeContext' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {ErrorMessage} from '../util/error/ErrorMessage' import {cleanError} from 'lib/strings/errors' import {resetToTab} from '../../../Navigation' -import {isDesktopWeb} from 'platform/detection' export const snapPoints = ['60%'] @@ -24,6 +24,7 @@ export function Component({}: {}) { const pal = usePalette('default') const theme = useTheme() const store = useStores() + const {isMobile} = useWebMediaQueries() const [isEmailSent, setIsEmailSent] = React.useState<boolean>(false) const [confirmCode, setConfirmCode] = React.useState<string>('') const [password, setPassword] = React.useState<string>('') @@ -78,7 +79,7 @@ export function Component({}: {}) { type="title-xl" numberOfLines={1} style={[ - isDesktopWeb ? styles.titleDesktop : styles.titleMobile, + isMobile ? styles.titleMobile : styles.titleDesktop, pal.text, s.bold, ]}> diff --git a/src/view/com/modals/EditImage.tsx b/src/view/com/modals/EditImage.tsx index a4e06b955..e4cfbac35 100644 --- a/src/view/com/modals/EditImage.tsx +++ b/src/view/com/modals/EditImage.tsx @@ -7,6 +7,7 @@ import {useTheme} from 'lib/ThemeContext' import {Text} from '../util/text/Text' import LinearGradient from 'react-native-linear-gradient' import {useStores} from 'state/index' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import ImageEditor, {Position} from 'react-avatar-editor' import {TextInput} from './util' import {enforceLen} from 'lib/strings/helpers' @@ -18,7 +19,6 @@ import {Slider} from '@miblanchard/react-native-slider' import {MaterialIcons} from '@expo/vector-icons' import {observer} from 'mobx-react-lite' import {getKeys} from 'lib/type-assertions' -import {isDesktopWeb} from 'platform/detection' export const snapPoints = ['80%'] @@ -51,6 +51,7 @@ export const Component = observer(function ({image, gallery}: Props) { const theme = useTheme() const store = useStores() const windowDimensions = useWindowDimensions() + const {isMobile} = useWebMediaQueries() const { aspectRatio, @@ -174,19 +175,28 @@ export const Component = observer(function ({image, gallery}: Props) { const computedWidth = windowDimensions.width > 500 ? 410 : windowDimensions.width - 80 - const sideLength = isDesktopWeb ? 300 : computedWidth + const sideLength = isMobile ? computedWidth : 300 const dimensions = image.getResizedDimensions(aspectRatio, sideLength) const imgContainerStyles = {width: sideLength, height: sideLength} const imgControlStyles = { alignItems: 'center' as const, - flexDirection: isDesktopWeb ? ('row' as const) : ('column' as const), - gap: isDesktopWeb ? 5 : 0, + flexDirection: isMobile ? ('column' as const) : ('row' as const), + gap: isMobile ? 0 : 5, } return ( - <View testID="editImageModal" style={[pal.view, styles.container, s.flex1]}> + <View + testID="editImageModal" + style={[ + pal.view, + styles.container, + s.flex1, + { + paddingHorizontal: isMobile ? 16 : undefined, + }, + ]}> <Text style={[styles.title, pal.text]}>Edit image</Text> <View style={[styles.gap18, s.flexRow]}> <View> @@ -213,7 +223,7 @@ export const Component = observer(function ({image, gallery}: Props) { /> </View> <View> - {isDesktopWeb ? ( + {!isMobile ? ( <Text type="sm-bold" style={pal.text}> Ratios </Text> @@ -248,7 +258,7 @@ export const Component = observer(function ({image, gallery}: Props) { ) })} </View> - {isDesktopWeb ? ( + {!isMobile ? ( <Text type="sm-bold" style={[pal.text, styles.subsection]}> Transformations </Text> @@ -282,7 +292,14 @@ export const Component = observer(function ({image, gallery}: Props) { </Text> <TextInput testID="altTextImageInput" - style={[styles.textArea, pal.border, pal.text]} + style={[ + styles.textArea, + pal.border, + pal.text, + { + maxHeight: isMobile ? 50 : undefined, + }, + ]} keyboardAppearance={theme.colorScheme} multiline value={altText} @@ -317,7 +334,6 @@ export const Component = observer(function ({image, gallery}: Props) { const styles = StyleSheet.create({ container: { gap: 18, - paddingHorizontal: isDesktopWeb ? undefined : 16, height: '100%', width: '100%', }, @@ -369,7 +385,6 @@ const styles = StyleSheet.create({ fontSize: 16, height: 100, textAlignVertical: 'top', - maxHeight: isDesktopWeb ? undefined : 50, }, bottomSection: { borderTopWidth: 1, diff --git a/src/view/com/modals/InviteCodes.tsx b/src/view/com/modals/InviteCodes.tsx index d46579f09..ba3cc382b 100644 --- a/src/view/com/modals/InviteCodes.tsx +++ b/src/view/com/modals/InviteCodes.tsx @@ -12,7 +12,7 @@ import * as Toast from '../util/Toast' import {useStores} from 'state/index' import {ScrollView} from './util' import {usePalette} from 'lib/hooks/usePalette' -import {isDesktopWeb} from 'platform/detection' +import {isWeb} from 'platform/detection' export const snapPoints = ['70%'] @@ -127,7 +127,7 @@ const InviteCode = observer( const styles = StyleSheet.create({ container: { flex: 1, - paddingBottom: isDesktopWeb ? 0 : 50, + paddingBottom: isWeb ? 0 : 50, }, title: { textAlign: 'center', diff --git a/src/view/com/modals/ListAddRemoveUser.tsx b/src/view/com/modals/ListAddRemoveUser.tsx index bfb7e4dc0..e00509285 100644 --- a/src/view/com/modals/ListAddRemoveUser.tsx +++ b/src/view/com/modals/ListAddRemoveUser.tsx @@ -19,7 +19,7 @@ import {sanitizeDisplayName} from 'lib/strings/display-names' import {sanitizeHandle} from 'lib/strings/handles' import {s} from 'lib/styles' import {usePalette} from 'lib/hooks/usePalette' -import {isDesktopWeb, isAndroid} from 'platform/detection' +import {isWeb, isAndroid} from 'platform/detection' import isEqual from 'lodash.isequal' export const snapPoints = ['fullscreen'] @@ -231,7 +231,7 @@ export const Component = observer( const styles = StyleSheet.create({ container: { - paddingHorizontal: isDesktopWeb ? 0 : 16, + paddingHorizontal: isWeb ? 0 : 16, }, title: { textAlign: 'center', diff --git a/src/view/com/modals/Modal.web.tsx b/src/view/com/modals/Modal.web.tsx index 5cfdd6bb3..b3a79221d 100644 --- a/src/view/com/modals/Modal.web.tsx +++ b/src/view/com/modals/Modal.web.tsx @@ -3,8 +3,8 @@ import {TouchableWithoutFeedback, StyleSheet, View} from 'react-native' import {observer} from 'mobx-react-lite' import {useStores} from 'state/index' import {usePalette} from 'lib/hooks/usePalette' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import type {Modal as ModalIface} from 'state/models/ui/shell' -import {isMobileWeb} from 'platform/detection' import * as ConfirmModal from './Confirm' import * as EditProfileModal from './EditProfile' @@ -47,6 +47,7 @@ export const ModalsContainer = observer(function ModalsContainer() { function Modal({modal}: {modal: ModalIface}) { const store = useStores() const pal = usePalette('default') + const {isMobile} = useWebMediaQueries() if (!store.shell.isModalActive) { return null @@ -119,7 +120,7 @@ function Modal({modal}: {modal: ModalIface}) { <View style={[ styles.container, - isMobileWeb && styles.containerMobile, + isMobile && styles.containerMobile, pal.view, pal.border, ]}> diff --git a/src/view/com/modals/ModerationDetails.tsx b/src/view/com/modals/ModerationDetails.tsx index b0e68e61b..bd51845c6 100644 --- a/src/view/com/modals/ModerationDetails.tsx +++ b/src/view/com/modals/ModerationDetails.tsx @@ -2,11 +2,12 @@ import React from 'react' import {StyleSheet, View} from 'react-native' import {ModerationUI} from '@atproto/api' import {useStores} from 'state/index' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {s} from 'lib/styles' import {Text} from '../util/text/Text' import {TextLink} from '../util/Link' import {usePalette} from 'lib/hooks/usePalette' -import {isDesktopWeb} from 'platform/detection' +import {isWeb} from 'platform/detection' import {listUriToHref} from 'lib/strings/url-helpers' import {Button} from '../util/forms/Button' @@ -20,6 +21,7 @@ export function Component({ moderation: ModerationUI }) { const store = useStores() + const {isMobile} = useWebMediaQueries() const pal = usePalette('default') let name @@ -64,7 +66,15 @@ export function Component({ } return ( - <View testID="moderationDetailsModal" style={[styles.container, pal.view]}> + <View + testID="moderationDetailsModal" + style={[ + styles.container, + { + paddingHorizontal: isMobile ? 14 : 0, + }, + pal.view, + ]}> <Text type="title-xl" style={[pal.text, styles.title]}> {name} </Text> @@ -87,7 +97,6 @@ export function Component({ const styles = StyleSheet.create({ container: { flex: 1, - paddingHorizontal: isDesktopWeb ? 0 : 14, }, title: { textAlign: 'center', @@ -99,7 +108,7 @@ const styles = StyleSheet.create({ }, btn: { paddingVertical: 14, - marginTop: isDesktopWeb ? 40 : 0, - marginBottom: isDesktopWeb ? 0 : 40, + marginTop: isWeb ? 40 : 0, + marginBottom: isWeb ? 0 : 40, }, }) diff --git a/src/view/com/modals/SelfLabel.tsx b/src/view/com/modals/SelfLabel.tsx index 42863fd33..820f2895b 100644 --- a/src/view/com/modals/SelfLabel.tsx +++ b/src/view/com/modals/SelfLabel.tsx @@ -5,7 +5,8 @@ import {Text} from '../util/text/Text' import {useStores} from 'state/index' import {s, colors} from 'lib/styles' import {usePalette} from 'lib/hooks/usePalette' -import {isDesktopWeb} from 'platform/detection' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import {isWeb} from 'platform/detection' import {Button} from '../util/forms/Button' import {SelectableBtn} from '../util/forms/SelectableBtn' import {ScrollView} from 'view/com/modals/util' @@ -25,6 +26,7 @@ export const Component = observer(function Component({ }) { const pal = usePalette('default') const store = useStores() + const {isMobile} = useWebMediaQueries() const [selected, setSelected] = useState(labels) const toggleAdultLabel = (label: string) => { @@ -54,7 +56,12 @@ export const Component = observer(function Component({ </View> <ScrollView> - <View style={[styles.section, pal.border, {borderBottomWidth: 1}]}> + <View + style={[ + styles.section, + pal.border, + {borderBottomWidth: 1, paddingHorizontal: isMobile ? 20 : 0}, + ]}> <View style={{ flexDirection: 'row', @@ -152,11 +159,11 @@ export const Component = observer(function Component({ const styles = StyleSheet.create({ container: { flex: 1, - paddingBottom: isDesktopWeb ? 0 : 40, + paddingBottom: isWeb ? 0 : 40, }, titleSection: { - paddingTop: isDesktopWeb ? 0 : 4, - paddingBottom: isDesktopWeb ? 14 : 10, + paddingTop: isWeb ? 0 : 4, + paddingBottom: isWeb ? 14 : 10, }, title: { textAlign: 'center', @@ -170,7 +177,6 @@ const styles = StyleSheet.create({ section: { borderTopWidth: 1, paddingVertical: 20, - paddingHorizontal: isDesktopWeb ? 0 : 20, }, adultExplainer: { paddingLeft: 5, diff --git a/src/view/com/modals/lang-settings/ConfirmLanguagesButton.tsx b/src/view/com/modals/lang-settings/ConfirmLanguagesButton.tsx index e1ecce589..c2d0c222a 100644 --- a/src/view/com/modals/lang-settings/ConfirmLanguagesButton.tsx +++ b/src/view/com/modals/lang-settings/ConfirmLanguagesButton.tsx @@ -2,8 +2,8 @@ import React from 'react' import {StyleSheet, Text, View, Pressable} from 'react-native' import LinearGradient from 'react-native-linear-gradient' import {s, colors, gradients} from 'lib/styles' -import {isDesktopWeb} from 'platform/detection' import {usePalette} from 'lib/hooks/usePalette' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' export const ConfirmLanguagesButton = ({ onPress, @@ -13,8 +13,17 @@ export const ConfirmLanguagesButton = ({ extraText?: string }) => { const pal = usePalette('default') + const {isMobile} = useWebMediaQueries() return ( - <View style={[styles.btnContainer, pal.borderDark]}> + <View + style={[ + styles.btnContainer, + pal.borderDark, + isMobile && { + paddingBottom: 40, + borderTopWidth: 1, + }, + ]}> <Pressable testID="confirmContentLanguagesBtn" onPress={onPress} @@ -37,8 +46,6 @@ const styles = StyleSheet.create({ btnContainer: { paddingTop: 10, paddingHorizontal: 10, - paddingBottom: isDesktopWeb ? 0 : 40, - borderTopWidth: isDesktopWeb ? 0 : 1, }, btn: { flexDirection: 'row', diff --git a/src/view/com/modals/lang-settings/ContentLanguagesSettings.tsx b/src/view/com/modals/lang-settings/ContentLanguagesSettings.tsx index 4f7bbc9c7..e577991c5 100644 --- a/src/view/com/modals/lang-settings/ContentLanguagesSettings.tsx +++ b/src/view/com/modals/lang-settings/ContentLanguagesSettings.tsx @@ -4,7 +4,8 @@ import {ScrollView} from '../util' import {useStores} from 'state/index' import {Text} from '../../util/text/Text' import {usePalette} from 'lib/hooks/usePalette' -import {isDesktopWeb, deviceLocales} from 'platform/detection' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import {deviceLocales} from 'platform/detection' import {LANGUAGES, LANGUAGES_MAP_CODE2} from '../../../../locale/languages' import {LanguageToggle} from './LanguageToggle' import {ConfirmLanguagesButton} from './ConfirmLanguagesButton' @@ -14,6 +15,7 @@ export const snapPoints = ['100%'] export function Component({}: {}) { const store = useStores() const pal = usePalette('default') + const {isMobile} = useWebMediaQueries() const onPressDone = React.useCallback(() => { store.shell.closeModal() }, [store]) @@ -47,7 +49,19 @@ export function Component({}: {}) { ) return ( - <View testID="contentLanguagesModal" style={[pal.view, styles.container]}> + <View + testID="contentLanguagesModal" + style={[ + pal.view, + styles.container, + isMobile + ? { + paddingTop: 20, + } + : { + maxHeight: '90vh', + }, + ]}> <Text style={[pal.text, styles.title]}>Content Languages</Text> <Text style={[pal.text, styles.description]}> Which languages would you like to see in your algorithmic feeds? @@ -67,7 +81,11 @@ export function Component({}: {}) { }} /> ))} - <View style={styles.bottomSpacer} /> + <View + style={{ + height: isMobile ? 60 : 0, + }} + /> </ScrollView> <ConfirmLanguagesButton onPress={onPressDone} /> </View> @@ -77,7 +95,6 @@ export function Component({}: {}) { const styles = StyleSheet.create({ container: { flex: 1, - paddingTop: 20, }, title: { textAlign: 'center', @@ -94,7 +111,4 @@ const styles = StyleSheet.create({ flex: 1, paddingHorizontal: 10, }, - bottomSpacer: { - height: isDesktopWeb ? 0 : 60, - }, }) diff --git a/src/view/com/modals/lang-settings/PostLanguagesSettings.tsx b/src/view/com/modals/lang-settings/PostLanguagesSettings.tsx index 0f336e7bc..c80f8731c 100644 --- a/src/view/com/modals/lang-settings/PostLanguagesSettings.tsx +++ b/src/view/com/modals/lang-settings/PostLanguagesSettings.tsx @@ -5,7 +5,8 @@ import {ScrollView} from '../util' import {useStores} from 'state/index' import {Text} from '../../util/text/Text' import {usePalette} from 'lib/hooks/usePalette' -import {isDesktopWeb, deviceLocales} from 'platform/detection' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import {deviceLocales} from 'platform/detection' import {LANGUAGES, LANGUAGES_MAP_CODE2} from '../../../../locale/languages' import {ConfirmLanguagesButton} from './ConfirmLanguagesButton' import {ToggleButton} from 'view/com/util/forms/ToggleButton' @@ -15,6 +16,7 @@ export const snapPoints = ['100%'] export const Component = observer(() => { const store = useStores() const pal = usePalette('default') + const {isMobile} = useWebMediaQueries() const onPressDone = React.useCallback(() => { store.shell.closeModal() }, [store]) @@ -48,7 +50,19 @@ export const Component = observer(() => { ) return ( - <View testID="postLanguagesModal" style={[pal.view, styles.container]}> + <View + testID="postLanguagesModal" + style={[ + pal.view, + styles.container, + isMobile + ? { + paddingTop: 20, + } + : { + maxHeight: '90vh', + }, + ]}> <Text style={[pal.text, styles.title]}>Post Languages</Text> <Text style={[pal.text, styles.description]}> Which languages are used in this post? @@ -80,7 +94,11 @@ export const Component = observer(() => { /> ) })} - <View style={styles.bottomSpacer} /> + <View + style={{ + height: isMobile ? 60 : 0, + }} + /> </ScrollView> <ConfirmLanguagesButton onPress={onPressDone} /> </View> @@ -90,7 +108,6 @@ export const Component = observer(() => { const styles = StyleSheet.create({ container: { flex: 1, - paddingTop: 20, }, title: { textAlign: 'center', @@ -107,9 +124,6 @@ const styles = StyleSheet.create({ flex: 1, paddingHorizontal: 10, }, - bottomSpacer: { - height: isDesktopWeb ? 0 : 60, - }, languageToggle: { borderTopWidth: 1, borderRadius: 0, diff --git a/src/view/com/modals/report/InputIssueDetails.tsx b/src/view/com/modals/report/InputIssueDetails.tsx index a2e5069a8..70a8f7b24 100644 --- a/src/view/com/modals/report/InputIssueDetails.tsx +++ b/src/view/com/modals/report/InputIssueDetails.tsx @@ -5,9 +5,9 @@ import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {CharProgress} from '../../composer/char-progress/CharProgress' import {Text} from '../../util/text/Text' import {usePalette} from 'lib/hooks/usePalette' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {s} from 'lib/styles' import {SendReportButton} from './SendReportButton' -import {isDesktopWeb} from 'platform/detection' export function InputIssueDetails({ details, @@ -23,9 +23,13 @@ export function InputIssueDetails({ isProcessing: boolean }) { const pal = usePalette('default') + const {isMobile} = useWebMediaQueries() return ( - <View style={[styles.detailsContainer]}> + <View + style={{ + marginTop: isMobile ? 12 : 0, + }}> <TouchableOpacity testID="addDetailsBtn" style={[s.mb10, styles.backBtn]} @@ -63,9 +67,6 @@ export function InputIssueDetails({ } const styles = StyleSheet.create({ - detailsContainer: { - marginTop: isDesktopWeb ? 0 : 12, - }, backBtn: { flexDirection: 'row', alignItems: 'center', diff --git a/src/view/com/modals/report/Modal.tsx b/src/view/com/modals/report/Modal.tsx index f386b110d..8aabe0871 100644 --- a/src/view/com/modals/report/Modal.tsx +++ b/src/view/com/modals/report/Modal.tsx @@ -3,6 +3,7 @@ import {Linking, StyleSheet, TouchableOpacity, View} from 'react-native' import {ScrollView} from 'react-native-gesture-handler' import {AtUri} from '@atproto/api' import {useStores} from 'state/index' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {s} from 'lib/styles' import {Text} from '../../util/text/Text' import * as Toast from '../../util/Toast' @@ -37,6 +38,7 @@ type ReportComponentProps = export function Component(content: ReportComponentProps) { const store = useStores() const pal = usePalette('default') + const {isMobile} = useWebMediaQueries() const [isProcessing, setIsProcessing] = useState(false) const [showDetailsInput, setShowDetailsInput] = useState(false) const [error, setError] = useState<string>() @@ -87,7 +89,13 @@ export function Component(content: ReportComponentProps) { return ( <ScrollView testID="reportModal" style={[s.flex1, pal.view]}> - <View style={styles.container}> + <View + style={[ + styles.container, + isMobile && { + paddingBottom: 40, + }, + ]}> {showDetailsInput ? ( <InputIssueDetails details={details} @@ -153,16 +161,14 @@ const SelectIssue = ({ <Text style={[pal.textLight, styles.description]}> What is the issue with this {collectionName}? </Text> - <ReportReasonOptions - atUri={atUri} - selectedIssue={issue} - onSelectIssue={onSelectIssue} - /> - {error ? ( - <View style={s.mt10}> - <ErrorMessage message={error} /> - </View> - ) : undefined} + <View style={{marginBottom: 10}}> + <ReportReasonOptions + atUri={atUri} + selectedIssue={issue} + onSelectIssue={onSelectIssue} + /> + </View> + {error ? <ErrorMessage message={error} /> : undefined} {/* If no atUri is present, the report would be for account in which case, we allow sending without specifying a reason */} {issue || !atUri ? ( <> @@ -188,7 +194,6 @@ const SelectIssue = ({ const styles = StyleSheet.create({ container: { paddingHorizontal: 10, - paddingBottom: 40, }, title: { textAlign: 'center', diff --git a/src/view/com/pager/FeedsTabBar.web.tsx b/src/view/com/pager/FeedsTabBar.web.tsx index 0df915950..6e94ab60e 100644 --- a/src/view/com/pager/FeedsTabBar.web.tsx +++ b/src/view/com/pager/FeedsTabBar.web.tsx @@ -13,8 +13,8 @@ export const FeedsTabBar = observer( ( props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void}, ) => { - const {isDesktop} = useWebMediaQueries() - if (!isDesktop) { + const {isMobile} = useWebMediaQueries() + if (isMobile) { return <FeedsTabBarMobile {...props} /> } else { return <FeedsTabBarDesktop {...props} /> diff --git a/src/view/com/pager/TabBar.tsx b/src/view/com/pager/TabBar.tsx index d454e89f1..319d28f95 100644 --- a/src/view/com/pager/TabBar.tsx +++ b/src/view/com/pager/TabBar.tsx @@ -3,7 +3,8 @@ import {StyleSheet, View, ScrollView, LayoutChangeEvent} from 'react-native' import {Text} from '../util/text/Text' import {PressableWithHover} from '../util/PressableWithHover' import {usePalette} from 'lib/hooks/usePalette' -import {isDesktopWeb, isMobileWeb} from 'platform/detection' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import {isWeb} from 'platform/detection' import {DraggableScrollView} from './DraggableScrollView' export interface TabBarProps { @@ -30,6 +31,7 @@ export function TabBar({ () => ({borderBottomColor: indicatorColor || pal.colors.link}), [indicatorColor, pal], ) + const {isDesktop, isTablet} = useWebMediaQueries() // scrolls to the selected item when the page changes useEffect(() => { @@ -61,6 +63,7 @@ export function TabBar({ [], ) + const styles = isDesktop || isTablet ? desktopStyles : mobileStyles return ( <View testID={testID} style={[pal.view, styles.outer]}> <DraggableScrollView @@ -78,7 +81,7 @@ export function TabBar({ hoverStyle={pal.viewLight} onPress={() => onPressItem(i)}> <Text - type={isDesktopWeb ? 'xl-bold' : 'lg-bold'} + type={isDesktop || isTablet ? 'xl-bold' : 'lg-bold'} testID={testID ? `${testID}-${item}` : undefined} style={selected ? pal.text : pal.textLight}> {item} @@ -91,46 +94,46 @@ export function TabBar({ ) } -const styles = isDesktopWeb - ? StyleSheet.create({ - outer: { - flexDirection: 'row', - width: 598, - }, - contentContainer: { - columnGap: 8, - marginLeft: 14, - paddingRight: 14, - backgroundColor: 'transparent', - }, - item: { - paddingTop: 14, - paddingBottom: 12, - paddingHorizontal: 10, - borderBottomWidth: 3, - borderBottomColor: 'transparent', - justifyContent: 'center', - }, - }) - : StyleSheet.create({ - outer: { - flex: 1, - flexDirection: 'row', - backgroundColor: 'transparent', - maxWidth: '100%', - }, - contentContainer: { - columnGap: isMobileWeb ? 0 : 20, - marginLeft: isMobileWeb ? 0 : 18, - paddingRight: isMobileWeb ? 0 : 36, - backgroundColor: 'transparent', - }, - item: { - paddingTop: 10, - paddingBottom: 10, - paddingHorizontal: isMobileWeb ? 8 : 0, - borderBottomWidth: 3, - borderBottomColor: 'transparent', - justifyContent: 'center', - }, - }) +const desktopStyles = StyleSheet.create({ + outer: { + flexDirection: 'row', + width: 598, + }, + contentContainer: { + columnGap: 8, + marginLeft: 14, + paddingRight: 14, + backgroundColor: 'transparent', + }, + item: { + paddingTop: 14, + paddingBottom: 12, + paddingHorizontal: 10, + borderBottomWidth: 3, + borderBottomColor: 'transparent', + justifyContent: 'center', + }, +}) + +const mobileStyles = StyleSheet.create({ + outer: { + flex: 1, + flexDirection: 'row', + backgroundColor: 'transparent', + maxWidth: '100%', + }, + contentContainer: { + columnGap: isWeb ? 0 : 20, + marginLeft: isWeb ? 0 : 18, + paddingRight: isWeb ? 0 : 36, + backgroundColor: 'transparent', + }, + item: { + paddingTop: 10, + paddingBottom: 10, + paddingHorizontal: isWeb ? 8 : 0, + borderBottomWidth: 3, + borderBottomColor: 'transparent', + justifyContent: 'center', + }, +}) diff --git a/src/view/com/post-thread/PostThread.tsx b/src/view/com/post-thread/PostThread.tsx index 3e951dbf0..f7766dfb7 100644 --- a/src/view/com/post-thread/PostThread.tsx +++ b/src/view/com/post-thread/PostThread.tsx @@ -18,18 +18,24 @@ import { } from '@fortawesome/react-native-fontawesome' import {PostThreadItem} from './PostThreadItem' import {ComposePrompt} from '../composer/Prompt' +import {ViewHeader} from '../util/ViewHeader' import {ErrorMessage} from '../util/error/ErrorMessage' import {Text} from '../util/text/Text' import {s} from 'lib/styles' -import {isIOS, isDesktopWeb, isMobileWeb} from 'platform/detection' +import {isIOS, isDesktopWeb} from 'platform/detection' import {usePalette} from 'lib/hooks/usePalette' import {useSetTitle} from 'lib/hooks/useSetTitle' import {useNavigation} from '@react-navigation/native' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {NavigationProp} from 'lib/routes/types' import {sanitizeDisplayName} from 'lib/strings/display-names' const MAINTAIN_VISIBLE_CONTENT_POSITION = {minIndexForVisible: 0} +const TOP_COMPONENT = { + _reactKey: '__top_component__', + _isHighlightedPost: false, +} const PARENT_SPINNER = { _reactKey: '__parent_spinner__', _isHighlightedPost: false, @@ -47,6 +53,7 @@ const BOTTOM_COMPONENT = { } type YieldedItem = | PostThreadItemModel + | typeof TOP_COMPONENT | typeof PARENT_SPINNER | typeof REPLY_PROMPT | typeof DELETED @@ -63,13 +70,14 @@ export const PostThread = observer(function PostThread({ onPressReply: () => void }) { const pal = usePalette('default') + const {isTablet} = useWebMediaQueries() const ref = useRef<FlatList>(null) const hasScrolledIntoView = useRef<boolean>(false) const [isRefreshing, setIsRefreshing] = React.useState(false) const navigation = useNavigation<NavigationProp>() const posts = React.useMemo(() => { if (view.thread) { - const arr = Array.from(flattenThread(view.thread)) + const arr = [TOP_COMPONENT].concat(Array.from(flattenThread(view.thread))) if (view.isLoadingFromCache) { if (view.thread?.postRecord?.reply) { arr.unshift(PARENT_SPINNER) @@ -158,7 +166,9 @@ export const PostThread = observer(function PostThread({ const renderItem = React.useCallback( ({item, index}: {item: YieldedItem; index: number}) => { - if (item === PARENT_SPINNER) { + if (item === TOP_COMPONENT) { + return isTablet ? <ViewHeader title="Post" /> : null + } else if (item === PARENT_SPINNER) { return ( <View style={styles.parentSpinner}> <ActivityIndicator /> @@ -186,19 +196,8 @@ export const PostThread = observer(function PostThread({ // HACK // due to some complexities with how flatlist works, this is the easiest way // I could find to get a border positioned directly under the last item - // - - // addendum -- it's also the best way to get mobile web to add padding - // at the bottom of the thread since paddingbottom is ignored. yikes. // -prf - return ( - <View - style={[ - styles.bottomBorder, - pal.border, - isMobileWeb && styles.bottomSpacer, - ]} - /> - ) + return <View style={[pal.border, styles.bottomSpacer]} /> } else if (item === CHILD_SPINNER) { return ( <View style={styles.childSpinner}> @@ -219,7 +218,7 @@ export const PostThread = observer(function PostThread({ } return <></> }, - [onRefresh, onPressReply, pal, posts], + [onRefresh, onPressReply, pal, posts, isTablet], ) // loading @@ -331,7 +330,6 @@ export const PostThread = observer(function PostThread({ } onScrollToIndexFailed={onScrollToIndexFailed} style={s.hContentRegion} - contentContainerStyle={styles.contentContainerExtra} /> ) }) @@ -384,13 +382,8 @@ const styles = StyleSheet.create({ paddingVertical: 10, }, childSpinner: {}, - bottomBorder: { - borderBottomWidth: 1, - }, bottomSpacer: { height: 400, - }, - contentContainerExtra: { - paddingBottom: 500, + borderTopWidth: 1, }, }) diff --git a/src/view/com/posts/MultiFeed.tsx b/src/view/com/posts/MultiFeed.tsx index 97899e554..9c8f4f246 100644 --- a/src/view/com/posts/MultiFeed.tsx +++ b/src/view/com/posts/MultiFeed.tsx @@ -22,7 +22,7 @@ import {s} from 'lib/styles' import {useAnalytics} from 'lib/analytics/analytics' import {usePalette} from 'lib/hooks/usePalette' import {useTheme} from 'lib/ThemeContext' -import {isDesktopWeb} from 'platform/detection' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {CogIcon} from 'lib/icons' export const MultiFeed = observer(function Feed({ @@ -48,6 +48,7 @@ export const MultiFeed = observer(function Feed({ }) { const pal = usePalette('default') const theme = useTheme() + const {isMobile} = useWebMediaQueries() const {track} = useAnalytics() const [isRefreshing, setIsRefreshing] = React.useState(false) @@ -80,19 +81,27 @@ export const MultiFeed = observer(function Feed({ const renderItem = React.useCallback( ({item}: {item: MultiFeedItem}) => { if (item.type === 'header') { - if (isDesktopWeb) { + if (!isMobile) { return ( - <View style={[pal.view, pal.border, styles.headerDesktop]}> - <Text type="2xl-bold" style={pal.text}> - My Feeds - </Text> - <Link href="/settings/saved-feeds"> - <CogIcon strokeWidth={1.5} style={pal.icon} size={28} /> - </Link> - </View> + <> + <View style={[pal.view, pal.border, styles.headerDesktop]}> + <Text type="2xl-bold" style={pal.text}> + My Feeds + </Text> + <Link href="/settings/saved-feeds"> + <CogIcon strokeWidth={1.5} style={pal.icon} size={28} /> + </Link> + </View> + <DiscoverLink /> + </> ) } - return <View style={[styles.header, pal.border]} /> + return ( + <> + <View style={[styles.header, pal.border]} /> + <DiscoverLink /> + </> + ) } else if (item.type === 'feed-header') { return ( <View style={styles.feedHeader}> @@ -124,18 +133,11 @@ export const MultiFeed = observer(function Feed({ </Link> ) } else if (item.type === 'footer') { - return ( - <Link style={[styles.footerLink, pal.viewLight]} href="/search/feeds"> - <FontAwesomeIcon icon="search" size={18} color={pal.colors.text} /> - <Text type="xl-medium" style={pal.text}> - Discover new feeds - </Text> - </Link> - ) + return <DiscoverLink /> } return null }, - [pal], + [pal, isMobile], ) const ListFooter = React.useCallback( @@ -150,17 +152,6 @@ export const MultiFeed = observer(function Feed({ [multifeed.isLoading, isRefreshing, pal], ) - const ListHeader = React.useCallback(() => { - return ( - <Link style={[styles.footerLink, pal.viewLight]} href="/search/feeds"> - <FontAwesomeIcon icon="search" size={18} color={pal.colors.text} /> - <Text type="xl-medium" style={pal.text}> - Discover new feeds - </Text> - </Link> - ) - }, [pal]) - return ( <View testID={testID} style={style}> {multifeed.items.length > 0 && ( @@ -171,7 +162,6 @@ export const MultiFeed = observer(function Feed({ keyExtractor={item => item._reactKey} renderItem={renderItem} ListFooterComponent={ListFooter} - ListHeaderComponent={ListHeader} refreshControl={ <RefreshControl refreshing={isRefreshing} @@ -199,6 +189,18 @@ export const MultiFeed = observer(function Feed({ ) }) +function DiscoverLink() { + const pal = usePalette('default') + return ( + <Link style={[styles.discoverLink, pal.viewLight]} href="/search/feeds"> + <FontAwesomeIcon icon="search" size={18} color={pal.colors.text} /> + <Text type="xl-medium" style={pal.text}> + Discover new feeds + </Text> + </Link> + ) +} + const styles = StyleSheet.create({ container: { height: '100%', @@ -237,7 +239,7 @@ const styles = StyleSheet.create({ borderTopWidth: 1, borderBottomWidth: 1, }, - footerLink: { + discoverLink: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', diff --git a/src/view/com/profile/ProfileHeader.tsx b/src/view/com/profile/ProfileHeader.tsx index dd3fb530e..8786fd0b9 100644 --- a/src/view/com/profile/ProfileHeader.tsx +++ b/src/view/com/profile/ProfileHeader.tsx @@ -27,8 +27,9 @@ import {UserBanner} from '../util/UserBanner' import {ProfileHeaderAlerts} from '../util/moderation/ProfileHeaderAlerts' import {usePalette} from 'lib/hooks/usePalette' import {useAnalytics} from 'lib/analytics/analytics' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {NavigationProp} from 'lib/routes/types' -import {isDesktopWeb, isNative} from 'platform/detection' +import {isNative} from 'platform/detection' import {FollowState} from 'state/models/cache/my-follows' import {shareUrl} from 'lib/sharing' import {formatCount} from '../util/numeric/format' @@ -108,6 +109,7 @@ const ProfileHeaderLoaded = observer( const navigation = useNavigation<NavigationProp>() const {track} = useAnalytics() const invalidHandle = isInvalidHandle(view.handle) + const {isDesktop} = useWebMediaQueries() const onPressBack = React.useCallback(() => { navigation.goBack() @@ -510,7 +512,7 @@ const ProfileHeaderLoaded = observer( )} <ProfileHeaderAlerts moderation={view.moderation} /> </View> - {!isDesktopWeb && !hideBackButton && ( + {!isDesktop && !hideBackButton && ( <TouchableWithoutFeedback onPress={onPressBack} hitSlop={BACK_HITSLOP} diff --git a/src/view/com/search/HeaderWithInput.tsx b/src/view/com/search/HeaderWithInput.tsx index f825c578e..7a8676602 100644 --- a/src/view/com/search/HeaderWithInput.tsx +++ b/src/view/com/search/HeaderWithInput.tsx @@ -10,6 +10,7 @@ import {useTheme} from 'lib/ThemeContext' import {usePalette} from 'lib/hooks/usePalette' import {useStores} from 'state/index' import {useAnalytics} from 'lib/analytics/analytics' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {HITSLOP_10} from 'lib/constants' interface Props { @@ -37,6 +38,7 @@ export function HeaderWithInput({ const pal = usePalette('default') const {track} = useAnalytics() const textInput = React.useRef<TextInput>(null) + const {isMobile} = useWebMediaQueries() const onPressMenu = React.useCallback(() => { track('ViewHeader:MenuButtonClicked') @@ -49,8 +51,14 @@ export function HeaderWithInput({ }, [onPressCancelSearch, textInput]) return ( - <View style={[pal.view, pal.border, styles.header]}> - {showMenu ? ( + <View + style={[ + pal.view, + pal.border, + styles.header, + !isMobile && styles.headerDesktop, + ]}> + {showMenu && isMobile ? ( <TouchableOpacity testID="viewHeaderBackOrMenuBtn" onPress={onPressMenu} @@ -85,7 +93,7 @@ export function HeaderWithInput({ onBlur={() => setIsInputFocused(false)} onChangeText={onChangeQuery} onSubmitEditing={onSubmitQuery} - autoFocus={true} + autoFocus={isMobile} accessibilityRole="search" accessibilityLabel="Search" accessibilityHint="" @@ -127,6 +135,11 @@ const styles = StyleSheet.create({ paddingHorizontal: 12, paddingVertical: 4, }, + headerDesktop: { + borderWidth: 1, + borderTopWidth: 0, + paddingVertical: 10, + }, headerMenuBtn: { width: 30, height: 30, diff --git a/src/view/com/search/SearchResults.tsx b/src/view/com/search/SearchResults.tsx index 984277705..e74a8cfe4 100644 --- a/src/view/com/search/SearchResults.tsx +++ b/src/view/com/search/SearchResults.tsx @@ -13,13 +13,14 @@ import { } from 'view/com/util/LoadingPlaceholder' import {Text} from 'view/com/util/text/Text' import {usePalette} from 'lib/hooks/usePalette' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {s} from 'lib/styles' -import {isDesktopWeb} from 'platform/detection' const SECTIONS = ['Posts', 'Users'] export const SearchResults = observer(({model}: {model: SearchUIModel}) => { const pal = usePalette('default') + const {isMobile} = useWebMediaQueries() const renderTabBar = React.useCallback( (props: RenderTabBarFnProps) => { @@ -39,10 +40,16 @@ export const SearchResults = observer(({model}: {model: SearchUIModel}) => { return ( <Pager renderTabBar={renderTabBar} tabBarPosition="top" initialPage={0}> - <View style={[styles.results]}> + <View + style={{ + paddingTop: isMobile ? 42 : 50, + }}> <PostResults key="0" model={model} /> </View> - <View style={[styles.results]}> + <View + style={{ + paddingTop: isMobile ? 42 : 50, + }}> <Profiles key="1" model={model} /> </View> </Pager> @@ -128,7 +135,4 @@ const styles = StyleSheet.create({ paddingHorizontal: 14, paddingVertical: 16, }, - results: { - paddingTop: isDesktopWeb ? 50 : 42, - }, }) diff --git a/src/view/com/util/ViewHeader.tsx b/src/view/com/util/ViewHeader.tsx index 7482db8eb..91cdb08c7 100644 --- a/src/view/com/util/ViewHeader.tsx +++ b/src/view/com/util/ViewHeader.tsx @@ -8,9 +8,9 @@ import {Text} from './text/Text' import {useStores} from 'state/index' import {usePalette} from 'lib/hooks/usePalette' import {useAnimatedValue} from 'lib/hooks/useAnimatedValue' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {useAnalytics} from 'lib/analytics/analytics' import {NavigationProp} from 'lib/routes/types' -import {isDesktopWeb} from 'platform/detection' const BACK_HITSLOP = {left: 20, top: 20, right: 50, bottom: 20} @@ -35,6 +35,7 @@ export const ViewHeader = observer(function ({ const store = useStores() const navigation = useNavigation<NavigationProp>() const {track} = useAnalytics() + const {isDesktop, isTablet} = useWebMediaQueries() const onPressBack = React.useCallback(() => { if (navigation.canGoBack()) { @@ -49,7 +50,7 @@ export const ViewHeader = observer(function ({ store.shell.openDrawer() }, [track, store]) - if (isDesktopWeb) { + if (isDesktop) { if (showOnDesktop) { return ( <DesktopWebHeader @@ -84,13 +85,13 @@ export const ViewHeader = observer(function ({ icon="angle-left" style={[styles.backIcon, pal.text]} /> - ) : ( + ) : !isTablet ? ( <FontAwesomeIcon size={18} icon="bars" style={[styles.backIcon, pal.textLight]} /> - )} + ) : null} </TouchableOpacity> ) : null} <View style={styles.titleContainer} pointerEvents="none"> @@ -122,6 +123,7 @@ function DesktopWebHeader({ <CenteredView style={[ styles.header, + styles.headerFixed, styles.desktopHeader, pal.border, { @@ -178,6 +180,7 @@ const Container = observer( <View style={[ styles.header, + styles.headerFixed, pal.view, pal.border, showBorder && styles.border, @@ -190,9 +193,9 @@ const Container = observer( <Animated.View style={[ styles.header, + styles.headerFloating, pal.view, pal.border, - styles.headerFloating, transform, showBorder && styles.border, ]}> @@ -208,6 +211,12 @@ const styles = StyleSheet.create({ alignItems: 'center', paddingHorizontal: 12, paddingVertical: 6, + width: '100%', + }, + headerFixed: { + maxWidth: 600, + marginLeft: 'auto', + marginRight: 'auto', }, headerFloating: { position: 'absolute', diff --git a/src/view/com/util/Views.web.tsx b/src/view/com/util/Views.web.tsx index 3313492e1..58a367f20 100644 --- a/src/view/com/util/Views.web.tsx +++ b/src/view/com/util/Views.web.tsx @@ -24,6 +24,7 @@ import { } from 'react-native' import {addStyle} from 'lib/styles' import {usePalette} from 'lib/hooks/usePalette' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' interface AddedProps { desktopFixedHeight?: boolean @@ -48,6 +49,7 @@ export const FlatList = React.forwardRef(function <ItemT>( ref: React.Ref<RNFlatList>, ) { const pal = usePalette('default') + const {isMobile} = useWebMediaQueries() contentContainerStyle = addStyle( contentContainerStyle, styles.containerScroll, @@ -67,6 +69,12 @@ export const FlatList = React.forwardRef(function <ItemT>( } if (desktopFixedHeight) { style = addStyle(style, styles.fixedHeight) + if (!isMobile) { + contentContainerStyle = addStyle( + contentContainerStyle, + styles.stableGutters, + ) + } } return ( <RNFlatList @@ -126,6 +134,9 @@ const styles = StyleSheet.create({ }, fixedHeight: { height: '100vh', + }, + stableGutters: { + // @ts-ignore web only -prf scrollbarGutter: 'stable both-edges', }, }) diff --git a/src/view/com/util/fab/FABInner.tsx b/src/view/com/util/fab/FABInner.tsx index 76824e575..afd172c82 100644 --- a/src/view/com/util/fab/FABInner.tsx +++ b/src/view/com/util/fab/FABInner.tsx @@ -5,7 +5,8 @@ import LinearGradient from 'react-native-linear-gradient' import {gradients} from 'lib/styles' import {useAnimatedValue} from 'lib/hooks/useAnimatedValue' import {useStores} from 'state/index' -import {isMobileWeb} from 'platform/detection' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import {isWeb} from 'platform/detection' export interface FABProps extends ComponentProps<typeof TouchableWithoutFeedback> { @@ -14,6 +15,7 @@ export interface FABProps } export const FABInner = observer(({testID, icon, ...props}: FABProps) => { + const {isTablet} = useWebMediaQueries() const store = useStores() const interp = useAnimatedValue(0) React.useEffect(() => { @@ -24,18 +26,33 @@ export const FABInner = observer(({testID, icon, ...props}: FABProps) => { isInteraction: false, }).start() }, [interp, store.shell.minimalShellMode]) - const transform = { - transform: [{translateY: Animated.multiply(interp, 60)}], - } + const transform = isTablet + ? undefined + : { + transform: [{translateY: Animated.multiply(interp, 60)}], + } + const size = isTablet ? styles.sizeLarge : styles.sizeRegular return ( <TouchableWithoutFeedback testID={testID} {...props}> <Animated.View - style={[styles.outer, isMobileWeb && styles.mobileWebOuter, transform]}> + style={[ + styles.outer, + size, + isWeb && isTablet + ? { + right: 50, + bottom: 50, + } + : { + bottom: 114, + }, + transform, + ]}> <LinearGradient colors={[gradients.blueLight.start, gradients.blueLight.end]} start={{x: 0, y: 0}} end={{x: 1, y: 1}} - style={styles.inner}> + style={[styles.inner, size]}> {icon} </LinearGradient> </Animated.View> @@ -44,22 +61,23 @@ export const FABInner = observer(({testID, icon, ...props}: FABProps) => { }) const styles = StyleSheet.create({ + sizeRegular: { + width: 60, + height: 60, + borderRadius: 30, + }, + sizeLarge: { + width: 70, + height: 70, + borderRadius: 35, + }, outer: { position: 'absolute', zIndex: 1, right: 24, bottom: 94, - width: 60, - height: 60, - borderRadius: 30, - }, - mobileWebOuter: { - bottom: 114, }, inner: { - width: 60, - height: 60, - borderRadius: 30, justifyContent: 'center', alignItems: 'center', }, diff --git a/src/view/com/util/forms/SelectableBtn.tsx b/src/view/com/util/forms/SelectableBtn.tsx index 4b494264e..f09d063a1 100644 --- a/src/view/com/util/forms/SelectableBtn.tsx +++ b/src/view/com/util/forms/SelectableBtn.tsx @@ -2,7 +2,7 @@ import React from 'react' import {Pressable, ViewStyle, StyleProp, StyleSheet} from 'react-native' import {Text} from '../text/Text' import {usePalette} from 'lib/hooks/usePalette' -import {isDesktopWeb} from 'platform/detection' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' interface SelectableBtnProps { testID?: string @@ -28,12 +28,16 @@ export function SelectableBtn({ const pal = usePalette('default') const palPrimary = usePalette('inverted') const needsWidthStyles = !style || !('width' in style || 'flex' in style) + const {isMobile} = useWebMediaQueries() return ( <Pressable testID={testID} style={[ styles.btn, - needsWidthStyles && styles.btnWidth, + needsWidthStyles && { + flex: isMobile ? 1 : undefined, + width: !isMobile ? 100 : undefined, + }, left && styles.btnLeft, right && styles.btnRight, pal.border, @@ -58,10 +62,6 @@ const styles = StyleSheet.create({ paddingHorizontal: 10, paddingVertical: 10, }, - btnWidth: { - flex: isDesktopWeb ? undefined : 1, - width: isDesktopWeb ? 100 : undefined, - }, btnLeft: { borderTopLeftRadius: 8, borderBottomLeftRadius: 8, diff --git a/src/view/com/util/layouts/Breakpoints.web.tsx b/src/view/com/util/layouts/Breakpoints.web.tsx index 7031a1735..5cf73df0c 100644 --- a/src/view/com/util/layouts/Breakpoints.web.tsx +++ b/src/view/com/util/layouts/Breakpoints.web.tsx @@ -2,18 +2,18 @@ import React from 'react' import MediaQuery from 'react-responsive' export const Desktop = ({children}: React.PropsWithChildren<{}>) => ( - <MediaQuery minWidth={1224}>{children}</MediaQuery> + <MediaQuery minWidth={1300}>{children}</MediaQuery> ) export const TabletOrDesktop = ({children}: React.PropsWithChildren<{}>) => ( <MediaQuery minWidth={800}>{children}</MediaQuery> ) export const Tablet = ({children}: React.PropsWithChildren<{}>) => ( - <MediaQuery minWidth={800} maxWidth={1224}> + <MediaQuery minWidth={800} maxWidth={1300}> {children} </MediaQuery> ) export const TabletOrMobile = ({children}: React.PropsWithChildren<{}>) => ( - <MediaQuery maxWidth={1224}>{children}</MediaQuery> + <MediaQuery maxWidth={1300}>{children}</MediaQuery> ) export const Mobile = ({children}: React.PropsWithChildren<{}>) => ( <MediaQuery maxWidth={800}>{children}</MediaQuery> diff --git a/src/view/com/util/load-latest/LoadLatestBtn.web.tsx b/src/view/com/util/load-latest/LoadLatestBtn.web.tsx index c90e5dfb1..c9576e56b 100644 --- a/src/view/com/util/load-latest/LoadLatestBtn.web.tsx +++ b/src/view/com/util/load-latest/LoadLatestBtn.web.tsx @@ -3,8 +3,8 @@ import {StyleSheet, TouchableOpacity} from 'react-native' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {Text} from '../text/Text' import {usePalette} from 'lib/hooks/usePalette' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {LoadLatestBtn as LoadLatestBtnMobile} from './LoadLatestBtnMobile' -import {isMobileWeb} from 'platform/detection' import {HITSLOP_20} from 'lib/constants' export const LoadLatestBtn = ({ @@ -19,7 +19,8 @@ export const LoadLatestBtn = ({ minimalShellMode?: boolean }) => { const pal = usePalette('default') - if (isMobileWeb) { + const {isMobile} = useWebMediaQueries() + if (isMobile) { return ( <LoadLatestBtnMobile onPress={onPress} diff --git a/src/view/com/util/moderation/ContentHider.tsx b/src/view/com/util/moderation/ContentHider.tsx index 853f7840c..6cf1cefd0 100644 --- a/src/view/com/util/moderation/ContentHider.tsx +++ b/src/view/com/util/moderation/ContentHider.tsx @@ -1,12 +1,12 @@ import React from 'react' import {Pressable, StyleProp, StyleSheet, View, ViewStyle} from 'react-native' import {usePalette} from 'lib/hooks/usePalette' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {ModerationUI} from '@atproto/api' import {Text} from '../text/Text' import {ShieldExclamation} from 'lib/icons' import {describeModerationCause} from 'lib/moderation' import {useStores} from 'state/index' -import {isDesktopWeb} from 'platform/detection' export function ContentHider({ testID, @@ -24,6 +24,7 @@ export function ContentHider({ }>) { const store = useStores() const pal = usePalette('default') + const {isMobile} = useWebMediaQueries() const [override, setOverride] = React.useState(false) if (!moderation.blur || (ignoreMute && moderation.cause?.type === 'muted')) { @@ -54,6 +55,7 @@ export function ContentHider({ accessibilityLabel="" style={[ styles.cover, + {paddingRight: isMobile ? 22 : 18}, moderation.noOverride ? {borderWidth: 1, borderColor: pal.colors.borderDark} : pal.viewLight, @@ -96,7 +98,6 @@ const styles = StyleSheet.create({ marginTop: 4, paddingVertical: 14, paddingLeft: 14, - paddingRight: isDesktopWeb ? 18 : 22, }, showBtn: { marginLeft: 'auto', diff --git a/src/view/com/util/moderation/PostHider.tsx b/src/view/com/util/moderation/PostHider.tsx index 2a52561d4..443885dfa 100644 --- a/src/view/com/util/moderation/PostHider.tsx +++ b/src/view/com/util/moderation/PostHider.tsx @@ -2,13 +2,13 @@ import React, {ComponentProps} from 'react' import {StyleSheet, Pressable, View} from 'react-native' import {ModerationUI} from '@atproto/api' import {usePalette} from 'lib/hooks/usePalette' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {Link} from '../Link' import {Text} from '../text/Text' import {addStyle} from 'lib/styles' import {describeModerationCause} from 'lib/moderation' import {ShieldExclamation} from 'lib/icons' import {useStores} from 'state/index' -import {isDesktopWeb} from 'platform/detection' interface Props extends ComponentProps<typeof Link> { // testID?: string @@ -27,6 +27,7 @@ export function PostHider({ }: Props) { const store = useStores() const pal = usePalette('default') + const {isMobile} = useWebMediaQueries() const [override, setOverride] = React.useState(false) if (!moderation.blur) { @@ -55,7 +56,11 @@ export function PostHider({ accessibilityRole="button" accessibilityHint={override ? 'Hide the content' : 'Show the content'} accessibilityLabel="" - style={[styles.description, pal.viewLight]}> + style={[ + styles.description, + {paddingRight: isMobile ? 22 : 18}, + pal.viewLight, + ]}> <Pressable onPress={() => { store.shell.openModal({ @@ -100,7 +105,6 @@ const styles = StyleSheet.create({ gap: 4, paddingVertical: 14, paddingLeft: 18, - paddingRight: isDesktopWeb ? 18 : 22, marginTop: 1, }, showBtn: { diff --git a/src/view/com/util/moderation/ScreenHider.tsx b/src/view/com/util/moderation/ScreenHider.tsx index b76b1101c..0224b9fee 100644 --- a/src/view/com/util/moderation/ScreenHider.tsx +++ b/src/view/com/util/moderation/ScreenHider.tsx @@ -13,10 +13,10 @@ import { import {useNavigation} from '@react-navigation/native' import {ModerationUI} from '@atproto/api' import {usePalette} from 'lib/hooks/usePalette' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {NavigationProp} from 'lib/routes/types' import {Text} from '../text/Text' import {Button} from '../forms/Button' -import {isDesktopWeb} from 'platform/detection' import {describeModerationCause} from 'lib/moderation' import {useStores} from 'state/index' @@ -39,6 +39,7 @@ export function ScreenHider({ const palInverted = usePalette('inverted') const [override, setOverride] = React.useState(false) const navigation = useNavigation<NavigationProp>() + const {isMobile} = useWebMediaQueries() if (!moderation.blur || override) { return ( @@ -85,7 +86,7 @@ export function ScreenHider({ </Text> </TouchableWithoutFeedback> </Text> - {!isDesktopWeb && <View style={styles.spacer} />} + {isMobile && <View style={styles.spacer} />} <View style={styles.btnContainer}> <Button type="inverted" diff --git a/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx b/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx index 81f1ca560..d5bb38fb2 100644 --- a/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx +++ b/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx @@ -3,8 +3,8 @@ import {Image} from 'expo-image' import {Text} from '../text/Text' import {StyleSheet, View} from 'react-native' import {usePalette} from 'lib/hooks/usePalette' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {AppBskyEmbedExternal} from '@atproto/api' -import {isDesktopWeb} from 'platform/detection' import {toNiceDomain} from 'lib/strings/url-helpers' export const ExternalLinkEmbed = ({ @@ -15,10 +15,31 @@ export const ExternalLinkEmbed = ({ imageChild?: React.ReactNode }) => { const pal = usePalette('default') + const {isMobile} = useWebMediaQueries() return ( - <View style={styles.extContainer}> + <View + style={{ + flexDirection: isMobile ? 'column' : 'row', + }}> {link.thumb ? ( - <View style={styles.extImageContainer}> + <View + style={ + !isMobile + ? { + borderTopLeftRadius: 6, + borderBottomLeftRadius: 6, + width: 120, + aspectRatio: 1, + overflow: 'hidden', + } + : { + borderTopLeftRadius: 6, + borderTopRightRadius: 6, + width: '100%', + height: 200, + overflow: 'hidden', + } + }> <Image style={styles.extImage} source={{uri: link.thumb}} @@ -27,7 +48,13 @@ export const ExternalLinkEmbed = ({ {imageChild} </View> ) : undefined} - <View style={styles.extInner}> + <View + style={{ + paddingHorizontal: isMobile ? 10 : 14, + paddingTop: 8, + paddingBottom: 10, + flex: !isMobile ? 1 : undefined, + }}> <Text type="sm" numberOfLines={1} @@ -36,14 +63,14 @@ export const ExternalLinkEmbed = ({ </Text> <Text type="lg-bold" - numberOfLines={isDesktopWeb ? 2 : 4} + numberOfLines={isMobile ? 4 : 2} style={[pal.text]}> {link.title || link.uri} </Text> {link.description ? ( <Text type="md" - numberOfLines={isDesktopWeb ? 2 : 4} + numberOfLines={isMobile ? 4 : 2} style={[pal.text, styles.extDescription]}> {link.description} </Text> @@ -54,30 +81,6 @@ export const ExternalLinkEmbed = ({ } const styles = StyleSheet.create({ - extContainer: { - flexDirection: isDesktopWeb ? 'row' : 'column', - }, - extInner: { - paddingHorizontal: isDesktopWeb ? 14 : 10, - paddingTop: 8, - paddingBottom: 10, - flex: isDesktopWeb ? 1 : undefined, - }, - extImageContainer: isDesktopWeb - ? { - borderTopLeftRadius: 6, - borderBottomLeftRadius: 6, - width: 120, - aspectRatio: 1, - overflow: 'hidden', - } - : { - borderTopLeftRadius: 6, - borderTopRightRadius: 6, - width: '100%', - height: 200, - overflow: 'hidden', - }, extImage: { width: '100%', height: 200, diff --git a/src/view/com/util/post-embeds/index.tsx b/src/view/com/util/post-embeds/index.tsx index bf2365f18..ce6da4a1b 100644 --- a/src/view/com/util/post-embeds/index.tsx +++ b/src/view/com/util/post-embeds/index.tsx @@ -22,6 +22,7 @@ import {ImageLayoutGrid} from '../images/ImageLayoutGrid' import {ImagesLightbox} from 'state/models/ui/shell' import {useStores} from 'state/index' import {usePalette} from 'lib/hooks/usePalette' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {YoutubeEmbed} from './YoutubeEmbed' import {ExternalLinkEmbed} from './ExternalLinkEmbed' import {getYoutubeVideoId} from 'lib/strings/url-helpers' @@ -29,7 +30,6 @@ import {MaybeQuoteEmbed} from './QuoteEmbed' import {AutoSizedImage} from '../images/AutoSizedImage' import {CustomFeedEmbed} from './CustomFeedEmbed' import {ListEmbed} from './ListEmbed' -import {isDesktopWeb} from 'platform/detection' import {isCauseALabelOnUri} from 'lib/moderation' type Embed = @@ -50,6 +50,7 @@ export function PostEmbeds({ }) { const pal = usePalette('default') const store = useStores() + const {isMobile} = useWebMediaQueries() // quote post with media // = @@ -111,7 +112,10 @@ export function PostEmbeds({ uri={thumb} onPress={() => openLightbox(0)} onPressIn={() => onPressIn(0)} - style={styles.singleImage}> + style={[ + styles.singleImage, + isMobile && styles.singleImageMobile, + ]}> {alt === '' ? null : ( <View style={styles.altContainer}> <Text style={styles.alt} accessible={false}> @@ -130,7 +134,11 @@ export function PostEmbeds({ images={embed.images} onPress={openLightbox} onPressIn={onPressIn} - style={embed.images.length === 1 ? styles.singleImage : undefined} + style={ + embed.images.length === 1 + ? [styles.singleImage, isMobile && styles.singleImageMobile] + : undefined + } /> </View> ) @@ -169,7 +177,10 @@ const styles = StyleSheet.create({ }, singleImage: { borderRadius: 8, - maxHeight: isDesktopWeb ? 1000 : 500, + maxHeight: 1000, + }, + singleImageMobile: { + maxHeight: 500, }, extOuter: { borderWidth: 1, diff --git a/src/view/screens/AppPasswords.tsx b/src/view/screens/AppPasswords.tsx index 582bc0f9b..8fac86d34 100644 --- a/src/view/screens/AppPasswords.tsx +++ b/src/view/screens/AppPasswords.tsx @@ -7,7 +7,7 @@ import {Button} from '../com/util/forms/Button' import * as Toast from '../com/util/Toast' import {useStores} from 'state/index' import {usePalette} from 'lib/hooks/usePalette' -import {isDesktopWeb} from 'platform/detection' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {withAuthRequired} from 'view/com/auth/withAuthRequired' import {observer} from 'mobx-react-lite' import {NativeStackScreenProps} from '@react-navigation/native-stack' @@ -23,6 +23,7 @@ export const AppPasswords = withAuthRequired( const pal = usePalette('default') const store = useStores() const {screen} = useAnalytics() + const {isTabletOrDesktop} = useWebMediaQueries() useFocusEffect( React.useCallback(() => { @@ -41,7 +42,7 @@ export const AppPasswords = withAuthRequired( <CenteredView style={[ styles.container, - isDesktopWeb && styles.containerDesktop, + isTabletOrDesktop && styles.containerDesktop, pal.view, pal.border, ]} @@ -53,11 +54,11 @@ export const AppPasswords = withAuthRequired( pressing the button below. </Text> </View> - {!isDesktopWeb && <View style={styles.flex1} />} + {!isTabletOrDesktop && <View style={styles.flex1} />} <View style={[ styles.btnContainer, - isDesktopWeb && styles.btnContainerDesktop, + isTabletOrDesktop && styles.btnContainerDesktop, ]}> <Button testID="appPasswordBtn" @@ -77,7 +78,7 @@ export const AppPasswords = withAuthRequired( <CenteredView style={[ styles.container, - isDesktopWeb && styles.containerDesktop, + isTabletOrDesktop && styles.containerDesktop, pal.view, pal.border, ]} @@ -87,7 +88,7 @@ export const AppPasswords = withAuthRequired( style={[ styles.scrollContainer, pal.border, - !isDesktopWeb && styles.flex1, + !isTabletOrDesktop && styles.flex1, ]}> {store.me.appPasswords.map((password, i) => ( <AppPassword @@ -97,7 +98,7 @@ export const AppPasswords = withAuthRequired( createdAt={password.createdAt} /> ))} - {isDesktopWeb && ( + {isTabletOrDesktop && ( <View style={[styles.btnContainer, styles.btnContainerDesktop]}> <Button testID="appPasswordBtn" @@ -110,7 +111,7 @@ export const AppPasswords = withAuthRequired( </View> )} </ScrollView> - {!isDesktopWeb && ( + {!isTabletOrDesktop && ( <View style={styles.btnContainer}> <Button testID="appPasswordBtn" @@ -128,6 +129,7 @@ export const AppPasswords = withAuthRequired( ) function AppPasswordsHeader() { + const {isTabletOrDesktop} = useWebMediaQueries() const pal = usePalette('default') return ( <> @@ -137,7 +139,7 @@ function AppPasswordsHeader() { style={[ styles.description, pal.text, - isDesktopWeb && styles.descriptionDesktop, + isTabletOrDesktop && styles.descriptionDesktop, ]}> Use app passwords to login to other Bluesky clients without giving full access to your account or password. @@ -207,11 +209,12 @@ function AppPassword({ const styles = StyleSheet.create({ container: { flex: 1, - paddingBottom: isDesktopWeb ? 0 : 100, + paddingBottom: 100, }, containerDesktop: { borderLeftWidth: 1, borderRightWidth: 1, + paddingBottom: 0, }, title: { textAlign: 'center', diff --git a/src/view/screens/CustomFeed.tsx b/src/view/screens/CustomFeed.tsx index 01d499dad..f4e1b0eb7 100644 --- a/src/view/screens/CustomFeed.tsx +++ b/src/view/screens/CustomFeed.tsx @@ -22,7 +22,7 @@ import {ViewHeader} from 'view/com/util/ViewHeader' import {Button} from 'view/com/util/forms/Button' import {Text} from 'view/com/util/text/Text' import * as Toast from 'view/com/util/Toast' -import {isDesktopWeb} from 'platform/detection' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {useSetTitle} from 'lib/hooks/useSetTitle' import {shareUrl} from 'lib/sharing' import {toShareUrl} from 'lib/strings/url-helpers' @@ -122,6 +122,7 @@ export const CustomFeedScreenInner = observer( ({route, feedOwnerDid}: Props & {feedOwnerDid: string}) => { const store = useStores() const pal = usePalette('default') + const {isTabletOrDesktop} = useWebMediaQueries() const {track} = useAnalytics() const {rkey, name: handleOrDid} = route.params const uri = useMemo( @@ -357,7 +358,7 @@ export const CustomFeedScreenInner = observer( )} </Text> )} - {isDesktopWeb && ( + {isTabletOrDesktop && ( <View style={[styles.headerBtns, styles.headerBtnsDesktop]}> <Button type={currentFeed?.isSaved ? 'default' : 'inverted'} @@ -452,7 +453,14 @@ export const CustomFeedScreenInner = observer( ) : null} </View> </View> - <View style={[styles.fakeSelector, pal.border]}> + <View + style={[ + styles.fakeSelector, + { + paddingHorizontal: isTabletOrDesktop ? 16 : 6, + }, + pal.border, + ]}> <View style={[styles.fakeSelectorItem, {borderColor: pal.colors.link}]}> <Text type="md-medium" style={[pal.text]}> @@ -474,6 +482,7 @@ export const CustomFeedScreenInner = observer( rkey, isPinned, onTogglePinned, + isTabletOrDesktop, ]) const renderEmptyState = React.useCallback(() => { @@ -486,7 +495,9 @@ export const CustomFeedScreenInner = observer( return ( <View style={s.hContentRegion}> - <ViewHeader title="" renderButton={currentFeed && renderHeaderBtns} /> + {!isTabletOrDesktop && ( + <ViewHeader title="" renderButton={currentFeed && renderHeaderBtns} /> + )} <Feed scrollElRef={scrollElRef} feed={algoFeed} @@ -495,6 +506,7 @@ export const CustomFeedScreenInner = observer( ListHeaderComponent={renderListHeaderComponent} renderEmptyState={renderEmptyState} extraData={[uri, isPinned]} + style={!isTabletOrDesktop ? {flex: 1} : undefined} /> {isScrolledDown ? ( <LoadLatestBtn @@ -550,7 +562,6 @@ const styles = StyleSheet.create({ }, fakeSelector: { flexDirection: 'row', - paddingHorizontal: isDesktopWeb ? 16 : 6, }, fakeSelectorItem: { paddingHorizontal: 12, diff --git a/src/view/screens/DiscoverFeeds.tsx b/src/view/screens/DiscoverFeeds.tsx index d4d4e5e6e..11f38c26a 100644 --- a/src/view/screens/DiscoverFeeds.tsx +++ b/src/view/screens/DiscoverFeeds.tsx @@ -10,8 +10,8 @@ import {FeedsDiscoveryModel} from 'state/models/discovery/feeds' import {CenteredView, FlatList} from 'view/com/util/Views' import {CustomFeed} from 'view/com/feeds/CustomFeed' import {Text} from 'view/com/util/text/Text' -import {isDesktopWeb} from 'platform/detection' import {usePalette} from 'lib/hooks/usePalette' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {s} from 'lib/styles' import {CustomFeedModel} from 'state/models/feeds/custom-feed' import {HeaderWithInput} from 'view/com/search/HeaderWithInput' @@ -23,6 +23,7 @@ export const DiscoverFeedsScreen = withAuthRequired( const store = useStores() const pal = usePalette('default') const feeds = React.useMemo(() => new FeedsDiscoveryModel(store), [store]) + const {isTabletOrDesktop} = useWebMediaQueries() // search stuff const [isInputFocused, setIsInputFocused] = React.useState<boolean>(false) @@ -74,7 +75,7 @@ export const DiscoverFeedsScreen = withAuthRequired( <View style={styles.empty}> <Text type="lg" style={pal.textLight}> {feeds.isLoading - ? isDesktopWeb + ? isTabletOrDesktop ? 'Loading...' : '' : query @@ -100,23 +101,22 @@ export const DiscoverFeedsScreen = withAuthRequired( return ( <CenteredView style={[styles.container, pal.view]}> - <View style={[isDesktopWeb && styles.containerDesktop, pal.border]}> + <View + style={[isTabletOrDesktop && styles.containerDesktop, pal.border]}> <ViewHeader title="Discover Feeds" showOnDesktop /> - <View style={{marginTop: isDesktopWeb ? 5 : 0, marginBottom: 4}}> - <HeaderWithInput - isInputFocused={isInputFocused} - query={query} - setIsInputFocused={setIsInputFocused} - onChangeQuery={onChangeQuery} - onPressClearQuery={onPressClearQuery} - onPressCancelSearch={onPressCancelSearch} - onSubmitQuery={onSubmitQuery} - showMenu={false} - /> - </View> </View> + <HeaderWithInput + isInputFocused={isInputFocused} + query={query} + setIsInputFocused={setIsInputFocused} + onChangeQuery={onChangeQuery} + onPressClearQuery={onPressClearQuery} + onPressCancelSearch={onPressCancelSearch} + onSubmitQuery={onSubmitQuery} + showMenu={false} + /> <FlatList - style={[!isDesktopWeb && s.flex1]} + style={[!isTabletOrDesktop && s.flex1]} data={feeds.feeds} keyExtractor={item => item.data.uri} contentContainerStyle={styles.contentContainer} diff --git a/src/view/screens/Feeds.tsx b/src/view/screens/Feeds.tsx index 7d45ce4c1..6e0706737 100644 --- a/src/view/screens/Feeds.tsx +++ b/src/view/screens/Feeds.tsx @@ -12,22 +12,23 @@ import {NativeStackScreenProps, FeedsTabNavigatorParams} from 'lib/routes/types' import {observer} from 'mobx-react-lite' import {PostsMultiFeedModel} from 'state/models/feeds/multi-feed' import {MultiFeed} from 'view/com/posts/MultiFeed' -import {isDesktopWeb} from 'platform/detection' import {usePalette} from 'lib/hooks/usePalette' import {useTimer} from 'lib/hooks/useTimer' import {useStores} from 'state/index' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {useOnMainScroll} from 'lib/hooks/useOnMainScroll' import {ComposeIcon2, CogIcon} from 'lib/icons' import {s} from 'lib/styles' const LOAD_NEW_PROMPT_TIME = 60e3 // 60 seconds -const HEADER_OFFSET = isDesktopWeb ? 0 : 40 +const MOBILE_HEADER_OFFSET = 40 type Props = NativeStackScreenProps<FeedsTabNavigatorParams, 'Feeds'> export const FeedsScreen = withAuthRequired( observer<Props>(({}: Props) => { const pal = usePalette('default') const store = useStores() + const {isMobile} = useWebMediaQueries() const flatListRef = React.useRef<FlatList>(null) const multifeed = React.useMemo<PostsMultiFeedModel>( () => new PostsMultiFeedModel(store), @@ -105,14 +106,16 @@ export const FeedsScreen = withAuthRequired( multifeed={multifeed} onScroll={onMainScroll} scrollEventThrottle={100} - headerOffset={HEADER_OFFSET} - /> - <ViewHeader - title="My Feeds" - canGoBack={false} - hideOnScroll - renderButton={renderHeaderBtn} + headerOffset={isMobile ? MOBILE_HEADER_OFFSET : undefined} /> + {isMobile && ( + <ViewHeader + title="My Feeds" + canGoBack={false} + hideOnScroll + renderButton={renderHeaderBtn} + /> + )} {isScrolledDown || loadPromptVisible ? ( <LoadLatestBtn onPress={onSoftReset} diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx index 4f1ebe039..795d813d1 100644 --- a/src/view/screens/Home.tsx +++ b/src/view/screens/Home.tsx @@ -19,14 +19,11 @@ import {useStores} from 'state/index' import {s} from 'lib/styles' import {useOnMainScroll} from 'lib/hooks/useOnMainScroll' import {useAnalytics} from 'lib/analytics/analytics' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {ComposeIcon2} from 'lib/icons' -import {isDesktopWeb, isMobileWebMediaQuery, isWeb} from 'platform/detection' const HEADER_OFFSET_MOBILE = 78 const HEADER_OFFSET_DESKTOP = 50 -const HEADER_OFFSET = isDesktopWeb - ? HEADER_OFFSET_DESKTOP - : HEADER_OFFSET_MOBILE const POLL_FREQ = 30e3 // 30sec type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home'> @@ -158,10 +155,13 @@ const FeedPage = observer( renderEmptyState?: () => JSX.Element }) => { const store = useStores() + const {isMobile} = useWebMediaQueries() const [onMainScroll, isScrolledDown, resetMainScroll] = useOnMainScroll(store) const {screen, track} = useAnalytics() - const [headerOffset, setHeaderOffset] = React.useState(HEADER_OFFSET) + const [headerOffset, setHeaderOffset] = React.useState( + isMobile ? HEADER_OFFSET_MOBILE : HEADER_OFFSET_DESKTOP, + ) const scrollElRef = React.useRef<FlatList>(null) const {appState} = useAppState({ onForeground: () => doPoll(true), @@ -206,15 +206,9 @@ const FeedPage = observer( }, [isPageFocused, scrollToTop, feed]) // listens for resize events - const listenForResize = React.useCallback(() => { - // @ts-ignore we know window exists -prf - const isMobileWeb = global.window.matchMedia( - isMobileWebMediaQuery, - )?.matches - setHeaderOffset( - isMobileWeb ? HEADER_OFFSET_MOBILE : HEADER_OFFSET_DESKTOP, - ) - }, []) + React.useEffect(() => { + setHeaderOffset(isMobile ? HEADER_OFFSET_MOBILE : HEADER_OFFSET_DESKTOP) + }, [isMobile]) // fires when page within screen is activated/deactivated // - check for latest @@ -234,17 +228,10 @@ const FeedPage = observer( feed.update() } - if (isWeb) { - window.addEventListener('resize', listenForResize) - } - return () => { clearInterval(pollInterval) softResetSub.remove() feedCleanup() - if (isWeb) { - isWeb && window.removeEventListener('resize', listenForResize) - } } }, [ store, @@ -254,7 +241,6 @@ const FeedPage = observer( feed, isPageFocused, isScreenFocused, - listenForResize, ]) const onPressCompose = React.useCallback(() => { diff --git a/src/view/screens/Moderation.tsx b/src/view/screens/Moderation.tsx index 41df1244e..23a808feb 100644 --- a/src/view/screens/Moderation.tsx +++ b/src/view/screens/Moderation.tsx @@ -16,7 +16,7 @@ import {Link} from '../com/util/Link' import {Text} from '../com/util/text/Text' import {usePalette} from 'lib/hooks/usePalette' import {useAnalytics} from 'lib/analytics/analytics' -import {isDesktopWeb} from 'platform/detection' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' type Props = NativeStackScreenProps<CommonNavigatorParams, 'Moderation'> export const ModerationScreen = withAuthRequired( @@ -24,6 +24,7 @@ export const ModerationScreen = withAuthRequired( const pal = usePalette('default') const store = useStores() const {screen, track} = useAnalytics() + const {isTabletOrDesktop} = useWebMediaQueries() useFocusEffect( React.useCallback(() => { @@ -42,7 +43,7 @@ export const ModerationScreen = withAuthRequired( style={[ s.hContentRegion, pal.border, - isDesktopWeb ? styles.desktopContainer : pal.viewLight, + isTabletOrDesktop ? styles.desktopContainer : pal.viewLight, ]} testID="moderationScreen"> <ViewHeader title="Moderation" showOnDesktop /> diff --git a/src/view/screens/ModerationBlockedAccounts.tsx b/src/view/screens/ModerationBlockedAccounts.tsx index 959c6d9ca..10fa87080 100644 --- a/src/view/screens/ModerationBlockedAccounts.tsx +++ b/src/view/screens/ModerationBlockedAccounts.tsx @@ -10,7 +10,7 @@ import {AppBskyActorDefs as ActorDefs} from '@atproto/api' import {Text} from '../com/util/text/Text' import {useStores} from 'state/index' import {usePalette} from 'lib/hooks/usePalette' -import {isDesktopWeb} from 'platform/detection' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {withAuthRequired} from 'view/com/auth/withAuthRequired' import {observer} from 'mobx-react-lite' import {NativeStackScreenProps} from '@react-navigation/native-stack' @@ -30,6 +30,7 @@ export const ModerationBlockedAccounts = withAuthRequired( observer(({}: Props) => { const pal = usePalette('default') const store = useStores() + const {isTabletOrDesktop} = useWebMediaQueries() const {screen} = useAnalytics() const blockedAccounts = useMemo( () => new BlockedAccountsModel(store), @@ -72,7 +73,7 @@ export const ModerationBlockedAccounts = withAuthRequired( <CenteredView style={[ styles.container, - isDesktopWeb && styles.containerDesktop, + isTabletOrDesktop && styles.containerDesktop, pal.view, pal.border, ]} @@ -83,14 +84,14 @@ export const ModerationBlockedAccounts = withAuthRequired( style={[ styles.description, pal.text, - isDesktopWeb && styles.descriptionDesktop, + isTabletOrDesktop && styles.descriptionDesktop, ]}> Blocked accounts cannot reply in your threads, mention you, or otherwise interact with you. You will not see their content and they will be prevented from seeing yours. </Text> {!blockedAccounts.hasContent ? ( - <View style={[pal.border, !isDesktopWeb && styles.flex1]}> + <View style={[pal.border, !isTabletOrDesktop && styles.flex1]}> <View style={[styles.empty, pal.viewLight]}> <Text type="lg" style={[pal.text, styles.emptyText]}> You have not blocked any accounts yet. To block an account, go @@ -101,7 +102,7 @@ export const ModerationBlockedAccounts = withAuthRequired( </View> ) : ( <FlatList - style={[!isDesktopWeb && styles.flex1]} + style={[!isTabletOrDesktop && styles.flex1]} data={blockedAccounts.blocks} keyExtractor={(item: ActorDefs.ProfileView) => item.did} refreshControl={ @@ -133,11 +134,12 @@ export const ModerationBlockedAccounts = withAuthRequired( const styles = StyleSheet.create({ container: { flex: 1, - paddingBottom: isDesktopWeb ? 0 : 100, + paddingBottom: 100, }, containerDesktop: { borderLeftWidth: 1, borderRightWidth: 1, + paddingBottom: 0, }, title: { textAlign: 'center', diff --git a/src/view/screens/ModerationMuteLists.tsx b/src/view/screens/ModerationMuteLists.tsx index c2771290f..bc933c24e 100644 --- a/src/view/screens/ModerationMuteLists.tsx +++ b/src/view/screens/ModerationMuteLists.tsx @@ -15,9 +15,9 @@ import {ListsList} from 'view/com/lists/ListsList' import {Button} from 'view/com/util/forms/Button' import {NavigationProp} from 'lib/routes/types' import {usePalette} from 'lib/hooks/usePalette' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {CenteredView} from 'view/com/util/Views' import {ViewHeader} from 'view/com/util/ViewHeader' -import {isDesktopWeb} from 'platform/detection' type Props = NativeStackScreenProps< CommonNavigatorParams, @@ -26,6 +26,7 @@ type Props = NativeStackScreenProps< export const ModerationMuteListsScreen = withAuthRequired(({}: Props) => { const pal = usePalette('default') const store = useStores() + const {isTabletOrDesktop} = useWebMediaQueries() const navigation = useNavigation<NavigationProp>() const mutelists: ListsListModel = React.useMemo( @@ -89,7 +90,7 @@ export const ModerationMuteListsScreen = withAuthRequired(({}: Props) => { styles.container, pal.view, pal.border, - isDesktopWeb && styles.containerDesktop, + isTabletOrDesktop && styles.containerDesktop, ]} testID="moderationMutelistsScreen"> <ViewHeader @@ -99,7 +100,7 @@ export const ModerationMuteListsScreen = withAuthRequired(({}: Props) => { /> <ListsList listsList={mutelists} - showAddBtns={isDesktopWeb} + showAddBtns={isTabletOrDesktop} renderEmptyState={renderEmptyState} onPressCreateNew={onPressNewMuteList} /> @@ -110,11 +111,12 @@ export const ModerationMuteListsScreen = withAuthRequired(({}: Props) => { const styles = StyleSheet.create({ container: { flex: 1, - paddingBottom: isDesktopWeb ? 0 : 100, + paddingBottom: 100, }, containerDesktop: { borderLeftWidth: 1, borderRightWidth: 1, + paddingBottom: 0, }, createBtn: { width: 40, diff --git a/src/view/screens/ModerationMutedAccounts.tsx b/src/view/screens/ModerationMutedAccounts.tsx index c638a55d7..eb822270a 100644 --- a/src/view/screens/ModerationMutedAccounts.tsx +++ b/src/view/screens/ModerationMutedAccounts.tsx @@ -10,7 +10,7 @@ import {AppBskyActorDefs as ActorDefs} from '@atproto/api' import {Text} from '../com/util/text/Text' import {useStores} from 'state/index' import {usePalette} from 'lib/hooks/usePalette' -import {isDesktopWeb} from 'platform/detection' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {withAuthRequired} from 'view/com/auth/withAuthRequired' import {observer} from 'mobx-react-lite' import {NativeStackScreenProps} from '@react-navigation/native-stack' @@ -30,6 +30,7 @@ export const ModerationMutedAccounts = withAuthRequired( observer(({}: Props) => { const pal = usePalette('default') const store = useStores() + const {isTabletOrDesktop} = useWebMediaQueries() const {screen} = useAnalytics() const mutedAccounts = useMemo(() => new MutedAccountsModel(store), [store]) @@ -69,7 +70,7 @@ export const ModerationMutedAccounts = withAuthRequired( <CenteredView style={[ styles.container, - isDesktopWeb && styles.containerDesktop, + isTabletOrDesktop && styles.containerDesktop, pal.view, pal.border, ]} @@ -80,13 +81,13 @@ export const ModerationMutedAccounts = withAuthRequired( style={[ styles.description, pal.text, - isDesktopWeb && styles.descriptionDesktop, + isTabletOrDesktop && styles.descriptionDesktop, ]}> Muted accounts have their posts removed from your feed and from your notifications. Mutes are completely private. </Text> {!mutedAccounts.hasContent ? ( - <View style={[pal.border, !isDesktopWeb && styles.flex1]}> + <View style={[pal.border, !isTabletOrDesktop && styles.flex1]}> <View style={[styles.empty, pal.viewLight]}> <Text type="lg" style={[pal.text, styles.emptyText]}> You have not muted any accounts yet. To mute an account, go to @@ -97,7 +98,7 @@ export const ModerationMutedAccounts = withAuthRequired( </View> ) : ( <FlatList - style={[!isDesktopWeb && styles.flex1]} + style={[!isTabletOrDesktop && styles.flex1]} data={mutedAccounts.mutes} keyExtractor={item => item.did} refreshControl={ @@ -129,11 +130,12 @@ export const ModerationMutedAccounts = withAuthRequired( const styles = StyleSheet.create({ container: { flex: 1, - paddingBottom: isDesktopWeb ? 0 : 100, + paddingBottom: 100, }, containerDesktop: { borderLeftWidth: 1, borderRightWidth: 1, + paddingBottom: 0, }, title: { textAlign: 'center', diff --git a/src/view/screens/PostThread.tsx b/src/view/screens/PostThread.tsx index 86b2d3027..a6aafa530 100644 --- a/src/view/screens/PostThread.tsx +++ b/src/view/screens/PostThread.tsx @@ -12,7 +12,7 @@ import {useStores} from 'state/index' import {s} from 'lib/styles' import {useSafeAreaInsets} from 'react-native-safe-area-context' import {clamp} from 'lodash' -import {isDesktopWeb} from 'platform/detection' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' const SHELL_FOOTER_HEIGHT = 44 @@ -26,6 +26,7 @@ export const PostThreadScreen = withAuthRequired(({route}: Props) => { () => new PostThreadModel(store, {uri}), [store, uri], ) + const {isMobile} = useWebMediaQueries() useFocusEffect( React.useCallback(() => { @@ -67,15 +68,15 @@ export const PostThreadScreen = withAuthRequired(({route}: Props) => { return ( <View style={s.hContentRegion}> - <ViewHeader title="Post" /> - <View style={s.hContentRegion}> + {isMobile && <ViewHeader title="Post" />} + <View style={s.flex1}> <PostThreadComponent uri={uri} view={view} onPressReply={onPressReply} /> </View> - {!isDesktopWeb && ( + {isMobile && ( <View style={[ styles.prompt, diff --git a/src/view/screens/PreferencesHomeFeed.tsx b/src/view/screens/PreferencesHomeFeed.tsx index b04f274f7..bd6dd8b39 100644 --- a/src/view/screens/PreferencesHomeFeed.tsx +++ b/src/view/screens/PreferencesHomeFeed.tsx @@ -6,7 +6,8 @@ import {Text} from '../com/util/text/Text' import {useStores} from 'state/index' import {s, colors} from 'lib/styles' import {usePalette} from 'lib/hooks/usePalette' -import {isWeb, isDesktopWeb} from 'platform/detection' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import {isWeb} from 'platform/detection' import {ToggleButton} from 'view/com/util/forms/ToggleButton' import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types' import {ViewHeader} from 'view/com/util/ViewHeader' @@ -50,6 +51,7 @@ type Props = NativeStackScreenProps< export const PreferencesHomeFeed = observer(({navigation}: Props) => { const pal = usePalette('default') const store = useStores() + const {isTabletOrDesktop} = useWebMediaQueries() return ( <CenteredView @@ -58,10 +60,11 @@ export const PreferencesHomeFeed = observer(({navigation}: Props) => { pal.view, pal.border, styles.container, - isDesktopWeb && styles.desktopContainer, + isTabletOrDesktop && styles.desktopContainer, ]}> <ViewHeader title="Home Feed Preferences" showOnDesktop /> - <View style={styles.titleSection}> + <View + style={[styles.titleSection, isTabletOrDesktop && {paddingTop: 20}]}> <Text type="xl" style={[pal.textLight, styles.description]}> Fine-tune the content you see on your home screen. </Text> @@ -122,7 +125,12 @@ export const PreferencesHomeFeed = observer(({navigation}: Props) => { </View> </ScrollView> - <View style={[styles.btnContainer, pal.borderDark]}> + <View + style={[ + styles.btnContainer, + !isTabletOrDesktop && {borderTopWidth: 1, paddingHorizontal: 20}, + pal.borderDark, + ]}> <TouchableOpacity testID="confirmBtn" onPress={() => { @@ -130,7 +138,7 @@ export const PreferencesHomeFeed = observer(({navigation}: Props) => { ? navigation.goBack() : navigation.navigate('Settings') }} - style={[styles.btn, isDesktopWeb && styles.btnDesktop]} + style={[styles.btn, isTabletOrDesktop && styles.btnDesktop]} accessibilityRole="button" accessibilityLabel="Confirm" accessibilityHint=""> @@ -144,15 +152,15 @@ export const PreferencesHomeFeed = observer(({navigation}: Props) => { const styles = StyleSheet.create({ container: { flex: 1, - paddingBottom: isDesktopWeb ? 40 : 90, + paddingBottom: 90, }, desktopContainer: { borderLeftWidth: 1, borderRightWidth: 1, + paddingBottom: 40, }, titleSection: { paddingBottom: 30, - paddingTop: isDesktopWeb ? 20 : 0, }, title: { textAlign: 'center', @@ -184,7 +192,6 @@ const styles = StyleSheet.create({ }, btnContainer: { paddingTop: 20, - borderTopWidth: isDesktopWeb ? 0 : 1, }, dimmed: { opacity: 0.3, diff --git a/src/view/screens/ProfileList.tsx b/src/view/screens/ProfileList.tsx index 3c50fdde0..e86a457b6 100644 --- a/src/view/screens/ProfileList.tsx +++ b/src/view/screens/ProfileList.tsx @@ -14,8 +14,8 @@ import {ListModel} from 'state/models/content/list' import {useStores} from 'state/index' import {usePalette} from 'lib/hooks/usePalette' import {useSetTitle} from 'lib/hooks/useSetTitle' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {NavigationProp} from 'lib/routes/types' -import {isDesktopWeb} from 'platform/detection' import {toShareUrl} from 'lib/strings/url-helpers' import {shareUrl} from 'lib/sharing' import {ListActions} from 'view/com/lists/ListActions' @@ -26,6 +26,7 @@ export const ProfileListScreen = withAuthRequired( observer(({route}: Props) => { const store = useStores() const navigation = useNavigation<NavigationProp>() + const {isTabletOrDesktop} = useWebMediaQueries() const pal = usePalette('default') const {name, rkey} = route.params @@ -131,7 +132,7 @@ export const ProfileListScreen = withAuthRequired( <CenteredView style={[ styles.container, - isDesktopWeb && styles.containerDesktop, + isTabletOrDesktop && styles.containerDesktop, pal.view, pal.border, ]} @@ -155,10 +156,11 @@ export const ProfileListScreen = withAuthRequired( const styles = StyleSheet.create({ container: { flex: 1, - paddingBottom: isDesktopWeb ? 0 : 100, + paddingBottom: 100, }, containerDesktop: { borderLeftWidth: 1, borderRightWidth: 1, + paddingBottom: 0, }, }) diff --git a/src/view/screens/SavedFeeds.tsx b/src/view/screens/SavedFeeds.tsx index aba61e7d9..5055ee76f 100644 --- a/src/view/screens/SavedFeeds.tsx +++ b/src/view/screens/SavedFeeds.tsx @@ -14,11 +14,12 @@ import {usePalette} from 'lib/hooks/usePalette' import {CommonNavigatorParams} from 'lib/routes/types' import {observer} from 'mobx-react-lite' import {useStores} from 'state/index' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {withAuthRequired} from 'view/com/auth/withAuthRequired' import {ViewHeader} from 'view/com/util/ViewHeader' import {CenteredView} from 'view/com/util/Views' import {Text} from 'view/com/util/text/Text' -import {isDesktopWeb, isWeb} from 'platform/detection' +import {isWeb} from 'platform/detection' import {s, colors} from 'lib/styles' import DraggableFlatList, { ShadowDecorator, @@ -37,6 +38,7 @@ export const SavedFeeds = withAuthRequired( observer(({}: Props) => { const pal = usePalette('default') const store = useStores() + const {isMobile, isTabletOrDesktop} = useWebMediaQueries() const {screen} = useAnalytics() const savedFeeds = useMemo(() => store.me.savedFeeds, [store]) @@ -53,7 +55,7 @@ export const SavedFeeds = withAuthRequired( <View style={[ pal.border, - !isDesktopWeb && s.flex1, + isMobile && s.flex1, pal.viewLight, styles.empty, ]}> @@ -62,7 +64,7 @@ export const SavedFeeds = withAuthRequired( </Text> </View> ) - }, [pal]) + }, [pal, isMobile]) const renderListFooterComponent = useCallback(() => { return ( @@ -116,15 +118,11 @@ export const SavedFeeds = withAuthRequired( style={[ s.hContentRegion, pal.border, - isDesktopWeb && styles.desktopContainer, + isTabletOrDesktop && styles.desktopContainer, ]}> - <ViewHeader - title="Edit My Feeds" - showOnDesktop - showBorder={!isDesktopWeb} - /> + <ViewHeader title="Edit My Feeds" showOnDesktop showBorder /> <DraggableFlatList - containerStyle={[isDesktopWeb ? s.hContentRegion : s.flex1]} + containerStyle={[isTabletOrDesktop ? s.hContentRegion : s.flex1]} data={savedFeeds.all} keyExtractor={item => item.data.uri} refreshing={savedFeeds.isRefreshing} diff --git a/src/view/screens/Search.web.tsx b/src/view/screens/Search.web.tsx index 3218b4579..f325b1233 100644 --- a/src/view/screens/Search.web.tsx +++ b/src/view/screens/Search.web.tsx @@ -12,6 +12,7 @@ import { SearchTabNavigatorParams, } from 'lib/routes/types' import {useStores} from 'state/index' +import {CenteredView} from 'view/com/util/Views' import * as Mobile from './SearchMobile' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' @@ -57,9 +58,9 @@ export const SearchScreen = withAuthRequired( if (!isDesktop) { return ( - <View style={styles.scrollContainer}> + <CenteredView style={styles.scrollContainer}> <Mobile.SearchScreen navigation={navigation} route={route} /> - </View> + </CenteredView> ) } diff --git a/src/view/screens/Settings.tsx b/src/view/screens/Settings.tsx index 481d77086..a416bad45 100644 --- a/src/view/screens/Settings.tsx +++ b/src/view/screens/Settings.tsx @@ -35,10 +35,10 @@ import {ToggleButton} from 'view/com/util/forms/ToggleButton' import {SelectableBtn} from 'view/com/util/forms/SelectableBtn' import {usePalette} from 'lib/hooks/usePalette' import {useCustomPalette} from 'lib/hooks/useCustomPalette' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {AccountData} from 'state/models/session' import {useAnalytics} from 'lib/analytics/analytics' import {NavigationProp} from 'lib/routes/types' -import {isDesktopWeb} from 'platform/detection' import {pluralize} from 'lib/strings/helpers' import {formatCount} from 'view/com/util/numeric/format' import Clipboard from '@react-native-clipboard/clipboard' @@ -58,6 +58,7 @@ export const SettingsScreen = withAuthRequired( const pal = usePalette('default') const store = useStores() const navigation = useNavigation<NavigationProp>() + const {isMobile} = useWebMediaQueries() const {screen, track} = useAnalytics() const [isSwitching, setIsSwitching] = React.useState(false) const [debugHeaderEnabled, toggleDebugHeader] = useDebugHeaderSetting( @@ -203,7 +204,7 @@ export const SettingsScreen = withAuthRequired( <ViewHeader title="Settings" /> <ScrollView style={[s.hContentRegion]} - contentContainerStyle={!isDesktopWeb && pal.viewLight} + contentContainerStyle={isMobile && pal.viewLight} scrollIndicatorInsets={{right: 1}}> <View style={styles.spacer20} /> {store.session.currentSession !== undefined ? ( @@ -508,7 +509,7 @@ export const SettingsScreen = withAuthRequired( System log </Text> </TouchableOpacity> - {isDesktopWeb || __DEV__ ? ( + {__DEV__ ? ( <ToggleButton type="default-light" label="Experiment: Use AppView Proxy" diff --git a/src/view/shell/Composer.web.tsx b/src/view/shell/Composer.web.tsx index c0da27da6..e8f7908c2 100644 --- a/src/view/shell/Composer.web.tsx +++ b/src/view/shell/Composer.web.tsx @@ -4,7 +4,7 @@ import {StyleSheet, View} from 'react-native' import {ComposePost} from '../com/composer/Composer' import {ComposerOpts} from 'state/models/ui/shell' import {usePalette} from 'lib/hooks/usePalette' -import {isMobileWeb} from 'platform/detection' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' const BOTTOM_BAR_HEIGHT = 61 @@ -26,6 +26,7 @@ export const Composer = observer( mention?: ComposerOpts['mention'] }) => { const pal = usePalette('default') + const {isMobile} = useWebMediaQueries() // rendering // = @@ -36,7 +37,13 @@ export const Composer = observer( return ( <View style={styles.mask} aria-modal accessibilityViewIsModal> - <View style={[styles.container, pal.view, pal.border]}> + <View + style={[ + styles.container, + isMobile && styles.containerMobile, + pal.view, + pal.border, + ]}> <ComposePost replyTo={replyTo} quote={quote} @@ -66,11 +73,14 @@ const styles = StyleSheet.create({ width: '100%', paddingVertical: 0, paddingHorizontal: 2, - borderRadius: isMobileWeb ? 0 : 8, - marginBottom: isMobileWeb ? BOTTOM_BAR_HEIGHT : 0, + borderRadius: 8, + marginBottom: 0, borderWidth: 1, - maxHeight: isMobileWeb - ? `calc(100% - ${BOTTOM_BAR_HEIGHT}px)` - : 'calc(100% - (40px * 2))', + maxHeight: 'calc(100% - (40px * 2))', + }, + containerMobile: { + borderRadius: 0, + marginBottom: BOTTOM_BAR_HEIGHT, + maxHeight: `calc(100% - ${BOTTOM_BAR_HEIGHT}px)`, }, }) diff --git a/src/view/shell/desktop/LeftNav.tsx b/src/view/shell/desktop/LeftNav.tsx index 50d482fda..087455d3f 100644 --- a/src/view/shell/desktop/LeftNav.tsx +++ b/src/view/shell/desktop/LeftNav.tsx @@ -17,6 +17,7 @@ import {Link} from 'view/com/util/Link' import {LoadingPlaceholder} from 'view/com/util/LoadingPlaceholder' import {usePalette} from 'lib/hooks/usePalette' import {useStores} from 'state/index' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {s, colors} from 'lib/styles' import { HomeIcon, @@ -41,18 +42,28 @@ import {makeProfileLink} from 'lib/routes/links' const ProfileCard = observer(() => { const store = useStores() + const {isDesktop} = useWebMediaQueries() + const size = isDesktop ? 64 : 48 return store.me.handle ? ( - <Link href={makeProfileLink(store.me)} style={styles.profileCard} asAnchor> - <UserAvatar avatar={store.me.avatar} size={64} /> + <Link + href={makeProfileLink(store.me)} + style={[styles.profileCard, !isDesktop && styles.profileCardTablet]} + asAnchor> + <UserAvatar avatar={store.me.avatar} size={size} /> </Link> ) : ( - <View style={styles.profileCard}> - <LoadingPlaceholder width={64} height={64} style={{borderRadius: 64}} /> + <View style={[styles.profileCard, !isDesktop && styles.profileCardTablet]}> + <LoadingPlaceholder + width={size} + height={size} + style={{borderRadius: size}} + /> </View> ) }) function BackBtn() { + const {isTablet} = useWebMediaQueries() const pal = usePalette('default') const navigation = useNavigation<NavigationProp>() const shouldShow = useNavigationState(state => !isStateAtTabRoot(state)) @@ -65,7 +76,7 @@ function BackBtn() { } }, [navigation]) - if (!shouldShow) { + if (!shouldShow || isTablet) { return <></> } return ( @@ -96,6 +107,7 @@ const NavItem = observer( ({count, href, icon, iconFilled, label}: NavItemProps) => { const pal = usePalette('default') const store = useStores() + const {isDesktop, isTablet} = useWebMediaQueries() const [pathName] = React.useMemo(() => router.matchPath(href), [href]) const currentRouteInfo = useNavigationState(state => { if (!state) { @@ -137,17 +149,28 @@ const NavItem = observer( accessibilityRole="tab" accessibilityLabel={label} accessibilityHint=""> - <View style={[styles.navItemIconWrapper]}> + <View + style={[ + styles.navItemIconWrapper, + isTablet && styles.navItemIconWrapperTablet, + ]}> {isCurrent ? iconFilled : icon} {typeof count === 'string' && count ? ( - <Text type="button" style={styles.navItemCount}> + <Text + type="button" + style={[ + styles.navItemCount, + isTablet && styles.navItemCountTablet, + ]}> {count} </Text> ) : null} </View> - <Text type="title" style={[isCurrent ? s.bold : s.normal, pal.text]}> - {label} - </Text> + {isDesktop && ( + <Text type="title" style={[isCurrent ? s.bold : s.normal, pal.text]}> + {label} + </Text> + )} </PressableWithHover> ) }, @@ -156,6 +179,7 @@ const NavItem = observer( function ComposeBtn() { const store = useStores() const {getState} = useNavigation() + const {isTablet} = useWebMediaQueries() const getProfileHandle = () => { const {routes} = getState() @@ -172,6 +196,9 @@ function ComposeBtn() { const onPressCompose = () => store.shell.openComposer({mention: getProfileHandle()}) + if (isTablet) { + return null + } return ( <TouchableOpacity style={[styles.newPostBtn]} @@ -196,28 +223,43 @@ function ComposeBtn() { export const DesktopLeftNav = observer(function DesktopLeftNav() { const store = useStores() const pal = usePalette('default') + const {isDesktop, isTablet} = useWebMediaQueries() return ( - <View style={[styles.leftNav, pal.view]}> + <View + style={[ + styles.leftNav, + isTablet && styles.leftNavTablet, + pal.view, + pal.border, + ]}> {store.session.hasSession && <ProfileCard />} <BackBtn /> <NavItem href="/" - icon={<HomeIcon size={24} style={pal.text} />} + icon={<HomeIcon size={isDesktop ? 24 : 28} style={pal.text} />} iconFilled={ - <HomeIconSolid strokeWidth={4} size={24} style={pal.text} /> + <HomeIconSolid + strokeWidth={4} + size={isDesktop ? 24 : 28} + style={pal.text} + /> } label="Home" /> <NavItem href="/search" icon={ - <MagnifyingGlassIcon2 strokeWidth={2} size={24} style={pal.text} /> + <MagnifyingGlassIcon2 + strokeWidth={2} + size={isDesktop ? 24 : 26} + style={pal.text} + /> } iconFilled={ <MagnifyingGlassIcon2Solid strokeWidth={2} - size={24} + size={isDesktop ? 24 : 26} style={pal.text} /> } @@ -229,14 +271,14 @@ export const DesktopLeftNav = observer(function DesktopLeftNav() { <SatelliteDishIcon strokeWidth={1.75} style={pal.text as FontAwesomeIconStyle} - size={24} + size={isDesktop ? 24 : 28} /> } iconFilled={ <SatelliteDishIconSolid strokeWidth={1.75} style={pal.text as FontAwesomeIconStyle} - size={24} + size={isDesktop ? 24 : 28} /> } label="My Feeds" @@ -244,9 +286,19 @@ export const DesktopLeftNav = observer(function DesktopLeftNav() { <NavItem href="/notifications" count={store.me.notifications.unreadCountLabel} - icon={<BellIcon strokeWidth={2} size={24} style={pal.text} />} + icon={ + <BellIcon + strokeWidth={2} + size={isDesktop ? 24 : 26} + style={pal.text} + /> + } iconFilled={ - <BellIconSolid strokeWidth={1.5} size={24} style={pal.text} /> + <BellIconSolid + strokeWidth={1.5} + size={isDesktop ? 24 : 26} + style={pal.text} + /> } label="Notifications" /> @@ -256,14 +308,14 @@ export const DesktopLeftNav = observer(function DesktopLeftNav() { <HandIcon strokeWidth={5.5} style={pal.text as FontAwesomeIconStyle} - size={24} + size={isDesktop ? 24 : 27} /> } iconFilled={ <FontAwesomeIcon icon="hand" style={pal.text as FontAwesomeIconStyle} - size={20} + size={isDesktop ? 20 : 26} /> } label="Moderation" @@ -271,18 +323,38 @@ export const DesktopLeftNav = observer(function DesktopLeftNav() { {store.session.hasSession && ( <NavItem href={makeProfileLink(store.me)} - icon={<UserIcon strokeWidth={1.75} size={28} style={pal.text} />} + icon={ + <UserIcon + strokeWidth={1.75} + size={isDesktop ? 28 : 30} + style={pal.text} + /> + } iconFilled={ - <UserIconSolid strokeWidth={1.75} size={28} style={pal.text} /> + <UserIconSolid + strokeWidth={1.75} + size={isDesktop ? 28 : 30} + style={pal.text} + /> } label="Profile" /> )} <NavItem href="/settings" - icon={<CogIcon strokeWidth={1.75} size={28} style={pal.text} />} + icon={ + <CogIcon + strokeWidth={1.75} + size={isDesktop ? 28 : 32} + style={pal.text} + /> + } iconFilled={ - <CogIconSolid strokeWidth={1.5} size={28} style={pal.text} /> + <CogIconSolid + strokeWidth={1.5} + size={isDesktop ? 28 : 32} + style={pal.text} + /> } label="Settings" /> @@ -300,12 +372,24 @@ const styles = StyleSheet.create({ maxHeight: 'calc(100vh - 10px)', overflowY: 'auto', }, + leftNavTablet: { + top: 0, + left: 0, + right: 'auto', + borderRightWidth: 1, + height: '100%', + width: 76, + alignItems: 'center', + }, profileCard: { marginVertical: 10, width: 90, paddingLeft: 12, }, + profileCardTablet: { + width: 70, + }, backBtn: { position: 'absolute', @@ -330,6 +414,10 @@ const styles = StyleSheet.create({ height: 28, marginTop: 2, }, + navItemIconWrapperTablet: { + width: 40, + height: 40, + }, navItemCount: { position: 'absolute', top: 0, @@ -341,6 +429,10 @@ const styles = StyleSheet.create({ paddingHorizontal: 4, borderRadius: 6, }, + navItemCountTablet: { + left: 18, + fontSize: 14, + }, newPostBtn: { flexDirection: 'row', @@ -354,10 +446,9 @@ const styles = StyleSheet.create({ marginLeft: 12, marginTop: 20, marginBottom: 10, + gap: 8, }, - newPostBtnIconWrapper: { - marginRight: 8, - }, + newPostBtnIconWrapper: {}, newPostBtnLabel: { color: colors.white, fontSize: 16, diff --git a/src/view/shell/desktop/RightNav.tsx b/src/view/shell/desktop/RightNav.tsx index 6fbd777fb..746bbcf59 100644 --- a/src/view/shell/desktop/RightNav.tsx +++ b/src/view/shell/desktop/RightNav.tsx @@ -9,6 +9,7 @@ import {TextLink} from 'view/com/util/Link' import {FEEDBACK_FORM_URL, HELP_DESK_URL} from 'lib/constants' import {s} from 'lib/styles' import {useStores} from 'state/index' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {pluralize} from 'lib/strings/helpers' import {formatCount} from 'view/com/util/numeric/format' @@ -17,6 +18,11 @@ export const DesktopRightNav = observer(function DesktopRightNav() { const pal = usePalette('default') const palError = usePalette('error') + const {isTablet} = useWebMediaQueries() + if (isTablet) { + return null + } + return ( <View style={[styles.rightNav, pal.view]}> {store.session.hasSession && <DesktopSearch />} diff --git a/src/view/shell/index.web.tsx b/src/view/shell/index.web.tsx index e36439c82..6182f1ba4 100644 --- a/src/view/shell/index.web.tsx +++ b/src/view/shell/index.web.tsx @@ -19,7 +19,7 @@ import {NavigationProp} from 'lib/routes/types' const ShellInner = observer(() => { const store = useStores() - const {isDesktop} = useWebMediaQueries() + const {isDesktop, isMobile} = useWebMediaQueries() const navigator = useNavigation<NavigationProp>() useEffect(() => { @@ -28,11 +28,11 @@ const ShellInner = observer(() => { }) }, [navigator, store.shell]) - const showBottomBar = !isDesktop && !store.onboarding.isActive + const showBottomBar = isMobile && !store.onboarding.isActive const showSideNavs = - isDesktop && store.session.hasSession && !store.onboarding.isActive + !isMobile && store.session.hasSession && !store.onboarding.isActive return ( - <> + <View style={[s.hContentRegion, {overflow: 'hidden'}]}> <View style={s.hContentRegion}> <ErrorBoundary> <FlatNavigator /> @@ -67,7 +67,7 @@ const ShellInner = observer(() => { </View> </TouchableOpacity> )} - </> + </View> ) }) |