Merge branch 'main' into hailey/native-autoplay

This commit is contained in:
Hailey
2024-07-25 12:55:01 -07:00
65 changed files with 11813 additions and 10358 deletions
+3 -2
View File
@@ -132,7 +132,8 @@ appId: xyz.blueskyweb.app
id: "feedItem-by-bob.test" id: "feedItem-by-bob.test"
- tapOn: - tapOn:
id: "e2eGotoFeeds" id: "e2eGotoFeeds"
- tapOn: "Good Ppl" - tapOn:
id: "saved-feed-Good Ppl"
- assertVisible: - assertVisible:
id: "feedItem-by-bob.test" id: "feedItem-by-bob.test"
- tapOn: - tapOn:
@@ -168,7 +169,7 @@ appId: xyz.blueskyweb.app
id: "profilePager-selector" id: "profilePager-selector"
direction: LEFT direction: LEFT
- tapOn: - tapOn:
id: "profilePager-selector-5" id: "profilePager-selector-6"
- tapOn: "Good Ppl" - tapOn: "Good Ppl"
- tapOn: - tapOn:
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" d="M9.576 2.534C7.578 1.299 5 2.737 5 5.086v13.828c0 2.35 2.578 3.787 4.576 2.552l11.194-6.914c1.899-1.172 1.899-3.932 0-5.104L9.576 2.534Z"/></svg>

After

Width:  |  Height:  |  Size: 239 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M5 5.086C5 2.736 7.578 1.3 9.576 2.534L20.77 9.448c1.899 1.172 1.899 3.932 0 5.104L9.576 21.466C7.578 22.701 5 21.263 5 18.914V5.086Zm3.525-.85A1 1 0 0 0 7 5.085v13.828a1 1 0 0 0 1.525.85l11.194-6.913a1 1 0 0 0 0-1.702L8.525 4.235Z" clip-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 374 B

