diff options
Diffstat (limited to 'src/lib')
-rw-r--r-- | src/lib/ThemeContext.tsx | 28 | ||||
-rw-r--r-- | src/lib/api/feed-manip.ts | 17 | ||||
-rw-r--r-- | src/lib/hooks/useAuxClick.ts | 2 | ||||
-rw-r--r-- | src/lib/hooks/useAuxClick.web.ts | 43 | ||||
-rw-r--r-- | src/lib/media/manip.web.ts | 3 | ||||
-rw-r--r-- | src/lib/strings/embed-player.ts | 13 | ||||
-rw-r--r-- | src/lib/strings/password.ts | 19 | ||||
-rw-r--r-- | src/lib/themes.ts | 13 |
8 files changed, 72 insertions, 66 deletions
diff --git a/src/lib/ThemeContext.tsx b/src/lib/ThemeContext.tsx index 38bd199cb..63e2beeb1 100644 --- a/src/lib/ThemeContext.tsx +++ b/src/lib/ThemeContext.tsx @@ -1,11 +1,7 @@ import React, {ReactNode, createContext, useContext} from 'react' -import { - TextStyle, - useColorScheme, - ViewStyle, - ColorSchemeName, -} from 'react-native' -import {darkTheme, defaultTheme} from './themes' +import {TextStyle, ViewStyle} from 'react-native' +import {darkTheme, defaultTheme, dimTheme} from './themes' +import {ThemeName} from '#/alf/themes' export type ColorScheme = 'light' | 'dark' @@ -84,23 +80,31 @@ export interface Theme { export interface ThemeProviderProps { children?: ReactNode - theme?: 'light' | 'dark' | 'system' + theme: ThemeName } export const ThemeContext = createContext<Theme>(defaultTheme) export const useTheme = () => useContext(ThemeContext) -function getTheme(theme: ColorSchemeName) { - return theme === 'dark' ? darkTheme : defaultTheme +function getTheme(theme: ThemeName) { + switch (theme) { + case 'light': + return defaultTheme + case 'dim': + return dimTheme + case 'dark': + return darkTheme + default: + return defaultTheme + } } export const ThemeProvider: React.FC<ThemeProviderProps> = ({ theme, children, }) => { - const colorScheme = useColorScheme() - const themeValue = getTheme(theme === 'system' ? colorScheme : theme) + const themeValue = getTheme(theme) return ( <ThemeContext.Provider value={themeValue}>{children}</ThemeContext.Provider> diff --git a/src/lib/api/feed-manip.ts b/src/lib/api/feed-manip.ts index c964693c4..227062592 100644 --- a/src/lib/api/feed-manip.ts +++ b/src/lib/api/feed-manip.ts @@ -168,14 +168,25 @@ export class FeedTuner { const selfReplyUri = getSelfReplyUri(item) if (selfReplyUri) { - const parent = slices.find(item2 => - item2.isNextInThread(selfReplyUri), + const index = slices.findIndex(slice => + slice.isNextInThread(selfReplyUri), ) - if (parent) { + + if (index !== -1) { + const parent = slices[index] + parent.insert(item) + + // If our slice isn't currently on the top, reinsert it to the top. + if (index !== 0) { + slices.splice(index, 1) + slices.unshift(parent) + } + continue } } + slices.unshift(new FeedViewPostsSlice([item])) } } diff --git a/src/lib/hooks/useAuxClick.ts b/src/lib/hooks/useAuxClick.ts deleted file mode 100644 index ab6fd4365..000000000 --- a/src/lib/hooks/useAuxClick.ts +++ /dev/null @@ -1,2 +0,0 @@ -// does nothing in native -export const useAuxClick = () => {} diff --git a/src/lib/hooks/useAuxClick.web.ts b/src/lib/hooks/useAuxClick.web.ts deleted file mode 100644 index ca9811615..000000000 --- a/src/lib/hooks/useAuxClick.web.ts +++ /dev/null @@ -1,43 +0,0 @@ -import {useEffect} from 'react' - -// This is the handler for the middle mouse button click on the feed. -// Normally, we would do this via `onAuxClick` handler on each link element -// However, that handler is not supported on react-native-web and there are some -// discrepancies between various browsers (i.e: safari doesn't trigger it and routes through click event) -// So, this temporary alternative is meant to bridge the gap in an efficient way until the support improves. -export const useAuxClick = () => { - const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent) - useEffect(() => { - // On the web, it should always be there but in case it gets accidentally included in native builds - const wrapperEl = document?.body - - // Safari already handles auxclick event as click+metaKey so we need to avoid doing this there in case it becomes recursive - if (wrapperEl && !isSafari) { - const handleAuxClick = (e: MouseEvent & {target: HTMLElement}) => { - // Only handle the middle mouse button click - // Only handle if the clicked element itself or one of its ancestors is a link - if ( - e.button !== 1 || - e.target.closest('a') || - e.target.tagName === 'A' - ) { - return - } - - // On the original element, trigger a click event with metaKey set to true so that it triggers - // the browser's default behavior of opening the link in a new tab - e.target.dispatchEvent( - new MouseEvent('click', {metaKey: true, bubbles: true}), - ) - } - - // @ts-ignore For web only - wrapperEl.addEventListener('auxclick', handleAuxClick) - - return () => { - // @ts-ignore For web only - wrapperEl?.removeEventListener('auxclick', handleAuxClick) - } - } - }, [isSafari]) -} diff --git a/src/lib/media/manip.web.ts b/src/lib/media/manip.web.ts index bdf6836a1..522aa2e51 100644 --- a/src/lib/media/manip.web.ts +++ b/src/lib/media/manip.web.ts @@ -132,6 +132,9 @@ function createResizedImage( ctx.drawImage(img, 0, 0, w, h) resolve(canvas.toDataURL('image/jpeg', quality)) }) + img.addEventListener('error', ev => { + reject(ev.error) + }) img.src = dataUri }) } diff --git a/src/lib/strings/embed-player.ts b/src/lib/strings/embed-player.ts index 3270b6f07..21a575b91 100644 --- a/src/lib/strings/embed-player.ts +++ b/src/lib/strings/embed-player.ts @@ -1,4 +1,5 @@ -import {Dimensions, Platform} from 'react-native' +import {Dimensions} from 'react-native' +import {isWeb} from 'platform/detection' const {height: SCREEN_HEIGHT} = Dimensions.get('window') export const embedPlayerSources = [ @@ -73,7 +74,7 @@ export function parseEmbedPlayerFromUrl( return { type: 'youtube_video', source: 'youtube', - playerUri: `https://www.youtube.com/embed/${videoId}?autoplay=1&playsinline=1&start=${seek}`, + playerUri: `https://bsky.app/iframe/youtube.html?videoId=${videoId}&start=${seek}`, } } } @@ -92,7 +93,7 @@ export function parseEmbedPlayerFromUrl( type: page === 'shorts' ? 'youtube_short' : 'youtube_video', source: page === 'shorts' ? 'youtubeShorts' : 'youtube', hideDetails: page === 'shorts' ? true : undefined, - playerUri: `https://www.youtube.com/embed/${videoId}?autoplay=1&playsinline=1&start=${seek}`, + playerUri: `https://bsky.app/iframe/youtube.html?videoId=${videoId}&start=${seek}`, } } } @@ -103,8 +104,10 @@ export function parseEmbedPlayerFromUrl( urlp.hostname === 'www.twitch.tv' || urlp.hostname === 'm.twitch.tv' ) { - const parent = - Platform.OS === 'web' ? window.location.hostname : 'localhost' + const parent = isWeb + ? // @ts-ignore only for web + window.location.hostname + : 'localhost' const [_, channelOrVideo, clipOrId, id] = urlp.pathname.split('/') diff --git a/src/lib/strings/password.ts b/src/lib/strings/password.ts new file mode 100644 index 000000000..e7735b90e --- /dev/null +++ b/src/lib/strings/password.ts @@ -0,0 +1,19 @@ +// Regex for base32 string for testing reset code +const RESET_CODE_REGEX = /^[A-Z2-7]{5}-[A-Z2-7]{5}$/ + +export function checkAndFormatResetCode(code: string): string | false { + // Trim the reset code + let fixed = code.trim().toUpperCase() + + // Add a dash if needed + if (fixed.length === 10) { + fixed = `${fixed.slice(0, 5)}-${fixed.slice(5, 10)}` + } + + // Check that it is a valid format + if (!RESET_CODE_REGEX.test(fixed)) { + return false + } + + return fixed +} diff --git a/src/lib/themes.ts b/src/lib/themes.ts index 2d4515c77..9a3880b92 100644 --- a/src/lib/themes.ts +++ b/src/lib/themes.ts @@ -2,7 +2,7 @@ import {Platform} from 'react-native' import type {Theme} from './ThemeContext' import {colors} from './styles' -import {darkPalette, lightPalette} from '#/alf/themes' +import {darkPalette, lightPalette, dimPalette} from '#/alf/themes' export const defaultTheme: Theme = { colorScheme: 'light', @@ -336,3 +336,14 @@ export const darkTheme: Theme = { }, }, } + +export const dimTheme: Theme = { + ...darkTheme, + palette: { + ...darkTheme.palette, + default: { + ...darkTheme.palette.default, + background: dimPalette.black, + }, + }, +} |