diff --git a/.github/workflows/golang-test-lint.yml b/.github/workflows/golang-test-lint.yml index 36e28841d5..c8124dbade 100644 --- a/.github/workflows/golang-test-lint.yml +++ b/.github/workflows/golang-test-lint.yml @@ -21,7 +21,7 @@ jobs: with: go-version: '1.22' - name: Dummy Static Files - run: touch bskyweb/static/js/blah.js && touch bskyweb/static/media/blah.txt + run: touch bskyweb/static/js/blah.js && touch bskyweb/static/css/blah.txt && touch bskyweb/static/media/blah.txt - name: Check run: cd bskyweb/ && make check - name: Build (binary) @@ -38,6 +38,6 @@ jobs: with: go-version: '1.22' - name: Dummy Static Files - run: touch bskyweb/static/js/blah.js && touch bskyweb/static/media/blah.txt + run: touch bskyweb/static/js/blah.js && touch bskyweb/static/css/blah.txt && touch bskyweb/static/media/blah.txt - name: Lint run: cd bskyweb/ && make lint diff --git a/.prettierignore b/.prettierignore index 641a1b8bfc..8ccbae2148 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,4 +1,4 @@ -# Ignore everything except JS-ey code. +# Ignore everything except JS-ey or CSS code. # Based on https://stackoverflow.com/a/70715829/458193 * !**/*.js @@ -7,6 +7,8 @@ !**/*.tsx !*/ +!**/*.css + # More specific ignores go below. .expo android diff --git a/Dockerfile.embedr b/Dockerfile.embedr index 9ff04aa5c7..663cbcfc51 100644 --- a/Dockerfile.embedr +++ b/Dockerfile.embedr @@ -40,6 +40,7 @@ RUN find ./bskyweb/embedr-static && find ./bskyweb/embedr-templates && find ./bs # hack around issue with empty directory and go:embed RUN touch bskyweb/static/js/empty.txt +RUN touch bskyweb/static/css/empty.txt RUN touch bskyweb/static/media/empty.txt # diff --git a/bskyembed/src/index.css b/bskyembed/src/index.css index 23457ec28d..22b2b8be5c 100644 --- a/bskyembed/src/index.css +++ b/bskyembed/src/index.css @@ -4,4 +4,4 @@ .break-word { word-break: break-word; -} \ No newline at end of file +} diff --git a/bskyweb/.gitignore b/bskyweb/.gitignore index 05b3ad7ab5..a63a381f94 100644 --- a/bskyweb/.gitignore +++ b/bskyweb/.gitignore @@ -10,6 +10,10 @@ static/js/*.js static/js/*.map static/js/*.js.LICENSE.txt static/js/empty.txt +static/css/*.css +static/css/*.map +static/css/*.css.LICENSE.txt +static/css/empty.txt static/media/*.png static/media/empty.txt templates/scripts.html diff --git a/bskyweb/static/css/.gitkeep b/bskyweb/static/css/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bskyweb/templates/base.html b/bskyweb/templates/base.html index 609c17c7ce..eaa31aa4a3 100644 --- a/bskyweb/templates/base.html +++ b/bskyweb/templates/base.html @@ -32,387 +32,6 @@ --> - {% include "scripts.html" %} diff --git a/package.json b/package.json index f4c63f9590..245c095f19 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "web": "expo start --web", "use-build-number": "./scripts/useBuildNumberEnv.sh", "use-build-number-with-bump": "./scripts/useBuildNumberEnvWithBump.sh", - "build-web": "expo export:web && node ./scripts/post-web-build.js && cp -v ./web-build/static/js/*.* ./bskyweb/static/js/ && cp -v ./web-build/static/media/* ./bskyweb/static/media/", + "build-web": "expo export:web && node ./scripts/post-web-build.js", "build-all": "yarn intl:build && yarn use-build-number-with-bump eas build --platform all", "build-ios": "yarn use-build-number-with-bump eas build -p ios", "build-android": "yarn use-build-number-with-bump eas build -p android", @@ -339,7 +339,9 @@ }, "lint-staged": { "*{.js,.jsx,.ts,.tsx}": [ - "eslint --cache --fix", + "eslint --cache --fix" + ], + "*{.js,.jsx,.ts,.tsx,.css}": [ "prettier --cache --write --ignore-unknown" ] } diff --git a/scripts/post-web-build.js b/scripts/post-web-build.js index baaa7cb8b7..7bbee38554 100644 --- a/scripts/post-web-build.js +++ b/scripts/post-web-build.js @@ -20,7 +20,30 @@ console.log(`Writing ${templateFile}`) const outputFile = entrypoints .map(name => { const file = path.basename(name) - return `` + const ext = path.extname(file) + + if (ext === '.js') { + return `` + } + if (ext === '.css') { + return `` + } + + return '' }) .join('\n') fs.writeFileSync(templateFile, outputFile) + +function copyFiles(sourceDir, targetDir) { + const files = fs.readdirSync(path.join(projectRoot, sourceDir)) + files.forEach(file => { + const sourcePath = path.join(projectRoot, sourceDir, file) + const targetPath = path.join(projectRoot, targetDir, file) + fs.copyFileSync(sourcePath, targetPath) + console.log(`Copied ${sourcePath} to ${targetPath}`) + }) +} + +copyFiles('web-build/static/js', 'bskyweb/static/js') +copyFiles('web-build/static/css', 'bskyweb/static/css') +copyFiles('web-build/static/media', 'bskyweb/static/media') diff --git a/src/App.web.tsx b/src/App.web.tsx index c81ed10d33..1664812d08 100644 --- a/src/App.web.tsx +++ b/src/App.web.tsx @@ -1,5 +1,6 @@ -import 'lib/sentry' // must be near top -import 'view/icons' +import '#/lib/sentry' // must be near top +import '#/view/icons' +import './style.css' import React, {useEffect, useState} from 'react' import {KeyboardProvider} from 'react-native-keyboard-controller' diff --git a/src/alf/fonts.ts b/src/alf/fonts.ts index 08cfd9f42d..b11ce939f8 100644 --- a/src/alf/fonts.ts +++ b/src/alf/fonts.ts @@ -1,5 +1,3 @@ -import {useFonts as defaultUseFonts} from 'expo-font' - import {isWeb} from '#/platform/detection' import {Device, device} from '#/storage' @@ -34,39 +32,6 @@ export function setFontFamily(fontFamily: Device['fontFamily']) { device.set(['fontFamily'], fontFamily) } -/* - * IMPORTANT: This is unused. Expo statically extracts these fonts, but we load - * them manually so that we can parallelize the loading along with the JS - * bundle. - * - * See `#/alf/util/useFonts` for the actually used hooks. - * - * All used fonts MUST be configured here. Unused fonts are commented out, but - * the files are there if we need them. - */ -export function DO_NOT_USE() { - return defaultUseFonts({ - // 'Inter-Thin': require('../../assets/fonts/inter/Inter-Thin.otf'), - // 'Inter-ThinItalic': require('../../assets/fonts/inter/Inter-ThinItalic.otf'), - // 'Inter-ExtraLight': require('../../assets/fonts/inter/Inter-ExtraLight.otf'), - // 'Inter-ExtraLightItalic': require('../../assets/fonts/inter/Inter-ExtraLightItalic.otf'), - // 'Inter-Light': require('../../assets/fonts/inter/Inter-Light.otf'), - // 'Inter-LightItalic': require('../../assets/fonts/inter/Inter-LightItalic.otf'), - 'Inter-Regular': require('../../assets/fonts/inter/Inter-Regular.otf'), - 'Inter-Italic': require('../../assets/fonts/inter/Inter-Italic.otf'), - // 'Inter-Medium': require('../../assets/fonts/inter/Inter-Medium.otf'), - // 'Inter-MediumItalic': require('../../assets/fonts/inter/Inter-MediumItalic.otf'), - 'Inter-SemiBold': require('../../assets/fonts/inter/Inter-SemiBold.otf'), - 'Inter-SemiBoldItalic': require('../../assets/fonts/inter/Inter-SemiBoldItalic.otf'), - // 'Inter-Bold': require('../../assets/fonts/inter/Inter-Bold.otf'), - // 'Inter-BoldItalic': require('../../assets/fonts/inter/Inter-BoldItalic.otf'), - 'Inter-ExtraBold': require('../../assets/fonts/inter/Inter-ExtraBold.otf'), - 'Inter-ExtraBoldItalic': require('../../assets/fonts/inter/Inter-ExtraBoldItalic.otf'), - // 'Inter-Black': require('../../assets/fonts/inter/Inter-Black.otf'), - // 'Inter-BlackItalic': require('../../assets/fonts/inter/Inter-BlackItalic.otf'), - }) -} - /* * Unused fonts are commented out, but the files are there if we need them. */ diff --git a/src/alf/themes.ts b/src/alf/themes.ts index f5d2247f9f..9f7ec5c673 100644 --- a/src/alf/themes.ts +++ b/src/alf/themes.ts @@ -183,7 +183,7 @@ export function createThemes({ } as const const darkPalette: Palette = { - white: color.gray_0, + white: color.gray_25, black: color.trueBlack, contrast_25: color.gray_975, diff --git a/src/components/LabelingServiceCard/index.tsx b/src/components/LabelingServiceCard/index.tsx index 9f4491aafc..03b8ece6b1 100644 --- a/src/components/LabelingServiceCard/index.tsx +++ b/src/components/LabelingServiceCard/index.tsx @@ -52,13 +52,14 @@ export function Title({value}: {value: string}) { } export function Description({value, handle}: {value?: string; handle: string}) { + const {_} = useLingui() return value ? ( ) : ( - - By {sanitizeHandle(handle, '@')} + + {_(msg`By ${sanitizeHandle(handle, '@')}`)} ) } diff --git a/src/components/ListCard.tsx b/src/components/ListCard.tsx index 35bbac53e8..ed5838fb04 100644 --- a/src/components/ListCard.tsx +++ b/src/components/ListCard.tsx @@ -7,7 +7,8 @@ import { moderateUserList, ModerationUI, } from '@atproto/api' -import {Trans} from '@lingui/macro' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useQueryClient} from '@tanstack/react-query' import {sanitizeHandle} from '#/lib/strings/handles' @@ -111,6 +112,7 @@ export function TitleAndByline({ modUi?: ModerationUI }) { const t = useTheme() + const {_} = useLingui() const {currentAccount} = useSession() return ( @@ -140,15 +142,12 @@ export function TitleAndByline({ {creator && ( - {purpose === MODLIST ? ( - - Moderation list by {sanitizeHandle(creator.handle, '@')} - - ) : ( - List by {sanitizeHandle(creator.handle, '@')} - )} + {purpose === MODLIST + ? _(msg`Moderation list by ${sanitizeHandle(creator.handle, '@')}`) + : _(msg`List by ${sanitizeHandle(creator.handle, '@')}`)} )} diff --git a/src/components/ProfileCard.tsx b/src/components/ProfileCard.tsx index aff11c4f37..50b34ba99d 100644 --- a/src/components/ProfileCard.tsx +++ b/src/components/ProfileCard.tsx @@ -181,6 +181,7 @@ export function NameAndHandle({ {name} {handle} diff --git a/src/components/StarterPack/StarterPackCard.tsx b/src/components/StarterPack/StarterPackCard.tsx index 687a7487f3..ead9c92480 100644 --- a/src/components/StarterPack/StarterPackCard.tsx +++ b/src/components/StarterPack/StarterPackCard.tsx @@ -2,7 +2,7 @@ import React from 'react' import {View} from 'react-native' import {Image} from 'expo-image' import {AppBskyGraphDefs, AppBskyGraphStarterpack, AtUri} from '@atproto/api' -import {msg, Trans} from '@lingui/macro' +import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useQueryClient} from '@tanstack/react-query' @@ -69,13 +69,10 @@ export function Card({ {record.name} - - - Starter pack by{' '} - {creator?.did === currentAccount?.did - ? _(msg`you`) - : `@${sanitizeHandle(creator.handle)}`} - + + {creator?.did === currentAccount?.did + ? _(msg`Starter pack by you`) + : _(msg`Starter pack by ${sanitizeHandle(creator.handle, '@')}`)} diff --git a/src/components/Typography.tsx b/src/components/Typography.tsx index 7c13241e2c..501e23872f 100644 --- a/src/components/Typography.tsx +++ b/src/components/Typography.tsx @@ -159,9 +159,7 @@ export function Text({ } if (emoji && !childIsString(children)) { - throw new Error( - 'Text: when , children can only be strings.', - ) + logger.error('Text: when , children can only be strings.') } } diff --git a/src/lib/themes.ts b/src/lib/themes.ts index 5030799932..eb11872fa3 100644 --- a/src/lib/themes.ts +++ b/src/lib/themes.ts @@ -325,11 +325,11 @@ export const darkTheme: Theme = { textInverted: colors.green2, }, inverted: { - background: lightPalette.white, + background: darkPalette.white, backgroundLight: lightPalette.contrast_50, text: lightPalette.black, textLight: lightPalette.contrast_700, - textInverted: lightPalette.white, + textInverted: darkPalette.white, link: lightPalette.primary_500, border: lightPalette.contrast_100, borderDark: lightPalette.contrast_200, diff --git a/src/screens/Profile/Header/Handle.tsx b/src/screens/Profile/Header/Handle.tsx index 0344f1a234..ba869b6626 100644 --- a/src/screens/Profile/Header/Handle.tsx +++ b/src/screens/Profile/Header/Handle.tsx @@ -1,11 +1,12 @@ import React from 'react' import {View} from 'react-native' import {AppBskyActorDefs} from '@atproto/api' -import {Trans} from '@lingui/macro' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' +import {isInvalidHandle} from '#/lib/strings/handles' +import {isIOS} from '#/platform/detection' import {Shadow} from '#/state/cache/types' -import {isInvalidHandle} from 'lib/strings/handles' -import {isIOS} from 'platform/detection' import {atoms as a, useTheme, web} from '#/alf' import {NewskieDialog} from '#/components/NewskieDialog' import {Text} from '#/components/Typography' @@ -18,6 +19,7 @@ export function ProfileHeaderHandle({ disableTaps?: boolean }) { const t = useTheme() + const {_} = useLingui() const invalidHandle = isInvalidHandle(profile.handle) const blockHide = profile.viewer?.blocking || profile.viewer?.blockedBy return ( @@ -33,6 +35,7 @@ export function ProfileHeaderHandle({ ) : undefined} - {invalidHandle ? ⚠Invalid Handle : `@${profile.handle}`} + {invalidHandle ? _(msg`⚠Invalid Handle`) : `@${profile.handle}`} ) diff --git a/src/style.css b/src/style.css new file mode 100644 index 0000000000..ebb0471584 --- /dev/null +++ b/src/style.css @@ -0,0 +1,399 @@ +@font-face { + font-family: 'Inter-Regular'; + src: local('Inter-Regular'), + url(/assets/fonts/inter/Inter-Regular.otf) format('font/otf'); + font-weight: 400; + font-style: normal; + font-display: swap; +} +@font-face { + font-family: 'Inter-Italic'; + src: local('Inter-Italic'), + url(/assets/fonts/inter/Inter-Italic.otf) format('font/otf'); + font-weight: 400; + font-style: italic; + font-display: swap; +} +/* +@font-face { + font-family: "Inter-Medium"; + src: local("Inter-Medium"), url(/assets/fonts/inter/Inter-Medium.otf) format("font/otf"); + font-weight: 500; + font-style: normal; + font-display: swap; +} +@font-face { + font-family: "Inter-MediumItalic"; + src: local("Inter-MediumItalic"), url(/assets/fonts/inter/Inter-MediumItalic.otf) format("font/otf"); + font-weight: 500; + font-style: italic; + font-display: swap; +} +*/ +@font-face { + font-family: 'Inter-SemiBold'; + src: local('Inter-SemiBold'), + url(/assets/fonts/inter/Inter-SemiBold.otf) format('font/otf'); + font-weight: 600; + font-style: normal; + font-display: swap; +} +@font-face { + font-family: 'Inter-SemiBoldItalic'; + src: local('Inter-SemiBoldItalic'), + url(/assets/fonts/inter/Inter-SemiBoldItalic.otf) format('font/otf'); + font-weight: 600; + font-style: italic; + font-display: swap; +} +/* +@font-face { + font-family: "Inter-Bold"; + src: local("Inter-Bold"), url(/assets/fonts/inter/Inter-Bold.otf) format("font/otf"); + font-weight: 700; + font-style: normal; + font-display: swap; +} +@font-face { + font-family: "Inter-BoldItalic"; + src: local("Inter-BoldItalic"), url(/assets/fonts/inter/Inter-BoldItalic.otf) format("font/otf"); + font-weight: 700; + font-style: italic; + font-display: swap; +} +*/ +@font-face { + font-family: 'Inter-ExtraBold'; + src: local('Inter-ExtraBold'), + url(/assets/fonts/inter/Inter-ExtraBold.otf) format('font/otf'); + font-weight: 800; + font-style: normal; + font-display: swap; +} +@font-face { + font-family: 'Inter-ExtraBoldItalic'; + src: local('Inter-ExtraBoldItalic'), + url(/assets/fonts/inter/Inter-ExtraBoldItalic.otf) format('font/otf'); + font-weight: 800; + font-style: italic; + font-display: swap; +} +/* +@font-face { + font-family: "Inter-Black"; + src: local("Inter-Black"), url(/assets/fonts/inter/Inter-Black.otf) format("font/otf"); + font-weight: 900; + font-style: normal; + font-display: swap; +} +@font-face { + font-family: "Inter-BlackItalic"; + src: local("Inter-BlackItalic"), url(/assets/fonts/inter/Inter-BlackItalic.otf) format("font/otf"); + font-weight: 900; + font-style: italic; + font-display: swap; +} +*/ + +/** + * Extend the react-native-web reset: + * https://github.com/necolas/react-native-web/blob/master/packages/react-native-web/src/exports/StyleSheet/initialRules.js + */ +html, +body, +#root { + width: 100%; + /* To smooth any scrolling behavior */ + -webkit-overflow-scrolling: touch; + margin: 0px; + padding: 0px; + /* Allows content to fill the viewport and go beyond the bottom */ + min-height: 100%; +} +#root { + flex-shrink: 0; + flex-basis: auto; + flex-grow: 1; + display: flex; + flex: 1; +} + +html { + /* Prevent text size change on orientation change https://gist.github.com/tfausak/2222823#file-ios-8-web-app-html-L138 */ + -webkit-text-size-adjust: 100%; + height: calc(100% + env(safe-area-inset-top)); + scrollbar-gutter: stable both-edges; +} +html, +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, + 'Liberation Sans', Helvetica, Arial, sans-serif; +} + +#preload { + width: 100px; + position: fixed; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); +} + +/* Buttons and inputs have a font set by UA, so we'll have to reset that */ +button, +input, +textarea { + font: inherit; + line-height: inherit; +} + +/* Color theming */ +/* Default will always be white */ +:root { + --text: black; + --background: white; + --backgroundLight: hsl(211, 20%, 95%); +} +/* This gives us a black background when system is dark and we have not loaded the theme/color scheme values in JS */ +@media (prefers-color-scheme: dark) { + :root { + --text: white; + --background: black; + --backgroundLight: hsl(211, 20%, 20%); + color-scheme: dark; + } +} + +/* Overwrite those preferences with the selected theme */ +html.theme--light { + --text: black; + --background: white; + --backgroundLight: hsl(211, 20%, 95%); +} +html.theme--dark { + --text: white; + --background: black; + --backgroundLight: hsl(211, 20%, 20%); + color-scheme: dark; +} +html.theme--dim { + --text: white; + --background: hsl(211, 20%, 4%); + --backgroundLight: hsl(211, 20%, 10%); + color-scheme: dark; +} + +/* Remove autofill styles on Webkit */ +input:autofill, +input:-webkit-autofill, +input:-webkit-autofill:hover, +input:-webkit-autofill:focus, +input:-webkit-autofill:active { + -webkit-background-clip: text; + -webkit-text-fill-color: var(--text); + transition: background-color 5000s ease-in-out 0s; + box-shadow: inset 0 0 20px 20px var(--background); + background: var(--background); + color: var(--text); +} +/* Force left-align date/time inputs on iOS mobile */ +input::-webkit-date-and-time-value { + text-align: left; +} + +body { + display: flex; + /* Allows you to scroll below the viewport; default value is visible */ + overflow-y: auto; + overscroll-behavior-y: none; + text-rendering: optimizeLegibility; + background-color: var(--background); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -ms-overflow-style: scrollbar; + font-synthesis-weight: none; +} + +/* Remove default link styling */ +a { + color: inherit; +} +a[role='link']:hover { + text-decoration: underline; +} +a[role='link'][data-no-underline='1']:hover { + text-decoration: none; +} + +/* Styling hacks */ +*[data-word-wrap] { + word-break: break-word; +} +*[data-stable-gutters] { + scrollbar-gutter: stable both-edges; +} + +/* ProseMirror */ +.ProseMirror { + font: 18px -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, + 'Liberation Sans', Helvetica, Arial, sans-serif; + min-height: 140px; +} +.ProseMirror-dark { + color: white; +} +.ProseMirror p { + margin: 0; +} +.ProseMirror p.is-editor-empty:first-child::before { + color: #8d8e96; + content: attr(data-placeholder); + float: left; + height: 0; + pointer-events: none; +} +.ProseMirror .mention { + color: #0085ff; +} +.ProseMirror a, +.ProseMirror .autolink { + color: #0085ff; +} +/* OLLIE: TODO -- this is not accessible */ +/* Remove focus state on inputs */ +.ProseMirror-focused { + outline: 0; +} +textarea:focus, +input:focus { + outline: 0; +} +.tippy-content .items { + width: fit-content; +} + +/* Tooltips */ +[data-tooltip] { + position: relative; + z-index: 10; +} +[data-tooltip]::after { + content: attr(data-tooltip); + display: none; + position: absolute; + bottom: 0; + left: 50%; + transform: translateY(100%) translateY(8px) translateX(-50%); + padding: 4px 10px; + border-radius: 10px; + background: var(--backgroundLight); + color: var(--text); + text-align: center; + white-space: nowrap; + font-size: 12px; + z-index: 10; +} +[data-tooltip]::before { + content: ''; + display: none; + position: absolute; + border-bottom: 6px solid var(--backgroundLight); + border-left: 6px solid transparent; + border-right: 6px solid transparent; + bottom: 0; + left: 50%; + transform: translateY(100%) translateY(2px) translateX(-50%); + z-index: 10; +} +[data-tooltip]:hover::after, +[data-tooltip]:hover::before { + display: block; +} + +/* NativeDropdown component */ +.radix-dropdown-item:focus, +.nativeDropdown-item:focus { + outline: none; +} + +/* Spinner component */ +@keyframes rotate { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} +.rotate-500ms { + position: absolute; + inset: 0; + animation: rotate 500ms linear infinite; +} + +@keyframes avatarHoverFadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes avatarHoverFadeOut { + from { + opacity: 1; + } + to { + opacity: 0; + } +} + +.force-no-clicks > *, +.force-no-clicks * { + pointer-events: none !important; +} + +input[type='range'][orient='vertical'] { + writing-mode: vertical-lr; + direction: rtl; + appearance: slider-vertical; + width: 16px; + vertical-align: bottom; + -webkit-appearance: none; + appearance: none; + background: transparent; + cursor: pointer; +} + +input[type='range'][orient='vertical']::-webkit-slider-runnable-track { + background: white; + height: 100%; + width: 4px; + border-radius: 4px; +} + +input[type='range'][orient='vertical']::-moz-range-track { + background: white; + height: 100%; + width: 4px; + border-radius: 4px; +} + +input[type='range']::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + border-radius: 50%; + background-color: white; + height: 16px; + width: 16px; + margin-left: -6px; +} + +input[type='range'][orient='vertical']::-moz-range-thumb { + border: none; + border-radius: 50%; + background-color: white; + height: 16px; + width: 16px; + margin-left: -6px; +} diff --git a/src/view/com/pager/TabBar.tsx b/src/view/com/pager/TabBar.tsx index 59bb77e367..83de3775c7 100644 --- a/src/view/com/pager/TabBar.tsx +++ b/src/view/com/pager/TabBar.tsx @@ -1,9 +1,9 @@ import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react' import {LayoutChangeEvent, ScrollView, StyleSheet, View} from 'react-native' +import {usePalette} from '#/lib/hooks/usePalette' +import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {isNative} from '#/platform/detection' -import {usePalette} from 'lib/hooks/usePalette' -import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {PressableWithHover} from '../util/PressableWithHover' import {Text} from '../util/text/Text' import {DraggableScrollView} from './DraggableScrollView' @@ -131,13 +131,14 @@ export function TabBar({ (itemRefs.current[i] = node)} + ref={node => (itemRefs.current[i] = node as any)} onLayout={e => onItemLayout(e, i)} style={styles.item} hoverStyle={pal.viewLight} onPress={() => onPressItem(i)}> - + {sanitizeHandle(post.author.handle, '@')} diff --git a/src/view/com/profile/ProfileCard.tsx b/src/view/com/profile/ProfileCard.tsx index 611e7957cd..eab8611dd4 100644 --- a/src/view/com/profile/ProfileCard.tsx +++ b/src/view/com/profile/ProfileCard.tsx @@ -113,7 +113,7 @@ export function ProfileCard({ moderation.ui('displayName'), )} - + {sanitizeHandle(profile.handle, '@')} } -export const PressableWithHover = forwardRef(function PressableWithHoverImpl( - { - children, - style, - hoverStyle, - ...props - }: PropsWithChildren, - ref: Ref, +export const PressableWithHover = forwardRef< + View, + PropsWithChildren +>(function PressableWithHoverImpl( + {children, style, hoverStyle, ...props}, + ref, ) { - const [isHovering, setIsHovering] = useState(false) - - const onHoverIn = useCallback(() => setIsHovering(true), [setIsHovering]) - const onHoverOut = useCallback(() => setIsHovering(false), [setIsHovering]) - style = - typeof style !== 'function' && isHovering - ? addStyle(style, hoverStyle) - : style + const { + state: hovered, + onIn: onHoverIn, + onOut: onHoverOut, + } = useInteractionState() return ( diff --git a/src/view/com/util/post-embeds/VideoEmbedInner/web-controls/ControlButton.tsx b/src/view/com/util/post-embeds/VideoEmbedInner/web-controls/ControlButton.tsx index 6b509d09a3..8ffe482a8f 100644 --- a/src/view/com/util/post-embeds/VideoEmbedInner/web-controls/ControlButton.tsx +++ b/src/view/com/util/post-embeds/VideoEmbedInner/web-controls/ControlButton.tsx @@ -1,8 +1,8 @@ import React from 'react' import {SvgProps} from 'react-native-svg' -import {atoms as a, useTheme} from '#/alf' -import {Button} from '#/components/Button' +import {atoms as a, useTheme, web} from '#/alf' +import {PressableWithHover} from '../../../PressableWithHover' export function ControlButton({ active, @@ -21,19 +21,21 @@ export function ControlButton({ }) { const t = useTheme() return ( - + ) } diff --git a/src/view/com/util/post-embeds/VideoEmbedInner/web-controls/VideoControls.tsx b/src/view/com/util/post-embeds/VideoEmbedInner/web-controls/VideoControls.tsx index 5bd7e0d179..2d1427347d 100644 --- a/src/view/com/util/post-embeds/VideoEmbedInner/web-controls/VideoControls.tsx +++ b/src/view/com/util/post-embeds/VideoEmbedInner/web-controls/VideoControls.tsx @@ -358,9 +358,8 @@ export function Controls({ style={[ a.flex_1, a.px_xs, - a.pt_2xs, - a.pb_md, - a.gap_md, + a.pb_sm, + a.gap_sm, a.flex_row, a.align_center, ]}> @@ -373,7 +372,11 @@ export function Controls({ onPress={onPressPlayPause} /> - + {formatTime(currentTime)} / {formatTime(duration)} {hasSubtitleTrack && ( diff --git a/src/view/com/util/text/Text.tsx b/src/view/com/util/text/Text.tsx index 9e4f7e5f5d..3d885480cc 100644 --- a/src/view/com/util/text/Text.tsx +++ b/src/view/com/util/text/Text.tsx @@ -56,9 +56,7 @@ export function Text({ } if (emoji && !childIsString(children)) { - throw new Error( - 'Text: when , children can only be strings.', - ) + logger.error('Text: when , children can only be strings.') } } diff --git a/src/view/screens/Search/Search.tsx b/src/view/screens/Search/Search.tsx index e1e412648b..07d762c0fe 100644 --- a/src/view/screens/Search/Search.tsx +++ b/src/view/screens/Search/Search.tsx @@ -959,6 +959,7 @@ function SearchHistory({ accessibilityIgnoresInvertColors /> {profile.displayName || profile.handle} diff --git a/src/view/shell/desktop/Search.tsx b/src/view/shell/desktop/Search.tsx index 1ba2d3f3db..b43dbcce32 100644 --- a/src/view/shell/desktop/Search.tsx +++ b/src/view/shell/desktop/Search.tsx @@ -16,19 +16,19 @@ import {useLingui} from '@lingui/react' import {StackActions, useNavigation} from '@react-navigation/native' import {useQueryClient} from '@tanstack/react-query' +import {usePalette} from '#/lib/hooks/usePalette' import {makeProfileLink} from '#/lib/routes/links' +import {NavigationProp} from '#/lib/routes/types' import {sanitizeDisplayName} from '#/lib/strings/display-names' import {sanitizeHandle} from '#/lib/strings/handles' import {s} from '#/lib/styles' import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useActorAutocompleteQuery} from '#/state/queries/actor-autocomplete' -import {usePalette} from 'lib/hooks/usePalette' -import {NavigationProp} from 'lib/routes/types' -import {precacheProfile} from 'state/queries/profile' +import {precacheProfile} from '#/state/queries/profile' +import {SearchInput} from '#/view/com/util/forms/SearchInput' import {Link} from '#/view/com/util/Link' +import {Text} from '#/view/com/util/text/Text' import {UserAvatar} from '#/view/com/util/UserAvatar' -import {SearchInput} from 'view/com/util/forms/SearchInput' -import {Text} from 'view/com/util/text/Text' import {atoms as a} from '#/alf' let SearchLinkCard = ({ @@ -126,6 +126,7 @@ let SearchProfileCard = ({ /> --> - -