+2 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "bsky.app", "name": "bsky.app",
"version": "1.89.0", "version": "1.90.0",
"private": true, "private": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
@@ -143,6 +143,7 @@
"expo-web-browser": "~13.0.3", "expo-web-browser": "~13.0.3",
"fast-text-encoding": "^1.0.6", "fast-text-encoding": "^1.0.6",
"history": "^5.3.0", "history": "^5.3.0",
"hls.js": "^1.5.11",
"js-sha256": "^0.9.0", "js-sha256": "^0.9.0",
"jwt-decode": "^4.0.0", "jwt-decode": "^4.0.0",
"lande": "^1.0.10", "lande": "^1.0.10",
+20
View File
@@ -0,0 +1,20 @@
--- a/node_modules/expo-video/android/src/main/java/expo/modules/video/PlayerViewExtension.kt
+++ b/node_modules/expo-video/android/src/main/java/expo/modules/video/PlayerViewExtension.kt
@@ -11,6 +11,7 @@ internal fun PlayerView.applyRequiresLinearPlayback(requireLinearPlayback: Boole
setShowPreviousButton(!requireLinearPlayback)
setShowNextButton(!requireLinearPlayback)
setTimeBarInteractive(requireLinearPlayback)
+ setShowSubtitleButton(true)
}
@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
@@ -27,7 +28,8 @@ internal fun PlayerView.setTimeBarInteractive(interactive: Boolean) {
@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
internal fun PlayerView.setFullscreenButtonVisibility(visible: Boolean) {
- val fullscreenButton = findViewById<android.widget.ImageButton>(androidx.media3.ui.R.id.exo_fullscreen)
+ val fullscreenButton =
+ findViewById<android.widget.ImageButton>(androidx.media3.ui.R.id.exo_fullscreen)
fullscreenButton?.visibility = if (visible) {
android.view.View.VISIBLE
} else {
+41 -38
View File
@@ -23,10 +23,12 @@ import {
} from '#/lib/statsig/statsig' } from '#/lib/statsig/statsig'
import {s} from '#/lib/styles' import {s} from '#/lib/styles'
import {ThemeProvider} from '#/lib/ThemeContext' import {ThemeProvider} from '#/lib/ThemeContext'
import I18nProvider from '#/locale/i18nProvider'
import {logger} from '#/logger' import {logger} from '#/logger'
import {Provider as A11yProvider} from '#/state/a11y' import {Provider as A11yProvider} from '#/state/a11y'
import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes' import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes'
import {Provider as DialogStateProvider} from '#/state/dialogs' import {Provider as DialogStateProvider} from '#/state/dialogs'
import {listenSessionDropped} from '#/state/events'
import {Provider as InvitesStateProvider} from '#/state/invites' import {Provider as InvitesStateProvider} from '#/state/invites'
import {Provider as LightboxStateProvider} from '#/state/lightbox' import {Provider as LightboxStateProvider} from '#/state/lightbox'
import {MessagesProvider} from '#/state/messages' import {MessagesProvider} from '#/state/messages'
@@ -49,6 +51,7 @@ import {Provider as ProgressGuideProvider} from '#/state/shell/progress-guide'
import {Provider as SelectedFeedProvider} from '#/state/shell/selected-feed' import {Provider as SelectedFeedProvider} from '#/state/shell/selected-feed'
import {Provider as StarterPackProvider} from '#/state/shell/starter-pack' import {Provider as StarterPackProvider} from '#/state/shell/starter-pack'
import {TestCtrls} from '#/view/com/testing/TestCtrls' import {TestCtrls} from '#/view/com/testing/TestCtrls'
import {ActiveVideoProvider} from '#/view/com/util/post-embeds/ActiveVideoContext'
import * as Toast from '#/view/com/util/Toast' import * as Toast from '#/view/com/util/Toast'
import {Shell} from '#/view/shell' import {Shell} from '#/view/shell'
import {ThemeProvider as Alf} from '#/alf' import {ThemeProvider as Alf} from '#/alf'
@@ -58,8 +61,6 @@ import {Provider as PortalProvider} from '#/components/Portal'
import {Splash} from '#/Splash' import {Splash} from '#/Splash'
import {Provider as TourProvider} from '#/tours' import {Provider as TourProvider} from '#/tours'
import {BackgroundNotificationPreferencesProvider} from '../modules/expo-background-notification-handler/src/BackgroundNotificationHandlerProvider' import {BackgroundNotificationPreferencesProvider} from '../modules/expo-background-notification-handler/src/BackgroundNotificationHandlerProvider'
import I18nProvider from './locale/i18nProvider'
import {listenSessionDropped} from './state/events'
SplashScreen.preventAutoHideAsync() SplashScreen.preventAutoHideAsync()
@@ -107,42 +108,44 @@ function InnerApp() {
<Alf theme={theme}> <Alf theme={theme}>
<ThemeProvider theme={theme}> <ThemeProvider theme={theme}>
<Splash isReady={isReady && hasCheckedReferrer}> <Splash isReady={isReady && hasCheckedReferrer}>
<RootSiblingParent> <ActiveVideoProvider>
<React.Fragment <RootSiblingParent>
// Resets the entire tree below when it changes: <React.Fragment
key={currentAccount?.did}> // Resets the entire tree below when it changes:
<QueryProvider currentDid={currentAccount?.did}> key={currentAccount?.did}>
<StatsigProvider> <QueryProvider currentDid={currentAccount?.did}>
<MessagesProvider> <StatsigProvider>
{/* LabelDefsProvider MUST come before ModerationOptsProvider */} <MessagesProvider>
<LabelDefsProvider> {/* LabelDefsProvider MUST come before ModerationOptsProvider */}
<ModerationOptsProvider> <LabelDefsProvider>
<LoggedOutViewProvider> <ModerationOptsProvider>
<SelectedFeedProvider> <LoggedOutViewProvider>
<UnreadNotifsProvider> <SelectedFeedProvider>
<BackgroundNotificationPreferencesProvider> <UnreadNotifsProvider>
<MutedThreadsProvider> <BackgroundNotificationPreferencesProvider>
<TourProvider> <MutedThreadsProvider>
<ProgressGuideProvider> <TourProvider>
<GestureHandlerRootView <ProgressGuideProvider>
style={s.h100pct}> <GestureHandlerRootView
<TestCtrls /> style={s.h100pct}>
<Shell /> <TestCtrls />
</GestureHandlerRootView> <Shell />
</ProgressGuideProvider> </GestureHandlerRootView>
</TourProvider> </ProgressGuideProvider>
</MutedThreadsProvider> </TourProvider>
</BackgroundNotificationPreferencesProvider> </MutedThreadsProvider>
</UnreadNotifsProvider> </BackgroundNotificationPreferencesProvider>
</SelectedFeedProvider> </UnreadNotifsProvider>
</LoggedOutViewProvider> </SelectedFeedProvider>
</ModerationOptsProvider> </LoggedOutViewProvider>
</LabelDefsProvider> </ModerationOptsProvider>
</MessagesProvider> </LabelDefsProvider>
</StatsigProvider> </MessagesProvider>
</QueryProvider> </StatsigProvider>
</React.Fragment> </QueryProvider>
</RootSiblingParent> </React.Fragment>
</RootSiblingParent>
</ActiveVideoProvider>
</Splash> </Splash>
</ThemeProvider> </ThemeProvider>
</Alf> </Alf>
+38 -35
View File
@@ -12,10 +12,12 @@ import {useIntentHandler} from '#/lib/hooks/useIntentHandler'
import {QueryProvider} from '#/lib/react-query' import {QueryProvider} from '#/lib/react-query'
import {Provider as StatsigProvider} from '#/lib/statsig/statsig' import {Provider as StatsigProvider} from '#/lib/statsig/statsig'
import {ThemeProvider} from '#/lib/ThemeContext' import {ThemeProvider} from '#/lib/ThemeContext'
import I18nProvider from '#/locale/i18nProvider'
import {logger} from '#/logger' import {logger} from '#/logger'
import {Provider as A11yProvider} from '#/state/a11y' import {Provider as A11yProvider} from '#/state/a11y'
import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes' import {Provider as MutedThreadsProvider} from '#/state/cache/thread-mutes'
import {Provider as DialogStateProvider} from '#/state/dialogs' import {Provider as DialogStateProvider} from '#/state/dialogs'
import {listenSessionDropped} from '#/state/events'
import {Provider as InvitesStateProvider} from '#/state/invites' import {Provider as InvitesStateProvider} from '#/state/invites'
import {Provider as LightboxStateProvider} from '#/state/lightbox' import {Provider as LightboxStateProvider} from '#/state/lightbox'
import {MessagesProvider} from '#/state/messages' import {MessagesProvider} from '#/state/messages'
@@ -37,6 +39,7 @@ import {Provider as LoggedOutViewProvider} from '#/state/shell/logged-out'
import {Provider as ProgressGuideProvider} from '#/state/shell/progress-guide' import {Provider as ProgressGuideProvider} from '#/state/shell/progress-guide'
import {Provider as SelectedFeedProvider} from '#/state/shell/selected-feed' import {Provider as SelectedFeedProvider} from '#/state/shell/selected-feed'
import {Provider as StarterPackProvider} from '#/state/shell/starter-pack' import {Provider as StarterPackProvider} from '#/state/shell/starter-pack'
import {ActiveVideoProvider} from '#/view/com/util/post-embeds/ActiveVideoContext'
import * as Toast from '#/view/com/util/Toast' import * as Toast from '#/view/com/util/Toast'
import {ToastContainer} from '#/view/com/util/Toast.web' import {ToastContainer} from '#/view/com/util/Toast.web'
import {Shell} from '#/view/shell/index' import {Shell} from '#/view/shell/index'
@@ -46,8 +49,6 @@ import {useStarterPackEntry} from '#/components/hooks/useStarterPackEntry'
import {Provider as PortalProvider} from '#/components/Portal' import {Provider as PortalProvider} from '#/components/Portal'
import {Provider as TourProvider} from '#/tours' import {Provider as TourProvider} from '#/tours'
import {BackgroundNotificationPreferencesProvider} from '../modules/expo-background-notification-handler/src/BackgroundNotificationHandlerProvider' import {BackgroundNotificationPreferencesProvider} from '../modules/expo-background-notification-handler/src/BackgroundNotificationHandlerProvider'
import I18nProvider from './locale/i18nProvider'
import {listenSessionDropped} from './state/events'
function InnerApp() { function InnerApp() {
const [isReady, setIsReady] = React.useState(false) const [isReady, setIsReady] = React.useState(false)
@@ -92,39 +93,41 @@ function InnerApp() {
<Alf theme={theme}> <Alf theme={theme}>
<ThemeProvider theme={theme}> <ThemeProvider theme={theme}>
<RootSiblingParent> <RootSiblingParent>
<React.Fragment <ActiveVideoProvider>
// Resets the entire tree below when it changes: <React.Fragment
key={currentAccount?.did}> // Resets the entire tree below when it changes:
<QueryProvider currentDid={currentAccount?.did}> key={currentAccount?.did}>
<StatsigProvider> <QueryProvider currentDid={currentAccount?.did}>
<MessagesProvider> <StatsigProvider>
{/* LabelDefsProvider MUST come before ModerationOptsProvider */} <MessagesProvider>
<LabelDefsProvider> {/* LabelDefsProvider MUST come before ModerationOptsProvider */}
<ModerationOptsProvider> <LabelDefsProvider>
<LoggedOutViewProvider> <ModerationOptsProvider>
<SelectedFeedProvider> <LoggedOutViewProvider>
<UnreadNotifsProvider> <SelectedFeedProvider>
<BackgroundNotificationPreferencesProvider> <UnreadNotifsProvider>
<MutedThreadsProvider> <BackgroundNotificationPreferencesProvider>
<SafeAreaProvider> <MutedThreadsProvider>
<TourProvider> <SafeAreaProvider>
<ProgressGuideProvider> <TourProvider>
<Shell /> <ProgressGuideProvider>
</ProgressGuideProvider> <Shell />
</TourProvider> </ProgressGuideProvider>
</SafeAreaProvider> </TourProvider>
</MutedThreadsProvider> </SafeAreaProvider>
</BackgroundNotificationPreferencesProvider> </MutedThreadsProvider>
</UnreadNotifsProvider> </BackgroundNotificationPreferencesProvider>
</SelectedFeedProvider> </UnreadNotifsProvider>
</LoggedOutViewProvider> </SelectedFeedProvider>
</ModerationOptsProvider> </LoggedOutViewProvider>
</LabelDefsProvider> </ModerationOptsProvider>
</MessagesProvider> </LabelDefsProvider>
</StatsigProvider> </MessagesProvider>
</QueryProvider> </StatsigProvider>
</React.Fragment> </QueryProvider>
<ToastContainer /> </React.Fragment>
<ToastContainer />
</ActiveVideoProvider>
</RootSiblingParent> </RootSiblingParent>
</ThemeProvider> </ThemeProvider>
</Alf> </Alf>
+5 -6
View File
@@ -2,7 +2,6 @@ import {Platform, StyleSheet} from 'react-native'
import * as tokens from '#/alf/tokens' import * as tokens from '#/alf/tokens'
import {native, web} from '#/alf/util/platform' import {native, web} from '#/alf/util/platform'
import hairlineWidth = StyleSheet.hairlineWidth
export const atoms = { export const atoms = {
/* /*
@@ -284,19 +283,19 @@ export const atoms = {
borderWidth: 0, borderWidth: 0,
}, },
border: { border: {
borderWidth: hairlineWidth, borderWidth: StyleSheet.hairlineWidth,
}, },
border_t: { border_t: {
borderTopWidth: hairlineWidth, borderTopWidth: StyleSheet.hairlineWidth,
}, },
border_b: { border_b: {
borderBottomWidth: hairlineWidth, borderBottomWidth: StyleSheet.hairlineWidth,
}, },
border_l: { border_l: {
borderLeftWidth: hairlineWidth, borderLeftWidth: StyleSheet.hairlineWidth,
}, },
border_r: { border_r: {
borderRightWidth: hairlineWidth, borderRightWidth: StyleSheet.hairlineWidth,
}, },
/* /*
+9
View File
@@ -0,0 +1,9 @@
import {createSinglePathSVG} from './TEMPLATE'
export const Play_Stroke2_Corner2_Rounded = createSinglePathSVG({
path: 'M5 5.086C5 2.736 7.578 1.3 9.576 2.534L20.77 9.448c1.899 1.172 1.899 3.932 0 5.104L9.576 21.466C7.578 22.701 5 21.263 5 18.914V5.086Zm3.525-.85A1 1 0 0 0 7 5.085v13.828a1 1 0 0 0 1.525.85l11.194-6.913a1 1 0 0 0 0-1.702L8.525 4.235Z',
})
export const Play_Filled_Corner2_Rounded = createSinglePathSVG({
path: 'M9.576 2.534C7.578 1.299 5 2.737 5 5.086v13.828c0 2.35 2.578 3.787 4.576 2.552l11.194-6.914c1.899-1.172 1.899-3.932 0-5.104L9.576 2.534Z',
})
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -11,7 +11,7 @@ import {useProfileQuery} from '#/state/queries/profile'
import {useSession} from '#/state/session' import {useSession} from '#/state/session'
import * as Toast from '#/view/com/util/Toast' import * as Toast from '#/view/com/util/Toast'
import {ViewHeader} from '#/view/com/util/ViewHeader' import {ViewHeader} from '#/view/com/util/ViewHeader'
import {CenteredView} from '#/view/com/util/Views' import {ScrollView} from '#/view/com/util/Views'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
import {Divider} from '#/components/Divider' import {Divider} from '#/components/Divider'
import * as Toggle from '#/components/forms/Toggle' import * as Toggle from '#/components/forms/Toggle'
@@ -55,7 +55,7 @@ export function MessagesSettingsScreen({}: Props) {
) )
return ( return (
<CenteredView sideBorders style={a.h_full_vh}> <ScrollView stickyHeaderIndices={[0]}>
<ViewHeader title={_(msg`Chat Settings`)} showOnDesktop showBorder /> <ViewHeader title={_(msg`Chat Settings`)} showOnDesktop showBorder />
<View style={[a.p_lg, a.gap_md]}> <View style={[a.p_lg, a.gap_md]}>
<Text style={[a.text_lg, a.font_bold]}> <Text style={[a.text_lg, a.font_bold]}>
@@ -149,6 +149,6 @@ export function MessagesSettingsScreen({}: Props) {
</> </>
)} )}
</View> </View>
</CenteredView> </ScrollView>
) )
} }
+21 -8
View File
@@ -78,7 +78,10 @@ export interface FeedPostSliceItem {
uri: string uri: string
post: AppBskyFeedDefs.PostView post: AppBskyFeedDefs.PostView
record: AppBskyFeedPost.Record record: AppBskyFeedPost.Record
reason?: AppBskyFeedDefs.ReasonRepost | ReasonFeedSource reason?:
| AppBskyFeedDefs.ReasonRepost
| ReasonFeedSource
| {[k: string]: unknown; $type: string}
feedContext: string | undefined feedContext: string | undefined
moderation: ModerationDecision moderation: ModerationDecision
parentAuthor?: AppBskyActorDefs.ProfileViewBasic parentAuthor?: AppBskyActorDefs.ProfileViewBasic
@@ -323,7 +326,7 @@ export function usePostFeedQuery(
) )
} }
return { const feedPostSlice: FeedPostSlice = {
_reactKey: slice._reactKey, _reactKey: slice._reactKey,
_isFeedPostSlice: true, _isFeedPostSlice: true,
rootUri: slice.rootItem.post.uri, rootUri: slice.rootItem.post.uri,
@@ -341,15 +344,23 @@ export function usePostFeedQuery(
AppBskyFeedPost.validateRecord(item.post.record) AppBskyFeedPost.validateRecord(item.post.record)
.success .success
) { ) {
const parentAuthor = const parent = item.reply?.parent
item.reply?.parent?.author ?? let parentAuthor:
slice.items[i + 1]?.reply?.grandparentAuthor | AppBskyActorDefs.ProfileViewBasic
| undefined
if (AppBskyFeedDefs.isPostView(parent)) {
parentAuthor = parent.author
}
if (!parentAuthor) {
parentAuthor =
slice.items[i + 1]?.reply?.grandparentAuthor
}
const replyRef = item.reply const replyRef = item.reply
const isParentBlocked = AppBskyFeedDefs.isBlockedPost( const isParentBlocked = AppBskyFeedDefs.isBlockedPost(
replyRef?.parent, replyRef?.parent,
) )
return { const feedPostSliceItem: FeedPostSliceItem = {
_reactKey: `${slice._reactKey}-${i}-${item.post.uri}`, _reactKey: `${slice._reactKey}-${i}-${item.post.uri}`,
uri: item.post.uri, uri: item.post.uri,
post: item.post, post: item.post,
@@ -363,13 +374,15 @@ export function usePostFeedQuery(
parentAuthor, parentAuthor,
isParentBlocked, isParentBlocked,
} }
return feedPostSliceItem
} }
return undefined return undefined
}) })
.filter(Boolean) as FeedPostSliceItem[], .filter(<T>(n?: T): n is T => Boolean(n)),
} }
return feedPostSlice
}) })
.filter(Boolean) as FeedPostSlice[], .filter(<T>(n?: T): n is T => Boolean(n)),
})), })),
], ],
} }
+11 -11
View File
@@ -4,7 +4,7 @@ export type ThreadgateSetting =
| {type: 'nobody'} | {type: 'nobody'}
| {type: 'mention'} | {type: 'mention'}
| {type: 'following'} | {type: 'following'}
| {type: 'list'; list: string} | {type: 'list'; list: unknown}
export function threadgateViewToSettings( export function threadgateViewToSettings(
threadgate: AppBskyFeedDefs.ThreadgateView | undefined, threadgate: AppBskyFeedDefs.ThreadgateView | undefined,
@@ -21,18 +21,18 @@ export function threadgateViewToSettings(
if (!record.allow?.length) { if (!record.allow?.length) {
return [{type: 'nobody'}] return [{type: 'nobody'}]
} }
return record.allow const settings: ThreadgateSetting[] = record.allow
.map(allow => { .map(allow => {
let setting: ThreadgateSetting | undefined
if (allow.$type === 'app.bsky.feed.threadgate#mentionRule') { if (allow.$type === 'app.bsky.feed.threadgate#mentionRule') {
return {type: 'mention'} setting = {type: 'mention'}
} else if (allow.$type === 'app.bsky.feed.threadgate#followingRule') {
setting = {type: 'following'}
} else if (allow.$type === 'app.bsky.feed.threadgate#listRule') {
setting = {type: 'list', list: allow.list}
} }
if (allow.$type === 'app.bsky.feed.threadgate#followingRule') { return setting
return {type: 'following'}
}
if (allow.$type === 'app.bsky.feed.threadgate#listRule') {
return {type: 'list', list: allow.list}
}
return undefined
}) })
.filter(Boolean) as ThreadgateSetting[] .filter(<T>(n?: T): n is T => Boolean(n))
return settings
} }
+4 -5
View File
@@ -99,7 +99,6 @@ import {SelectVideoBtn} from './videos/SelectVideoBtn'
import {useVideoState} from './videos/state' import {useVideoState} from './videos/state'
import {VideoPreview} from './videos/VideoPreview' import {VideoPreview} from './videos/VideoPreview'
import {VideoTranscodeProgress} from './videos/VideoTranscodeProgress' import {VideoTranscodeProgress} from './videos/VideoTranscodeProgress'
import hairlineWidth = StyleSheet.hairlineWidth
type CancelRef = { type CancelRef = {
onPressCancel: () => void onPressCancel: () => void
@@ -763,7 +762,7 @@ function useAnimatedBorders() {
const topBarAnimatedStyle = useAnimatedStyle(() => { const topBarAnimatedStyle = useAnimatedStyle(() => {
return { return {
borderBottomWidth: hairlineWidth, borderBottomWidth: StyleSheet.hairlineWidth,
borderColor: interpolateColor( borderColor: interpolateColor(
hasScrolledTop.value, hasScrolledTop.value,
[0, 1], [0, 1],
@@ -773,7 +772,7 @@ function useAnimatedBorders() {
}) })
const bottomBarAnimatedStyle = useAnimatedStyle(() => { const bottomBarAnimatedStyle = useAnimatedStyle(() => {
return { return {
borderTopWidth: hairlineWidth, borderTopWidth: StyleSheet.hairlineWidth,
borderColor: interpolateColor( borderColor: interpolateColor(
hasScrolledBottom.value, hasScrolledBottom.value,
[0, 1], [0, 1],
@@ -855,7 +854,7 @@ const styles = StyleSheet.create({
marginBottom: 8, marginBottom: 8,
}, },
errorIcon: { errorIcon: {
borderWidth: hairlineWidth, borderWidth: StyleSheet.hairlineWidth,
borderColor: colors.red4, borderColor: colors.red4,
color: colors.red4, color: colors.red4,
borderRadius: 30, borderRadius: 30,
@@ -891,6 +890,6 @@ const styles = StyleSheet.create({
paddingLeft: 7, paddingLeft: 7,
paddingRight: 16, paddingRight: 16,
alignItems: 'center', alignItems: 'center',
borderTopWidth: hairlineWidth, borderTopWidth: StyleSheet.hairlineWidth,
}, },
}) })
+1 -2
View File
@@ -9,7 +9,6 @@ import {usePalette} from 'lib/hooks/usePalette'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {Text} from '../util/text/Text' import {Text} from '../util/text/Text'
import {UserAvatar} from '../util/UserAvatar' import {UserAvatar} from '../util/UserAvatar'
import hairlineWidth = StyleSheet.hairlineWidth
export function ComposePrompt({onPressCompose}: {onPressCompose: () => void}) { export function ComposePrompt({onPressCompose}: {onPressCompose: () => void}) {
const {currentAccount} = useSession() const {currentAccount} = useSession()
@@ -49,7 +48,7 @@ const styles = StyleSheet.create({
paddingBottom: 10, paddingBottom: 10,
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
borderTopWidth: hairlineWidth, borderTopWidth: StyleSheet.hairlineWidth,
}, },
labelMobile: { labelMobile: {
paddingLeft: 12, paddingLeft: 12,
+3 -4
View File
@@ -13,6 +13,7 @@ import {msg, Plural, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {logger} from '#/logger' import {logger} from '#/logger'
import {shouldClickOpenNewTab} from '#/platform/urls'
import {FeedSourceInfo, useFeedSourceInfoQuery} from '#/state/queries/feed' import {FeedSourceInfo, useFeedSourceInfoQuery} from '#/state/queries/feed'
import { import {
useAddSavedFeedsMutation, useAddSavedFeedsMutation,
@@ -32,8 +33,6 @@ import * as Prompt from '#/components/Prompt'
import {RichText} from '#/components/RichText' import {RichText} from '#/components/RichText'
import {Text} from '../util/text/Text' import {Text} from '../util/text/Text'
import {UserAvatar} from '../util/UserAvatar' import {UserAvatar} from '../util/UserAvatar'
import hairlineWidth = StyleSheet.hairlineWidth
import {shouldClickOpenNewTab} from '#/platform/urls'
export function FeedSourceCard({ export function FeedSourceCard({
feedUri, feedUri,
@@ -209,7 +208,7 @@ export function FeedSourceCardLoaded({
styles.container, styles.container,
pal.border, pal.border,
style, style,
{borderTopWidth: hideTopBorder ? 0 : hairlineWidth}, {borderTopWidth: hideTopBorder ? 0 : StyleSheet.hairlineWidth},
]} ]}
onPress={e => { onPress={e => {
const shouldOpenInNewTab = shouldClickOpenNewTab(e) const shouldOpenInNewTab = shouldClickOpenNewTab(e)
@@ -330,7 +329,7 @@ const styles = StyleSheet.create({
gap: 14, gap: 14,
}, },
border: { border: {
borderTopWidth: hairlineWidth, borderTopWidth: StyleSheet.hairlineWidth,
}, },
headerContainer: { headerContainer: {
flexDirection: 'row', flexDirection: 'row',
+1 -2
View File
@@ -14,7 +14,6 @@ import {RichText as RichTextCom} from '#/components/RichText'
import {Link} from '../util/Link' import {Link} from '../util/Link'
import {Text} from '../util/text/Text' import {Text} from '../util/text/Text'
import {UserAvatar} from '../util/UserAvatar' import {UserAvatar} from '../util/UserAvatar'
import hairlineWidth = StyleSheet.hairlineWidth
export const ListCard = ({ export const ListCard = ({
testID, testID,
@@ -134,7 +133,7 @@ export const ListCard = ({
const styles = StyleSheet.create({ const styles = StyleSheet.create({
outer: { outer: {
borderTopWidth: hairlineWidth, borderTopWidth: StyleSheet.hairlineWidth,
paddingHorizontal: 6, paddingHorizontal: 6,
}, },
outerNoBorder: { outerNoBorder: {
+3 -4
View File
@@ -29,7 +29,6 @@ import {Button} from '../util/forms/Button'
import {Text} from '../util/text/Text' import {Text} from '../util/text/Text'
import * as Toast from '../util/Toast' import * as Toast from '../util/Toast'
import {UserAvatar} from '../util/UserAvatar' import {UserAvatar} from '../util/UserAvatar'
import hairlineWidth = StyleSheet.hairlineWidth
export const snapPoints = ['fullscreen'] export const snapPoints = ['fullscreen']
@@ -63,7 +62,7 @@ export function Component({
return [pal.border, {height: screenHeight / 1.5}] return [pal.border, {height: screenHeight / 1.5}]
} }
return [pal.border, {flex: 1, borderTopWidth: hairlineWidth}] return [pal.border, {flex: 1, borderTopWidth: StyleSheet.hairlineWidth}]
}, [pal.border, screenHeight]) }, [pal.border, screenHeight])
return ( return (
@@ -188,7 +187,7 @@ function ListItem({
style={[ style={[
styles.listItem, styles.listItem,
pal.border, pal.border,
index !== 0 && {borderTopWidth: hairlineWidth}, index !== 0 && {borderTopWidth: StyleSheet.hairlineWidth},
]}> ]}>
<View style={styles.listItemAvi}> <View style={styles.listItemAvi}>
<UserAvatar size={40} avatar={list.avatar} type="list" /> <UserAvatar size={40} avatar={list.avatar} type="list" />
@@ -248,7 +247,7 @@ const styles = StyleSheet.create({
gap: 10, gap: 10,
paddingTop: 10, paddingTop: 10,
paddingBottom: isAndroid ? 10 : 0, paddingBottom: isAndroid ? 10 : 0,
borderTopWidth: hairlineWidth, borderTopWidth: StyleSheet.hairlineWidth,
}, },
footerBtn: { footerBtn: {
paddingHorizontal: 24, paddingHorizontal: 24,
+1 -2
View File
@@ -24,7 +24,6 @@ import {NotificationFeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceho
import {LoadMoreRetryBtn} from '#/view/com/util/LoadMoreRetryBtn' import {LoadMoreRetryBtn} from '#/view/com/util/LoadMoreRetryBtn'
import {CenteredView} from '#/view/com/util/Views' import {CenteredView} from '#/view/com/util/Views'
import {FeedItem} from './FeedItem' import {FeedItem} from './FeedItem'
import hairlineWidth = StyleSheet.hairlineWidth
const EMPTY_FEED_ITEM = {_reactKey: '__empty__'} const EMPTY_FEED_ITEM = {_reactKey: '__empty__'}
const LOAD_MORE_ERROR_ITEM = {_reactKey: '__load_more_error__'} const LOAD_MORE_ERROR_ITEM = {_reactKey: '__load_more_error__'}
@@ -137,7 +136,7 @@ export function Feed({
<View <View
style={[ style={[
pal.border, pal.border,
!isTabletOrMobile && {borderTopWidth: hairlineWidth}, !isTabletOrMobile && {borderTopWidth: StyleSheet.hairlineWidth},
]}> ]}>
<NotificationFeedLoadingPlaceholder /> <NotificationFeedLoadingPlaceholder />
</View> </View>
+12 -17
View File
@@ -20,20 +20,28 @@ import {
import {AtUri} from '@atproto/api' import {AtUri} from '@atproto/api'
import {msg, plural, Trans} from '@lingui/macro' import {msg, plural, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {useNavigation} from '@react-navigation/native'
import {useQueryClient} from '@tanstack/react-query' import {useQueryClient} from '@tanstack/react-query'
import {useGate} from '#/lib/statsig/statsig' import {useGate} from '#/lib/statsig/statsig'
import {parseTenorGif} from '#/lib/strings/embed-player'
import {logger} from '#/logger'
import {FeedNotification} from '#/state/queries/notifications/feed' import {FeedNotification} from '#/state/queries/notifications/feed'
import {useAnimatedValue} from 'lib/hooks/useAnimatedValue' import {useAnimatedValue} from 'lib/hooks/useAnimatedValue'
import {usePalette} from 'lib/hooks/usePalette' import {usePalette} from 'lib/hooks/usePalette'
import {makeProfileLink} from 'lib/routes/links' import {makeProfileLink} from 'lib/routes/links'
import {NavigationProp} from 'lib/routes/types'
import {forceLTR} from 'lib/strings/bidi'
import {sanitizeDisplayName} from 'lib/strings/display-names' import {sanitizeDisplayName} from 'lib/strings/display-names'
import {sanitizeHandle} from 'lib/strings/handles' import {sanitizeHandle} from 'lib/strings/handles'
import {niceDate} from 'lib/strings/time' import {niceDate} from 'lib/strings/time'
import {colors, s} from 'lib/styles' import {colors, s} from 'lib/styles'
import {isWeb} from 'platform/detection' import {isWeb} from 'platform/detection'
import {DM_SERVICE_HEADERS} from 'state/queries/messages/const'
import {precacheProfile} from 'state/queries/profile' import {precacheProfile} from 'state/queries/profile'
import {useAgent} from 'state/session'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
import {Button, ButtonText} from '#/components/Button'
import { import {
ChevronBottom_Stroke2_Corner0_Rounded as ChevronDownIcon, ChevronBottom_Stroke2_Corner0_Rounded as ChevronDownIcon,
ChevronTop_Stroke2_Corner0_Rounded as ChevronUpIcon, ChevronTop_Stroke2_Corner0_Rounded as ChevronUpIcon,
@@ -41,8 +49,10 @@ import {
import {Heart2_Filled_Stroke2_Corner0_Rounded as HeartIconFilled} from '#/components/icons/Heart2' import {Heart2_Filled_Stroke2_Corner0_Rounded as HeartIconFilled} from '#/components/icons/Heart2'
import {PersonPlus_Filled_Stroke2_Corner0_Rounded as PersonPlusIcon} from '#/components/icons/Person' import {PersonPlus_Filled_Stroke2_Corner0_Rounded as PersonPlusIcon} from '#/components/icons/Person'
import {Repost_Stroke2_Corner2_Rounded as RepostIcon} from '#/components/icons/Repost' import {Repost_Stroke2_Corner2_Rounded as RepostIcon} from '#/components/icons/Repost'
import {StarterPack} from '#/components/icons/StarterPack'
import {Link as NewLink} from '#/components/Link' import {Link as NewLink} from '#/components/Link'
import {ProfileHoverCard} from '#/components/ProfileHoverCard' import {ProfileHoverCard} from '#/components/ProfileHoverCard'
import {Notification as StarterPackCard} from '#/components/StarterPack/StarterPackCard'
import {FeedSourceCard} from '../feeds/FeedSourceCard' import {FeedSourceCard} from '../feeds/FeedSourceCard'
import {Post} from '../post/Post' import {Post} from '../post/Post'
import {ImageHorzList} from '../util/images/ImageHorzList' import {ImageHorzList} from '../util/images/ImageHorzList'
@@ -52,19 +62,6 @@ import {Text} from '../util/text/Text'
import {TimeElapsed} from '../util/TimeElapsed' import {TimeElapsed} from '../util/TimeElapsed'
import {PreviewableUserAvatar, UserAvatar} from '../util/UserAvatar' import {PreviewableUserAvatar, UserAvatar} from '../util/UserAvatar'
import hairlineWidth = StyleSheet.hairlineWidth
import {useNavigation} from '@react-navigation/native'
import {parseTenorGif} from '#/lib/strings/embed-player'
import {logger} from '#/logger'
import {NavigationProp} from 'lib/routes/types'
import {forceLTR} from 'lib/strings/bidi'
import {DM_SERVICE_HEADERS} from 'state/queries/messages/const'
import {useAgent} from 'state/session'
import {Button, ButtonText} from '#/components/Button'
import {StarterPack} from '#/components/icons/StarterPack'
import {Notification as StarterPackCard} from '#/components/StarterPack/StarterPackCard'
const MAX_AUTHORS = 5 const MAX_AUTHORS = 5
const EXPANDED_AUTHOR_EL_HEIGHT = 35 const EXPANDED_AUTHOR_EL_HEIGHT = 35
@@ -171,7 +168,6 @@ let FeedItem = ({
) )
} }
let isFollowBack = false
let action = '' let action = ''
let icon = ( let icon = (
<HeartIconFilled <HeartIconFilled
@@ -192,7 +188,6 @@ let FeedItem = ({
item.notification.author.viewer?.following && item.notification.author.viewer?.following &&
gate('ungroup_follow_backs') gate('ungroup_follow_backs')
) { ) {
isFollowBack = true
action = _(msg`followed you back`) action = _(msg`followed you back`)
} else { } else {
action = _(msg`followed you`) action = _(msg`followed you`)
@@ -224,7 +219,7 @@ let FeedItem = ({
backgroundColor: pal.colors.unreadNotifBg, backgroundColor: pal.colors.unreadNotifBg,
borderColor: pal.colors.unreadNotifBorder, borderColor: pal.colors.unreadNotifBorder,
}, },
{borderTopWidth: hideTopBorder ? 0 : hairlineWidth}, {borderTopWidth: hideTopBorder ? 0 : StyleSheet.hairlineWidth},
]} ]}
href={itemHref} href={itemHref}
noFeedback noFeedback
@@ -272,7 +267,7 @@ let FeedItem = ({
visible={!isAuthorsExpanded} visible={!isAuthorsExpanded}
authors={authors} authors={authors}
onToggleAuthorsExpanded={onToggleAuthorsExpanded} onToggleAuthorsExpanded={onToggleAuthorsExpanded}
showDmButton={item.type === 'starterpack-joined' || isFollowBack} showDmButton={item.type === 'starterpack-joined'}
/> />
<ExpandedAuthorsList visible={isAuthorsExpanded} authors={authors} /> <ExpandedAuthorsList visible={isAuthorsExpanded} authors={authors} />
<Text style={[styles.meta, a.self_start]}> <Text style={[styles.meta, a.self_start]}>
+1 -2
View File
@@ -7,7 +7,6 @@ import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {PressableWithHover} from '../util/PressableWithHover' import {PressableWithHover} from '../util/PressableWithHover'
import {Text} from '../util/text/Text' import {Text} from '../util/text/Text'
import {DraggableScrollView} from './DraggableScrollView' import {DraggableScrollView} from './DraggableScrollView'
import hairlineWidth = StyleSheet.hairlineWidth
export interface TabBarProps { export interface TabBarProps {
testID?: string testID?: string
@@ -208,6 +207,6 @@ const mobileStyles = StyleSheet.create({
left: 0, left: 0,
right: 0, right: 0,
top: '100%', top: '100%',
borderBottomWidth: hairlineWidth, borderBottomWidth: StyleSheet.hairlineWidth,
}, },
}) })
+5 -6
View File
@@ -44,7 +44,6 @@ import {PostEmbeds} from '../util/post-embeds'
import {PostMeta} from '../util/PostMeta' import {PostMeta} from '../util/PostMeta'
import {Text} from '../util/text/Text' import {Text} from '../util/text/Text'
import {PreviewableUserAvatar} from '../util/UserAvatar' import {PreviewableUserAvatar} from '../util/UserAvatar'
import hairlineWidth = StyleSheet.hairlineWidth
export function PostThreadItem({ export function PostThreadItem({
post, post,
@@ -604,7 +603,7 @@ function PostOuterWrapper({
{ {
flexDirection: 'row', flexDirection: 'row',
paddingHorizontal: isMobile ? 10 : 6, paddingHorizontal: isMobile ? 10 : 6,
borderTopWidth: depth === 1 ? hairlineWidth : 0, borderTopWidth: depth === 1 ? StyleSheet.hairlineWidth : 0,
}, },
]}> ]}>
{Array.from(Array(depth - 1)).map((_, n: number) => ( {Array.from(Array(depth - 1)).map((_, n: number) => (
@@ -699,7 +698,7 @@ function getThreadAuthor(
const styles = StyleSheet.create({ const styles = StyleSheet.create({
outer: { outer: {
borderTopWidth: hairlineWidth, borderTopWidth: StyleSheet.hairlineWidth,
paddingLeft: 8, paddingLeft: 8,
}, },
outerHighlighted: { outerHighlighted: {
@@ -709,7 +708,7 @@ const styles = StyleSheet.create({
paddingRight: 8, paddingRight: 8,
}, },
outerHighlightedRoot: { outerHighlightedRoot: {
borderTopWidth: hairlineWidth, borderTopWidth: StyleSheet.hairlineWidth,
paddingTop: 16, paddingTop: 16,
}, },
noTopBorder: { noTopBorder: {
@@ -761,8 +760,8 @@ const styles = StyleSheet.create({
expandedInfo: { expandedInfo: {
flexDirection: 'row', flexDirection: 'row',
padding: 10, padding: 10,
borderTopWidth: hairlineWidth, borderTopWidth: StyleSheet.hairlineWidth,
borderBottomWidth: hairlineWidth, borderBottomWidth: StyleSheet.hairlineWidth,
marginTop: 5, marginTop: 5,
marginBottom: 10, marginBottom: 10,
}, },
+33 -32
View File
@@ -37,7 +37,6 @@ import {PostMeta} from '../util/PostMeta'
import {Text} from '../util/text/Text' import {Text} from '../util/text/Text'
import {PreviewableUserAvatar} from '../util/UserAvatar' import {PreviewableUserAvatar} from '../util/UserAvatar'
import {UserInfoText} from '../util/UserInfoText' import {UserInfoText} from '../util/UserInfoText'
import hairlineWidth = StyleSheet.hairlineWidth
export function Post({ export function Post({
post, post,
@@ -155,7 +154,7 @@ function PostInner({
style={[ style={[
styles.outer, styles.outer,
pal.border, pal.border,
!hideTopBorder && {borderTopWidth: hairlineWidth}, !hideTopBorder && {borderTopWidth: StyleSheet.hairlineWidth},
style, style,
]} ]}
onBeforePress={onBeforePress}> onBeforePress={onBeforePress}>
@@ -210,38 +209,40 @@ function PostInner({
</View> </View>
)} )}
<LabelsOnMyPost post={post} /> <LabelsOnMyPost post={post} />
<ContentHider {false && (
modui={moderation.ui('contentView')} <ContentHider
style={styles.contentHider}
childContainerStyle={styles.contentHiderChild}>
<PostAlerts
modui={moderation.ui('contentView')} modui={moderation.ui('contentView')}
style={[a.py_xs]} style={styles.contentHider}
/> childContainerStyle={styles.contentHiderChild}>
{richText.text ? ( <PostAlerts
<View style={styles.postTextContainer}> modui={moderation.ui('contentView')}
<RichText style={[a.py_xs]}
enableTags
testID="postText"
value={richText}
numberOfLines={limitLines ? MAX_POST_LINES : undefined}
style={[a.flex_1, a.text_md]}
authorHandle={post.author.handle}
/>
</View>
) : undefined}
{limitLines ? (
<TextLink
text={_(msg`Show More`)}
style={pal.link}
onPress={onPressShowMore}
href="#"
/> />
) : undefined} {richText.text ? (
{post.embed ? ( <View style={styles.postTextContainer}>
<PostEmbeds embed={post.embed} moderation={moderation} /> <RichText
) : null} enableTags
</ContentHider> testID="postText"
value={richText}
numberOfLines={limitLines ? MAX_POST_LINES : undefined}
style={[a.flex_1, a.text_md]}
authorHandle={post.author.handle}
/>
</View>
) : undefined}
{limitLines ? (
<TextLink
text={_(msg`Show More`)}
style={pal.link}
onPress={onPressShowMore}
href="#"
/>
) : undefined}
{post.embed ? (
<PostEmbeds embed={post.embed} moderation={moderation} />
) : null}
</ContentHider>
)}
<PostCtrls <PostCtrls
post={post} post={post}
record={record} record={record}
+58 -33
View File
@@ -16,8 +16,10 @@ import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {useQueryClient} from '@tanstack/react-query' import {useQueryClient} from '@tanstack/react-query'
import {useGate} from '#/lib/statsig/statsig'
import {POST_TOMBSTONE, Shadow, usePostShadow} from '#/state/cache/post-shadow' import {POST_TOMBSTONE, Shadow, usePostShadow} from '#/state/cache/post-shadow'
import {useFeedFeedbackContext} from '#/state/feed-feedback' import {useFeedFeedbackContext} from '#/state/feed-feedback'
import {useSession} from '#/state/session'
import {useComposerControls} from '#/state/shell/composer' import {useComposerControls} from '#/state/shell/composer'
import {isReasonFeedSource, ReasonFeedSource} from 'lib/api/feed/types' import {isReasonFeedSource, ReasonFeedSource} from 'lib/api/feed/types'
import {MAX_POST_LINES} from 'lib/constants' import {MAX_POST_LINES} from 'lib/constants'
@@ -29,6 +31,7 @@ import {countLines} from 'lib/strings/helpers'
import {s} from 'lib/styles' import {s} from 'lib/styles'
import {precacheProfile} from 'state/queries/profile' import {precacheProfile} from 'state/queries/profile'
import {atoms as a} from '#/alf' import {atoms as a} from '#/alf'
import {Repost_Stroke2_Corner2_Rounded as Repost} from '#/components/icons/Repost'
import {ContentHider} from '#/components/moderation/ContentHider' import {ContentHider} from '#/components/moderation/ContentHider'
import {ProfileHoverCard} from '#/components/ProfileHoverCard' import {ProfileHoverCard} from '#/components/ProfileHoverCard'
import {RichText} from '#/components/RichText' import {RichText} from '#/components/RichText'
@@ -38,17 +41,19 @@ import {FeedNameText} from '../util/FeedInfoText'
import {Link, TextLink, TextLinkOnWebOnly} from '../util/Link' import {Link, TextLink, TextLinkOnWebOnly} from '../util/Link'
import {PostCtrls} from '../util/post-ctrls/PostCtrls' import {PostCtrls} from '../util/post-ctrls/PostCtrls'
import {PostEmbeds} from '../util/post-embeds' import {PostEmbeds} from '../util/post-embeds'
import {VideoEmbed} from '../util/post-embeds/VideoEmbed'
import {PostMeta} from '../util/PostMeta' import {PostMeta} from '../util/PostMeta'
import {Text} from '../util/text/Text' import {Text} from '../util/text/Text'
import {PreviewableUserAvatar} from '../util/UserAvatar' import {PreviewableUserAvatar} from '../util/UserAvatar'
import {AviFollowButton} from './AviFollowButton' import {AviFollowButton} from './AviFollowButton'
import hairlineWidth = StyleSheet.hairlineWidth
import {useSession} from '#/state/session'
import {Repost_Stroke2_Corner2_Rounded as Repost} from '#/components/icons/Repost'
interface FeedItemProps { interface FeedItemProps {
record: AppBskyFeedPost.Record record: AppBskyFeedPost.Record
reason: AppBskyFeedDefs.ReasonRepost | ReasonFeedSource | undefined reason:
| AppBskyFeedDefs.ReasonRepost
| ReasonFeedSource
| {[k: string]: unknown; $type: string}
| undefined
moderation: ModerationDecision moderation: ModerationDecision
parentAuthor: AppBskyActorDefs.ProfileViewBasic | undefined parentAuthor: AppBskyActorDefs.ProfileViewBasic | undefined
showReplyTo: boolean showReplyTo: boolean
@@ -132,6 +137,8 @@ let FeedItemInner = ({
const {openComposer} = useComposerControls() const {openComposer} = useComposerControls()
const pal = usePalette('default') const pal = usePalette('default')
const {_} = useLingui() const {_} = useLingui()
const gate = useGate()
const href = useMemo(() => { const href = useMemo(() => {
const urip = new AtUri(post.uri) const urip = new AtUri(post.uri)
return makeProfileLink(post.author, 'post', urip.rkey) return makeProfileLink(post.author, 'post', urip.rkey)
@@ -197,7 +204,8 @@ let FeedItemInner = ({
isThreadLastChild || (!isThreadChild && !isThreadParent) isThreadLastChild || (!isThreadChild && !isThreadParent)
? 8 ? 8
: undefined, : undefined,
borderTopWidth: hideTopBorder || isThreadChild ? 0 : hairlineWidth, borderTopWidth:
hideTopBorder || isThreadChild ? 0 : StyleSheet.hairlineWidth,
}, },
] ]
@@ -337,9 +345,11 @@ let FeedItemInner = ({
postHref={href} postHref={href}
onOpenAuthor={onOpenAuthor} onOpenAuthor={onOpenAuthor}
/> />
{!isThreadChild && showReplyTo && parentAuthor && ( {!isThreadChild &&
<ReplyToLabel blocked={isParentBlocked} profile={parentAuthor} /> showReplyTo &&
)} (parentAuthor || isParentBlocked) && (
<ReplyToLabel blocked={isParentBlocked} profile={parentAuthor} />
)}
<LabelsOnMyPost post={post} /> <LabelsOnMyPost post={post} />
<PostContent <PostContent
moderation={moderation} moderation={moderation}
@@ -348,6 +358,9 @@ let FeedItemInner = ({
postAuthor={post.author} postAuthor={post.author}
onOpenEmbed={onOpenEmbed} onOpenEmbed={onOpenEmbed}
/> />
{__DEV__ && gate('videos') && (
<VideoEmbed source="https://lumi.jazco.dev/watch/did:plc:q6gjnaw2blty4crticxkmujt/Qmc8w93UpTa2adJHg4ZhnDPrBs1EsbzrekzPcqF5SwusuZ/playlist.m3u8" />
)}
<PostCtrls <PostCtrls
post={post} post={post}
record={record} record={record}
@@ -431,12 +444,46 @@ function ReplyToLabel({
profile, profile,
blocked, blocked,
}: { }: {
profile: AppBskyActorDefs.ProfileViewBasic profile: AppBskyActorDefs.ProfileViewBasic | undefined
blocked?: boolean blocked?: boolean
}) { }) {
const pal = usePalette('default') const pal = usePalette('default')
const {currentAccount} = useSession() const {currentAccount} = useSession()
const isMe = profile.did === currentAccount?.did
let label
if (blocked) {
label = <Trans context="description">Reply to a blocked post</Trans>
} else if (profile != null) {
const isMe = profile.did === currentAccount?.did
if (isMe) {
label = <Trans context="description">Reply to you</Trans>
} else {
label = (
<Trans context="description">
Reply to{' '}
<ProfileHoverCard inline did={profile.did}>
<TextLinkOnWebOnly
type="md"
style={pal.textLight}
lineHeight={1.2}
numberOfLines={1}
href={makeProfileLink(profile)}
text={
profile.displayName
? sanitizeDisplayName(profile.displayName)
: sanitizeHandle(profile.handle)
}
/>
</ProfileHoverCard>
</Trans>
)
}
}
if (!label) {
// Should not happen.
return null
}
return ( return (
<View style={[s.flexRow, s.mb2, s.alignCenter]}> <View style={[s.flexRow, s.mb2, s.alignCenter]}>
@@ -450,29 +497,7 @@ function ReplyToLabel({
style={[pal.textLight, s.mr2]} style={[pal.textLight, s.mr2]}
lineHeight={1.2} lineHeight={1.2}
numberOfLines={1}> numberOfLines={1}>
{isMe ? ( {label}
<Trans context="description">Reply to you</Trans>
) : blocked ? (
<Trans context="description">Reply to a blocked post</Trans>
) : (
<Trans context="description">
Reply to{' '}
<ProfileHoverCard inline did={profile.did}>
<TextLinkOnWebOnly
type="md"
style={pal.textLight}
lineHeight={1.2}
numberOfLines={1}
href={makeProfileLink(profile)}
text={
profile.displayName
? sanitizeDisplayName(profile.displayName)
: sanitizeHandle(profile.handle)
}
/>
</ProfileHoverCard>
</Trans>
)}
</Text> </Text>
</View> </View>
) )
+2 -3
View File
@@ -23,12 +23,11 @@ import {
KnownFollowers, KnownFollowers,
shouldShowKnownFollowers, shouldShowKnownFollowers,
} from '#/components/KnownFollowers' } from '#/components/KnownFollowers'
import * as Pills from '#/components/Pills'
import {Link} from '../util/Link' import {Link} from '../util/Link'
import {Text} from '../util/text/Text' import {Text} from '../util/text/Text'
import {PreviewableUserAvatar} from '../util/UserAvatar' import {PreviewableUserAvatar} from '../util/UserAvatar'
import {FollowButton} from './FollowButton' import {FollowButton} from './FollowButton'
import hairlineWidth = StyleSheet.hairlineWidth
import * as Pills from '#/components/Pills'
export function ProfileCard({ export function ProfileCard({
testID, testID,
@@ -217,7 +216,7 @@ export function ProfileCardWithFollowBtn({
const styles = StyleSheet.create({ const styles = StyleSheet.create({
outer: { outer: {
borderTopWidth: hairlineWidth, borderTopWidth: StyleSheet.hairlineWidth,
paddingHorizontal: 6, paddingHorizontal: 6,
paddingVertical: 4, paddingVertical: 4,
}, },
@@ -15,15 +15,13 @@ import {makeProfileLink} from 'lib/routes/links'
import {NavigationProp} from 'lib/routes/types' import {NavigationProp} from 'lib/routes/types'
import {sanitizeHandle} from 'lib/strings/handles' import {sanitizeHandle} from 'lib/strings/handles'
import {isNative} from 'platform/detection' import {isNative} from 'platform/detection'
import {Menu_Stroke2_Corner0_Rounded as Menu} from '#/components/icons/Menu'
import {StarterPack} from '#/components/icons/StarterPack'
import {TextLink} from '../util/Link' import {TextLink} from '../util/Link'
import {LoadingPlaceholder} from '../util/LoadingPlaceholder' import {LoadingPlaceholder} from '../util/LoadingPlaceholder'
import {Text} from '../util/text/Text' import {Text} from '../util/text/Text'
import {UserAvatar, UserAvatarType} from '../util/UserAvatar' import {UserAvatar, UserAvatarType} from '../util/UserAvatar'
import {CenteredView} from '../util/Views' import {CenteredView} from '../util/Views'
import hairlineWidth = StyleSheet.hairlineWidth
import {Menu_Stroke2_Corner0_Rounded as Menu} from '#/components/icons/Menu'
import {StarterPack} from '#/components/icons/StarterPack'
export function ProfileSubpageHeader({ export function ProfileSubpageHeader({
isLoading, isLoading,
@@ -84,7 +82,7 @@ export function ProfileSubpageHeader({
{ {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
borderBottomWidth: hairlineWidth, borderBottomWidth: StyleSheet.hairlineWidth,
paddingTop: isNative ? 0 : 8, paddingTop: isNative ? 0 : 8,
paddingBottom: 8, paddingBottom: 8,
paddingHorizontal: isMobile ? 12 : 14, paddingHorizontal: isMobile ? 12 : 14,
+1 -2
View File
@@ -17,7 +17,6 @@ import {
Heart2_Stroke2_Corner0_Rounded as HeartIconOutline, Heart2_Stroke2_Corner0_Rounded as HeartIconOutline,
} from '#/components/icons/Heart2' } from '#/components/icons/Heart2'
import {Repost_Stroke2_Corner2_Rounded as Repost} from '#/components/icons/Repost' import {Repost_Stroke2_Corner2_Rounded as Repost} from '#/components/icons/Repost'
import hairlineWidth = StyleSheet.hairlineWidth
export function LoadingPlaceholder({ export function LoadingPlaceholder({
width, width,
@@ -236,7 +235,7 @@ export function FeedLoadingPlaceholder({
{ {
paddingHorizontal: 12, paddingHorizontal: 12,
paddingVertical: 18, paddingVertical: 18,
borderTopWidth: showTopBorder ? hairlineWidth : 0, borderTopWidth: showTopBorder ? StyleSheet.hairlineWidth : 0,
}, },
pal.border, pal.border,
style, style,
+1 -1
View File
@@ -12,7 +12,7 @@ import {atoms as a, useTheme} from '#/alf'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_TEST} from '#/env' import {IS_TEST} from '#/env'
const TIMEOUT = 3.7e3 const TIMEOUT = 2e3
export function show( export function show(
message: string, message: string,
+3 -4
View File
@@ -13,10 +13,9 @@ import {usePalette} from 'lib/hooks/usePalette'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {NavigationProp} from 'lib/routes/types' import {NavigationProp} from 'lib/routes/types'
import {useTheme} from '#/alf' import {useTheme} from '#/alf'
import {Menu_Stroke2_Corner0_Rounded as Menu} from '#/components/icons/Menu'
import {Text} from './text/Text' import {Text} from './text/Text'
import {CenteredView} from './Views' import {CenteredView} from './Views'
import hairlineWidth = StyleSheet.hairlineWidth
import {Menu_Stroke2_Corner0_Rounded as Menu} from '#/components/icons/Menu'
const BACK_HITSLOP = {left: 20, top: 20, right: 50, bottom: 20} const BACK_HITSLOP = {left: 20, top: 20, right: 50, bottom: 20}
@@ -154,7 +153,7 @@ function DesktopWebHeader({
styles.desktopHeader, styles.desktopHeader,
pal.border, pal.border,
{ {
borderBottomWidth: showBorder ? hairlineWidth : 0, borderBottomWidth: showBorder ? StyleSheet.hairlineWidth : 0,
}, },
{display: 'flex', flexDirection: 'column'}, {display: 'flex', flexDirection: 'column'},
]}> ]}>
@@ -243,7 +242,7 @@ const styles = StyleSheet.create({
marginRight: 'auto', marginRight: 'auto',
}, },
border: { border: {
borderBottomWidth: hairlineWidth, borderBottomWidth: StyleSheet.hairlineWidth,
}, },
titleContainer: { titleContainer: {
marginLeft: 'auto', marginLeft: 'auto',
+4 -5
View File
@@ -26,7 +26,6 @@ import Animated from 'react-native-reanimated'
import {usePalette} from 'lib/hooks/usePalette' import {usePalette} from 'lib/hooks/usePalette'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {addStyle} from 'lib/styles' import {addStyle} from 'lib/styles'
import hairlineWidth = StyleSheet.hairlineWidth
interface AddedProps { interface AddedProps {
desktopFixedHeight?: boolean | number desktopFixedHeight?: boolean | number
@@ -50,8 +49,8 @@ export const CenteredView = React.forwardRef(function CenteredView(
} }
if (sideBorders) { if (sideBorders) {
style = addStyle(style, { style = addStyle(style, {
borderLeftWidth: hairlineWidth, borderLeftWidth: StyleSheet.hairlineWidth,
borderRightWidth: hairlineWidth, borderRightWidth: StyleSheet.hairlineWidth,
}) })
style = addStyle(style, pal.border) style = addStyle(style, pal.border)
} }
@@ -163,8 +162,8 @@ export const ScrollView = React.forwardRef(function ScrollViewImpl(
const styles = StyleSheet.create({ const styles = StyleSheet.create({
contentContainer: { contentContainer: {
borderLeftWidth: hairlineWidth, borderLeftWidth: StyleSheet.hairlineWidth,
borderRightWidth: hairlineWidth, borderRightWidth: StyleSheet.hairlineWidth,
// @ts-ignore web only // @ts-ignore web only
minHeight: '100vh', minHeight: '100vh',
}, },
@@ -13,7 +13,6 @@ import {clamp} from '#/lib/numbers'
import {colors} from '#/lib/styles' import {colors} from '#/lib/styles'
import {isWeb} from '#/platform/detection' import {isWeb} from '#/platform/detection'
import {useSession} from '#/state/session' import {useSession} from '#/state/session'
import hairlineWidth = StyleSheet.hairlineWidth
const AnimatedTouchableOpacity = const AnimatedTouchableOpacity =
Animated.createAnimatedComponent(TouchableOpacity) Animated.createAnimatedComponent(TouchableOpacity)
@@ -74,7 +73,7 @@ const styles = StyleSheet.create({
// @ts-ignore 'fixed' is web only -prf // @ts-ignore 'fixed' is web only -prf
position: isWeb ? 'fixed' : 'absolute', position: isWeb ? 'fixed' : 'absolute',
left: 18, left: 18,
borderWidth: hairlineWidth, borderWidth: StyleSheet.hairlineWidth,
width: 52, width: 52,
height: 52, height: 52,
borderRadius: 26, borderRadius: 26,
@@ -0,0 +1,48 @@
import React, {useCallback, useId, useMemo, useState} from 'react'
import {VideoPlayerProvider} from './VideoPlayerContext'
const ActiveVideoContext = React.createContext<{
activeViewId: string | null
setActiveView: (viewId: string, src: string) => void
} | null>(null)
export function ActiveVideoProvider({children}: {children: React.ReactNode}) {
const [activeViewId, setActiveViewId] = useState<string | null>(null)
const [source, setSource] = useState<string | null>(null)
const value = useMemo(
() => ({
activeViewId,
setActiveView: (viewId: string, src: string) => {
setActiveViewId(viewId)
setSource(src)
},
}),
[activeViewId],
)
return (
<ActiveVideoContext.Provider value={value}>
<VideoPlayerProvider source={source ?? ''} viewId={activeViewId}>
{children}
</VideoPlayerProvider>
</ActiveVideoContext.Provider>
)
}
export function useActiveVideoView() {
const context = React.useContext(ActiveVideoContext)
if (!context) {
throw new Error('useActiveVideo must be used within a ActiveVideoProvider')
}
const id = useId()
return {
active: context.activeViewId === id,
setActive: useCallback(
(source: string) => context.setActiveView(id, source),
[context, id],
),
}
}
+2 -3
View File
@@ -39,7 +39,6 @@ import {Link} from '../Link'
import {PostMeta} from '../PostMeta' import {PostMeta} from '../PostMeta'
import {Text} from '../text/Text' import {Text} from '../text/Text'
import {PostEmbeds} from '.' import {PostEmbeds} from '.'
import hairlineWidth = StyleSheet.hairlineWidth
export function MaybeQuoteEmbed({ export function MaybeQuoteEmbed({
embed, embed,
@@ -262,7 +261,7 @@ const styles = StyleSheet.create({
marginTop: 8, marginTop: 8,
paddingVertical: 12, paddingVertical: 12,
paddingHorizontal: 12, paddingHorizontal: 12,
borderWidth: hairlineWidth, borderWidth: StyleSheet.hairlineWidth,
}, },
errorContainer: { errorContainer: {
flexDirection: 'row', flexDirection: 'row',
@@ -272,7 +271,7 @@ const styles = StyleSheet.create({
marginTop: 8, marginTop: 8,
paddingVertical: 14, paddingVertical: 14,
paddingHorizontal: 14, paddingHorizontal: 14,
borderWidth: hairlineWidth, borderWidth: StyleSheet.hairlineWidth,
}, },
alert: { alert: {
marginBottom: 6, marginBottom: 6,
@@ -0,0 +1,44 @@
import React, {useCallback} from 'react'
import {View} from 'react-native'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {atoms as a, useTheme} from '#/alf'
import {Button, ButtonIcon} from '#/components/Button'
import {Play_Filled_Corner2_Rounded as PlayIcon} from '#/components/icons/Play'
import {useActiveVideoView} from './ActiveVideoContext'
import {VideoEmbedInner} from './VideoEmbedInner'
export function VideoEmbed({source}: {source: string}) {
const t = useTheme()
const {active, setActive} = useActiveVideoView()
const {_} = useLingui()
const onPress = useCallback(() => setActive(source), [setActive, source])
return (
<View
style={[
a.w_full,
a.rounded_sm,
{aspectRatio: 16 / 9},
a.overflow_hidden,
t.atoms.bg_contrast_25,
a.my_xs,
]}>
{active ? (
<VideoEmbedInner source={source} />
) : (
<Button
style={[a.flex_1, t.atoms.bg_contrast_25]}
onPress={onPress}
label={_(msg`Play video`)}
variant="ghost"
color="secondary"
size="large">
<ButtonIcon icon={PlayIcon} />
</Button>
)}
</View>
)
}
@@ -0,0 +1,138 @@
import React, {useCallback, useEffect, useRef, useState} from 'react'
import {Pressable, StyleSheet, useWindowDimensions, View} from 'react-native'
import Animated, {
measure,
runOnJS,
useAnimatedRef,
useFrameCallback,
useSharedValue,
} from 'react-native-reanimated'
import {VideoPlayer, VideoView} from 'expo-video'
import {atoms as a} from '#/alf'
import {Text} from '#/components/Typography'
import {useVideoPlayer} from './VideoPlayerContext'
export const VideoEmbedInner = ({}: {source: string}) => {
const player = useVideoPlayer()
const aref = useAnimatedRef<Animated.View>()
const {height: windowHeight} = useWindowDimensions()
const hasLeftView = useSharedValue(false)
const ref = useRef<VideoView>(null)
const onEnterView = useCallback(() => {
if (player.status === 'readyToPlay') {
player.play()
}
}, [player])
const onLeaveView = useCallback(() => {
player.pause()
}, [player])
const enterFullscreen = useCallback(() => {
if (ref.current) {
ref.current.enterFullscreen()
}
}, [])
useFrameCallback(() => {
const measurement = measure(aref)
if (measurement) {
if (hasLeftView.value) {
// Check if the video is in view
if (
measurement.pageY >= 0 &&
measurement.pageY + measurement.height <= windowHeight
) {
runOnJS(onEnterView)()
hasLeftView.value = false
}
} else {
// Check if the video is out of view
if (
measurement.pageY + measurement.height < 0 ||
measurement.pageY > windowHeight
) {
runOnJS(onLeaveView)()
hasLeftView.value = true
}
}
}
})
return (
<Animated.View
style={[a.flex_1, a.relative]}
ref={aref}
collapsable={false}>
<VideoView
ref={ref}
player={player}
style={a.flex_1}
nativeControls={true}
/>
<VideoControls player={player} enterFullscreen={enterFullscreen} />
</Animated.View>
)
}
function VideoControls({
player,
enterFullscreen,
}: {
player: VideoPlayer
enterFullscreen: () => void
}) {
const [currentTime, setCurrentTime] = useState(Math.floor(player.currentTime))
useEffect(() => {
const interval = setInterval(() => {
setCurrentTime(Math.floor(player.duration - player.currentTime))
// how often should we update the time?
// 1000 gets out of sync with the video time
}, 250)
return () => {
clearInterval(interval)
}
}, [player])
const minutes = Math.floor(currentTime / 60)
const seconds = String(currentTime % 60).padStart(2, '0')
return (
<View style={[a.absolute, a.inset_0]}>
<View style={styles.timeContainer} pointerEvents="none">
<Text style={styles.timeElapsed}>
{minutes}:{seconds}
</Text>
</View>
<Pressable
onPress={enterFullscreen}
style={a.flex_1}
accessibilityLabel="Video"
accessibilityHint="Tap to enter full screen"
accessibilityRole="button"
/>
</View>
)
}
const styles = StyleSheet.create({
timeContainer: {
backgroundColor: 'rgba(0, 0, 0, 0.75)',
borderRadius: 6,
paddingHorizontal: 6,
paddingVertical: 3,
position: 'absolute',
left: 5,
bottom: 5,
},
timeElapsed: {
color: 'white',
fontSize: 12,
fontWeight: 'bold',
},
})
@@ -0,0 +1,52 @@
import React, {useEffect, useRef} from 'react'
import Hls from 'hls.js'
import {atoms as a} from '#/alf'
export const VideoEmbedInner = ({source}: {source: string}) => {
const ref = useRef<HTMLVideoElement>(null)
// Use HLS.js to play HLS video
useEffect(() => {
if (ref.current) {
if (ref.current.canPlayType('application/vnd.apple.mpegurl')) {
ref.current.src = source
} else if (Hls.isSupported()) {
var hls = new Hls()
hls.loadSource(source)
hls.attachMedia(ref.current)
} else {
// TODO: fallback
}
}
}, [source])
useEffect(() => {
if (ref.current) {
const observer = new IntersectionObserver(
([entry]) => {
if (ref.current) {
if (entry.isIntersecting) {
if (ref.current.paused) {
ref.current.play()
}
} else {
if (!ref.current.paused) {
ref.current.pause()
}
}
}
},
{threshold: 0},
)
observer.observe(ref.current)
return () => {
observer.disconnect()
}
}
}, [])
return <video ref={ref} style={a.flex_1} controls playsInline autoPlay loop />
}
@@ -0,0 +1,41 @@
import React, {useContext, useEffect} from 'react'
import type {VideoPlayer} from 'expo-video'
import {useVideoPlayer as useExpoVideoPlayer} from 'expo-video'
const VideoPlayerContext = React.createContext<VideoPlayer | null>(null)
export function VideoPlayerProvider({
viewId,
source,
children,
}: {
viewId: string | null
source: string
children: React.ReactNode
}) {
// eslint-disable-next-line @typescript-eslint/no-shadow
const player = useExpoVideoPlayer(source, player => {
player.loop = true
player.play()
})
// make sure we're playing every time the viewId changes
// this means the video is different
useEffect(() => {
player.play()
}, [viewId, player])
return (
<VideoPlayerContext.Provider value={player}>
{children}
</VideoPlayerContext.Provider>
)
}
export function useVideoPlayer() {
const context = useContext(VideoPlayerContext)
if (!context) {
throw new Error('useVideoPlayer must be used within a VideoPlayerProvider')
}
return context
}
@@ -0,0 +1,9 @@
import React from 'react'
export function VideoPlayerProvider({children}: {children: React.ReactNode}) {
return children
}
export function useVideoPlayer() {
throw new Error('useVideoPlayer must not be used on web')
}
+3 -4
View File
@@ -19,18 +19,17 @@ import {
} from '@atproto/api' } from '@atproto/api'
import {ImagesLightbox, useLightboxControls} from '#/state/lightbox' import {ImagesLightbox, useLightboxControls} from '#/state/lightbox'
import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge'
import {usePalette} from 'lib/hooks/usePalette' import {usePalette} from 'lib/hooks/usePalette'
import {FeedSourceCard} from 'view/com/feeds/FeedSourceCard' import {FeedSourceCard} from 'view/com/feeds/FeedSourceCard'
import {atoms as a} from '#/alf' import {atoms as a} from '#/alf'
import {Embed as StarterPackCard} from '#/components/StarterPack/StarterPackCard'
import {ContentHider} from '../../../../components/moderation/ContentHider' import {ContentHider} from '../../../../components/moderation/ContentHider'
import {AutoSizedImage} from '../images/AutoSizedImage' import {AutoSizedImage} from '../images/AutoSizedImage'
import {ImageLayoutGrid} from '../images/ImageLayoutGrid' import {ImageLayoutGrid} from '../images/ImageLayoutGrid'
import {ExternalLinkEmbed} from './ExternalLinkEmbed' import {ExternalLinkEmbed} from './ExternalLinkEmbed'
import {ListEmbed} from './ListEmbed' import {ListEmbed} from './ListEmbed'
import {MaybeQuoteEmbed} from './QuoteEmbed' import {MaybeQuoteEmbed} from './QuoteEmbed'
import hairlineWidth = StyleSheet.hairlineWidth
import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge'
import {Embed as StarterPackCard} from '#/components/StarterPack/StarterPackCard'
type Embed = type Embed =
| AppBskyEmbedRecord.View | AppBskyEmbedRecord.View
@@ -205,7 +204,7 @@ const styles = StyleSheet.create({
fontWeight: 'bold', fontWeight: 'bold',
}, },
customFeedOuter: { customFeedOuter: {
borderWidth: hairlineWidth, borderWidth: StyleSheet.hairlineWidth,
borderRadius: 8, borderRadius: 8,
marginTop: 4, marginTop: 4,
paddingHorizontal: 12, paddingHorizontal: 12,
+5 -6
View File
@@ -33,14 +33,13 @@ import {ViewHeader} from 'view/com/util/ViewHeader'
import {NoFollowingFeed} from '#/screens/Feeds/NoFollowingFeed' import {NoFollowingFeed} from '#/screens/Feeds/NoFollowingFeed'
import {NoSavedFeedsOfAnyType} from '#/screens/Feeds/NoSavedFeedsOfAnyType' import {NoSavedFeedsOfAnyType} from '#/screens/Feeds/NoSavedFeedsOfAnyType'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
import {Divider} from '#/components/Divider'
import * as FeedCard from '#/components/FeedCard'
import {IconCircle} from '#/components/IconCircle' import {IconCircle} from '#/components/IconCircle'
import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron'
import {FilterTimeline_Stroke2_Corner0_Rounded as FilterTimeline} from '#/components/icons/FilterTimeline' import {FilterTimeline_Stroke2_Corner0_Rounded as FilterTimeline} from '#/components/icons/FilterTimeline'
import {ListMagnifyingGlass_Stroke2_Corner0_Rounded} from '#/components/icons/ListMagnifyingGlass' import {ListMagnifyingGlass_Stroke2_Corner0_Rounded} from '#/components/icons/ListMagnifyingGlass'
import {ListSparkle_Stroke2_Corner0_Rounded} from '#/components/icons/ListSparkle' import {ListSparkle_Stroke2_Corner0_Rounded} from '#/components/icons/ListSparkle'
import hairlineWidth = StyleSheet.hairlineWidth
import {Divider} from '#/components/Divider'
import * as FeedCard from '#/components/FeedCard'
import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron'
import * as ListCard from '#/components/ListCard' import * as ListCard from '#/components/ListCard'
type Props = NativeStackScreenProps<CommonNavigatorParams, 'Feeds'> type Props = NativeStackScreenProps<CommonNavigatorParams, 'Feeds'>
@@ -793,13 +792,13 @@ const styles = StyleSheet.create({
paddingHorizontal: 16, paddingHorizontal: 16,
paddingVertical: 14, paddingVertical: 14,
gap: 12, gap: 12,
borderBottomWidth: hairlineWidth, borderBottomWidth: StyleSheet.hairlineWidth,
}, },
savedFeedMobile: { savedFeedMobile: {
paddingVertical: 10, paddingVertical: 10,
}, },
offlineSlug: { offlineSlug: {
borderWidth: hairlineWidth, borderWidth: StyleSheet.hairlineWidth,
borderRadius: 4, borderRadius: 4,
paddingHorizontal: 4, paddingHorizontal: 4,
paddingVertical: 2, paddingVertical: 2,
+5 -3
View File
@@ -16,7 +16,6 @@ import {MyLists} from '#/view/com/lists/MyLists'
import {Button} from 'view/com/util/forms/Button' import {Button} from 'view/com/util/forms/Button'
import {SimpleViewHeader} from 'view/com/util/SimpleViewHeader' import {SimpleViewHeader} from 'view/com/util/SimpleViewHeader'
import {Text} from 'view/com/util/text/Text' import {Text} from 'view/com/util/text/Text'
import hairlineWidth = StyleSheet.hairlineWidth
type Props = NativeStackScreenProps<CommonNavigatorParams, 'Lists'> type Props = NativeStackScreenProps<CommonNavigatorParams, 'Lists'>
export function ListsScreen({}: Props) { export function ListsScreen({}: Props) {
@@ -55,8 +54,11 @@ export function ListsScreen({}: Props) {
style={[ style={[
pal.border, pal.border,
isMobile isMobile
? {borderBottomWidth: hairlineWidth} ? {borderBottomWidth: StyleSheet.hairlineWidth}
: {borderLeftWidth: hairlineWidth, borderRightWidth: hairlineWidth}, : {
borderLeftWidth: StyleSheet.hairlineWidth,
borderRightWidth: StyleSheet.hairlineWidth,
},
]}> ]}>
<View style={{flex: 1}}> <View style={{flex: 1}}>
<Text type="title-lg" style={[pal.text, {fontWeight: 'bold'}]}> <Text type="title-lg" style={[pal.text, {fontWeight: 'bold'}]}>
+3 -3
View File
@@ -8,7 +8,7 @@ import {AllNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
import {useNotificationFeedQuery} from '#/state/queries/notifications/feed' import {useNotificationFeedQuery} from '#/state/queries/notifications/feed'
import {useNotificationsSettingsMutation} from '#/state/queries/notifications/settings' import {useNotificationsSettingsMutation} from '#/state/queries/notifications/settings'
import {ViewHeader} from '#/view/com/util/ViewHeader' import {ViewHeader} from '#/view/com/util/ViewHeader'
import {CenteredView} from '#/view/com/util/Views' import {ScrollView} from '#/view/com/util/Views'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
import {Error} from '#/components/Error' import {Error} from '#/components/Error'
import * as Toggle from '#/components/forms/Toggle' import * as Toggle from '#/components/forms/Toggle'
@@ -34,7 +34,7 @@ export function NotificationsSettingsScreen({}: Props) {
: serverPriority : serverPriority
return ( return (
<CenteredView style={a.flex_1} sideBorders> <ScrollView stickyHeaderIndices={[0]}>
<ViewHeader <ViewHeader
title={_(msg`Notification Settings`)} title={_(msg`Notification Settings`)}
showOnDesktop showOnDesktop
@@ -89,6 +89,6 @@ export function NotificationsSettingsScreen({}: Props) {
</View> </View>
</View> </View>
)} )}
</CenteredView> </ScrollView>
) )
} }
+2 -3
View File
@@ -65,7 +65,6 @@ import {useDialogControl} from '#/components/Dialog'
import * as Prompt from '#/components/Prompt' import * as Prompt from '#/components/Prompt'
import {ReportDialog, useReportDialogControl} from '#/components/ReportDialog' import {ReportDialog, useReportDialogControl} from '#/components/ReportDialog'
import {RichText} from '#/components/RichText' import {RichText} from '#/components/RichText'
import hairlineWidth = StyleSheet.hairlineWidth
const SECTION_TITLES_CURATE = ['Posts', 'About'] const SECTION_TITLES_CURATE = ['Posts', 'About']
const SECTION_TITLES_MOD = ['About'] const SECTION_TITLES_MOD = ['About']
@@ -803,7 +802,7 @@ const AboutSection = React.forwardRef<SectionRef, AboutSectionProps>(
<View <View
style={[ style={[
{ {
borderTopWidth: hairlineWidth, borderTopWidth: StyleSheet.hairlineWidth,
padding: isMobile ? 14 : 20, padding: isMobile ? 14 : 20,
gap: 12, gap: 12,
}, },
@@ -954,7 +953,7 @@ function ErrorScreen({error}: {error: string}) {
marginTop: 10, marginTop: 10,
paddingHorizontal: 18, paddingHorizontal: 18,
paddingVertical: 14, paddingVertical: 14,
borderTopWidth: hairlineWidth, borderTopWidth: StyleSheet.hairlineWidth,
}, },
]}> ]}>
<Text type="title-lg" style={[pal.text, s.mb10]}> <Text type="title-lg" style={[pal.text, s.mb10]}>
+6 -5
View File
@@ -32,7 +32,6 @@ import {NoFollowingFeed} from '#/screens/Feeds/NoFollowingFeed'
import {NoSavedFeedsOfAnyType} from '#/screens/Feeds/NoSavedFeedsOfAnyType' import {NoSavedFeedsOfAnyType} from '#/screens/Feeds/NoSavedFeedsOfAnyType'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
import {FilterTimeline_Stroke2_Corner0_Rounded as FilterTimeline} from '#/components/icons/FilterTimeline' import {FilterTimeline_Stroke2_Corner0_Rounded as FilterTimeline} from '#/components/icons/FilterTimeline'
import hairlineWidth = StyleSheet.hairlineWidth
const HITSLOP_TOP = { const HITSLOP_TOP = {
top: 20, top: 20,
@@ -93,7 +92,8 @@ export function SavedFeeds({}: Props) {
<ViewHeader title={_(msg`Edit My Feeds`)} showOnDesktop showBorder /> <ViewHeader title={_(msg`Edit My Feeds`)} showOnDesktop showBorder />
<ScrollView style={s.flex1} contentContainerStyle={[styles.noBorder]}> <ScrollView style={s.flex1} contentContainerStyle={[styles.noBorder]}>
{noSavedFeedsOfAnyType && ( {noSavedFeedsOfAnyType && (
<View style={[pal.border, {borderBottomWidth: hairlineWidth}]}> <View
style={[pal.border, {borderBottomWidth: StyleSheet.hairlineWidth}]}>
<NoSavedFeedsOfAnyType /> <NoSavedFeedsOfAnyType />
</View> </View>
)} )}
@@ -135,7 +135,8 @@ export function SavedFeeds({}: Props) {
)} )}
{noFollowingFeed && ( {noFollowingFeed && (
<View style={[pal.border, {borderBottomWidth: hairlineWidth}]}> <View
style={[pal.border, {borderBottomWidth: StyleSheet.hairlineWidth}]}>
<NoFollowingFeed /> <NoFollowingFeed />
</View> </View>
)} )}
@@ -437,12 +438,12 @@ const styles = StyleSheet.create({
paddingHorizontal: 14, paddingHorizontal: 14,
paddingTop: 20, paddingTop: 20,
paddingBottom: 10, paddingBottom: 10,
borderBottomWidth: hairlineWidth, borderBottomWidth: StyleSheet.hairlineWidth,
}, },
itemContainer: { itemContainer: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
borderBottomWidth: hairlineWidth, borderBottomWidth: StyleSheet.hairlineWidth,
}, },
footerText: { footerText: {
paddingHorizontal: 26, paddingHorizontal: 26,
+9 -8
View File
@@ -75,17 +75,17 @@ function SuggestedItemsHeader({
) )
} }
type LoadMoreItems = type LoadMoreItem =
| { | {
type: 'profile' type: 'profile'
key: string key: string
avatar: string avatar: string | undefined
moderation: ModerationDecision moderation: ModerationDecision
} }
| { | {
type: 'feed' type: 'feed'
key: string key: string
avatar: string avatar: string | undefined
moderation: undefined moderation: undefined
} }
@@ -98,27 +98,28 @@ function LoadMore({
}) { }) {
const t = useTheme() const t = useTheme()
const {_} = useLingui() const {_} = useLingui()
const items = React.useMemo(() => { const items: LoadMoreItem[] = React.useMemo(() => {
return item.items return item.items
.map(_item => { .map(_item => {
let loadMoreItem: LoadMoreItem | undefined
if (_item.type === 'profile') { if (_item.type === 'profile') {
return { loadMoreItem = {
type: 'profile', type: 'profile',
key: _item.profile.did, key: _item.profile.did,
avatar: _item.profile.avatar, avatar: _item.profile.avatar,
moderation: moderateProfile(_item.profile, moderationOpts!), moderation: moderateProfile(_item.profile, moderationOpts!),
} }
} else if (_item.type === 'feed') { } else if (_item.type === 'feed') {
return { loadMoreItem = {
type: 'feed', type: 'feed',
key: _item.feed.uri, key: _item.feed.uri,
avatar: _item.feed.avatar, avatar: _item.feed.avatar,
moderation: undefined, moderation: undefined,
} }
} }
return undefined return loadMoreItem
}) })
.filter(Boolean) as LoadMoreItems[] .filter(<T,>(n?: T): n is T => Boolean(n))
}, [item.items, moderationOpts]) }, [item.items, moderationOpts])
if (items.length === 0) return null if (items.length === 0) return null
+2 -3
View File
@@ -62,13 +62,12 @@ import {UserAvatar} from 'view/com/util/UserAvatar'
import {ScrollView} from 'view/com/util/Views' import {ScrollView} from 'view/com/util/Views'
import {DeactivateAccountDialog} from '#/screens/Settings/components/DeactivateAccountDialog' import {DeactivateAccountDialog} from '#/screens/Settings/components/DeactivateAccountDialog'
import {useTheme} from '#/alf' import {useTheme} from '#/alf'
import {atoms as a} from '#/alf'
import {useDialogControl} from '#/components/Dialog' import {useDialogControl} from '#/components/Dialog'
import {BirthDateSettingsDialog} from '#/components/dialogs/BirthDateSettings' import {BirthDateSettingsDialog} from '#/components/dialogs/BirthDateSettings'
import {navigate, resetToTab} from '#/Navigation' import {navigate, resetToTab} from '#/Navigation'
import {Email2FAToggle} from './Email2FAToggle' import {Email2FAToggle} from './Email2FAToggle'
import {ExportCarDialog} from './ExportCarDialog' import {ExportCarDialog} from './ExportCarDialog'
import hairlineWidth = StyleSheet.hairlineWidth
import {atoms as a} from '#/alf'
function SettingsAccountCard({ function SettingsAccountCard({
account, account,
@@ -326,7 +325,7 @@ export function SettingsScreen({}: Props) {
showBackButton={isMobile} showBackButton={isMobile}
style={[ style={[
pal.border, pal.border,
{borderBottomWidth: hairlineWidth}, {borderBottomWidth: StyleSheet.hairlineWidth},
!isMobile && {borderLeftWidth: 1, borderRightWidth: 1}, !isMobile && {borderLeftWidth: 1, borderRightWidth: 1},
]}> ]}>
<View style={{flex: 1}}> <View style={{flex: 1}}>
@@ -1,7 +1,6 @@
import {StyleSheet} from 'react-native' import {StyleSheet} from 'react-native'
import {colors} from 'lib/styles' import {colors} from 'lib/styles'
import hairlineWidth = StyleSheet.hairlineWidth
export const styles = StyleSheet.create({ export const styles = StyleSheet.create({
bottomBar: { bottomBar: {
@@ -10,7 +9,7 @@ export const styles = StyleSheet.create({
left: 0, left: 0,
right: 0, right: 0,
flexDirection: 'row', flexDirection: 'row',
borderTopWidth: hairlineWidth, borderTopWidth: StyleSheet.hairlineWidth,
paddingLeft: 5, paddingLeft: 5,
paddingRight: 10, paddingRight: 10,
}, },
+3 -4
View File
@@ -11,10 +11,9 @@ import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {s} from 'lib/styles' import {s} from 'lib/styles'
import {TextLink} from 'view/com/util/Link' import {TextLink} from 'view/com/util/Link'
import {Text} from 'view/com/util/text/Text' import {Text} from 'view/com/util/text/Text'
import {ProgressGuideList} from '#/components/ProgressGuide/List'
import {DesktopFeeds} from './Feeds' import {DesktopFeeds} from './Feeds'
import {DesktopSearch} from './Search' import {DesktopSearch} from './Search'
import hairlineWidth = StyleSheet.hairlineWidth
import {ProgressGuideList} from '#/components/ProgressGuide/List'
export function DesktopRightNav({routeName}: {routeName: string}) { export function DesktopRightNav({routeName}: {routeName: string}) {
const pal = usePalette('default') const pal = usePalette('default')
@@ -135,8 +134,8 @@ const styles = StyleSheet.create({
marginBottom: 10, marginBottom: 10,
}, },
desktopFeedsContainer: { desktopFeedsContainer: {
borderTopWidth: hairlineWidth, borderTopWidth: StyleSheet.hairlineWidth,
borderBottomWidth: hairlineWidth, borderBottomWidth: StyleSheet.hairlineWidth,
marginTop: 18, marginTop: 18,
marginBottom: 18, marginBottom: 18,
}, },
+5
View File
@@ -13345,6 +13345,11 @@ history@^5.3.0:
dependencies: dependencies:
"@babel/runtime" "^7.7.6" "@babel/runtime" "^7.7.6"
hls.js@^1.5.11:
version "1.5.11"
resolved "https://registry.yarnpkg.com/hls.js/-/hls.js-1.5.11.tgz#3941347df454983859ae8c75fe19e8818719a826"
integrity sha512-q3We1izi2+qkOO+TvZdHv+dx6aFzdtk3xc1/Qesrvto4thLTT/x/1FK85c5h1qZE4MmMBNgKg+MIW8nxQfxwBw==
hmac-drbg@^1.0.1: hmac-drbg@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"