make standardsiteembed peekable
This commit is contained in:
@@ -976,9 +976,6 @@
|
||||
}
|
||||
},
|
||||
"src/lib/hooks/useOpenLink.ts": {
|
||||
"@typescript-eslint/no-floating-promises": {
|
||||
"count": 2
|
||||
},
|
||||
"@typescript-eslint/require-await": {
|
||||
"count": 1
|
||||
}
|
||||
|
||||
@@ -127,7 +127,6 @@ export const StandardSiteEmbed = ({
|
||||
style={[
|
||||
a.flex_col,
|
||||
a.rounded_lg,
|
||||
a.overflow_hidden,
|
||||
a.w_full,
|
||||
a.border,
|
||||
t.atoms.bg,
|
||||
@@ -135,126 +134,149 @@ export const StandardSiteEmbed = ({
|
||||
preview && a.pointer_events_none,
|
||||
style,
|
||||
]}>
|
||||
{/*
|
||||
* The article is an in-flow `Link` wrapping the content so the iOS peek
|
||||
* menu has something to lift. The publication footer below lives outside
|
||||
* it - it has its own link (to the publication) and Subscribe button, so
|
||||
* it can't be nested inside the article's peek.
|
||||
*/}
|
||||
<Link
|
||||
peek
|
||||
shouldProxy
|
||||
to={view.uri}
|
||||
label={view.title || l`Open link to ${niceUrl}`}
|
||||
onPress={onPress}
|
||||
onLongPress={onLongPress}
|
||||
style={[a.absolute, a.inset_0, a.z_10]}
|
||||
style={[a.rounded_lg]}
|
||||
{...web({
|
||||
onMouseEnter: onInteract,
|
||||
onMouseLeave: onInteractOut,
|
||||
})}
|
||||
onFocus={onInteract}
|
||||
onBlur={onInteractOut}>
|
||||
<></>
|
||||
</Link>
|
||||
|
||||
<View
|
||||
style={[
|
||||
a.w_full,
|
||||
a.z_10,
|
||||
a.pointer_events_none,
|
||||
interacted && [t.atoms.bg_contrast_25],
|
||||
]}>
|
||||
{imageUri ? (
|
||||
<Image
|
||||
style={[a.aspect_card]}
|
||||
source={{uri: imageUri}}
|
||||
accessibilityIgnoresInvertColors
|
||||
loading="lazy"
|
||||
useAppleWebpCodec
|
||||
/>
|
||||
) : undefined}
|
||||
|
||||
<View
|
||||
style={[
|
||||
a.flex_1,
|
||||
a.pt_sm,
|
||||
hasMedia && a.border_t,
|
||||
interacted
|
||||
? t.atoms.border_contrast_high
|
||||
: t.atoms.border_contrast_low,
|
||||
{gap: 3},
|
||||
isStandard && a.pt_md,
|
||||
]}>
|
||||
{() => (
|
||||
<View
|
||||
style={[
|
||||
a.pb_xs,
|
||||
a.px_md,
|
||||
{gap: 3},
|
||||
isStandard && [{gap: 5}, a.pb_sm],
|
||||
a.w_full,
|
||||
a.overflow_hidden,
|
||||
// The container can't clip (overflow_hidden there breaks the peek
|
||||
// lift animation), so each half rounds its own outer corners to
|
||||
// match the container border. The article rounds the top, plus the
|
||||
// bottom when there's no footer beneath it.
|
||||
{
|
||||
borderTopLeftRadius: a.rounded_lg.borderRadius,
|
||||
borderTopRightRadius: a.rounded_lg.borderRadius,
|
||||
},
|
||||
!view.source && {
|
||||
borderBottomLeftRadius: a.rounded_lg.borderRadius,
|
||||
borderBottomRightRadius: a.rounded_lg.borderRadius,
|
||||
},
|
||||
interacted ? t.atoms.bg_contrast_25 : t.atoms.bg,
|
||||
]}>
|
||||
<Text
|
||||
emoji
|
||||
numberOfLines={3}
|
||||
style={[
|
||||
a.text_md,
|
||||
a.font_semi_bold,
|
||||
a.leading_snug,
|
||||
isStandard && [a.text_lg, a.font_bold],
|
||||
]}>
|
||||
{view.title}
|
||||
</Text>
|
||||
{view.description ? (
|
||||
<Text
|
||||
emoji
|
||||
numberOfLines={view.thumb ? 2 : 4}
|
||||
style={[a.text_sm, a.leading_snug]}>
|
||||
{view.description}
|
||||
</Text>
|
||||
{imageUri ? (
|
||||
<Image
|
||||
style={[a.aspect_card]}
|
||||
source={{uri: imageUri}}
|
||||
accessibilityIgnoresInvertColors
|
||||
loading="lazy"
|
||||
useAppleWebpCodec
|
||||
/>
|
||||
) : undefined}
|
||||
|
||||
{isStandard && (view.createdAt || view.readingTime) && (
|
||||
<View
|
||||
style={[
|
||||
a.flex_1,
|
||||
a.pt_sm,
|
||||
hasMedia && a.border_t,
|
||||
interacted
|
||||
? t.atoms.border_contrast_high
|
||||
: t.atoms.border_contrast_low,
|
||||
{gap: 3},
|
||||
isStandard && a.pt_md,
|
||||
]}>
|
||||
<View
|
||||
style={[a.flex_row, a.align_center, a.gap_md, {paddingTop: 2}]}>
|
||||
{view.createdAt && (
|
||||
style={[
|
||||
a.pb_xs,
|
||||
a.px_md,
|
||||
{gap: 3},
|
||||
isStandard && [{gap: 5}, a.pb_sm],
|
||||
]}>
|
||||
<Text
|
||||
emoji
|
||||
numberOfLines={3}
|
||||
style={[
|
||||
a.text_md,
|
||||
a.font_semi_bold,
|
||||
a.leading_snug,
|
||||
isStandard && [a.text_lg, a.font_bold],
|
||||
]}>
|
||||
{view.title}
|
||||
</Text>
|
||||
{view.description ? (
|
||||
<Text
|
||||
style={[
|
||||
a.text_xs,
|
||||
a.leading_snug,
|
||||
t.atoms.text_contrast_medium,
|
||||
]}>
|
||||
{niceDate(i18n, view.createdAt, 'long', 'none')}
|
||||
emoji
|
||||
numberOfLines={view.thumb ? 2 : 4}
|
||||
style={[a.text_sm, a.leading_snug]}>
|
||||
{view.description}
|
||||
</Text>
|
||||
)}
|
||||
{view.readingTime && (
|
||||
<View style={[a.flex_row, a.align_center, a.gap_2xs]}>
|
||||
<Clock size="xs" style={t.atoms.text_contrast_medium} />
|
||||
<Text
|
||||
style={[
|
||||
a.text_xs,
|
||||
a.leading_snug,
|
||||
t.atoms.text_contrast_medium,
|
||||
]}>
|
||||
{l({
|
||||
message: plural(view.readingTime, {
|
||||
one: '#m',
|
||||
other: '#m',
|
||||
}),
|
||||
comment: `How long it takes to read an article, in minutes. Displayed in a short form, e.g. "5m" for 5 minutes.`,
|
||||
})}
|
||||
</Text>
|
||||
) : undefined}
|
||||
|
||||
{isStandard && (view.createdAt || view.readingTime) && (
|
||||
<View
|
||||
style={[
|
||||
a.flex_row,
|
||||
a.align_center,
|
||||
a.gap_md,
|
||||
{paddingTop: 2},
|
||||
]}>
|
||||
{view.createdAt && (
|
||||
<Text
|
||||
style={[
|
||||
a.text_xs,
|
||||
a.leading_snug,
|
||||
t.atoms.text_contrast_medium,
|
||||
]}>
|
||||
{niceDate(i18n, view.createdAt, 'long', 'none')}
|
||||
</Text>
|
||||
)}
|
||||
{view.readingTime && (
|
||||
<View style={[a.flex_row, a.align_center, a.gap_2xs]}>
|
||||
<Clock size="xs" style={t.atoms.text_contrast_medium} />
|
||||
<Text
|
||||
style={[
|
||||
a.text_xs,
|
||||
a.leading_snug,
|
||||
t.atoms.text_contrast_medium,
|
||||
]}>
|
||||
{l({
|
||||
message: plural(view.readingTime, {
|
||||
one: '#m',
|
||||
other: '#m',
|
||||
}),
|
||||
comment: `How long it takes to read an article, in minutes. Displayed in a short form, e.g. "5m" for 5 minutes.`,
|
||||
})}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{!view.source && (
|
||||
<View style={[a.px_md]}>
|
||||
<Divider />
|
||||
<View style={[a.py_sm]}>
|
||||
<StandardSiteMetaRow preview={preview} view={view} />
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{!view.source && (
|
||||
<View style={[a.px_md]}>
|
||||
<Divider />
|
||||
<View style={[a.py_sm]}>
|
||||
<StandardSiteMetaRow preview={preview} view={view} />
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</Link>
|
||||
|
||||
{view.source && (
|
||||
<View style={[a.z_20]}>
|
||||
<View>
|
||||
<Divider />
|
||||
<PublicationFooter
|
||||
preview={preview}
|
||||
@@ -629,6 +651,14 @@ export function PublicationFooter({
|
||||
a.justify_between,
|
||||
a.p_md,
|
||||
a.gap_md,
|
||||
a.overflow_hidden,
|
||||
// Rounds the bottom corners to match the container border, since the
|
||||
// container itself can't clip (overflow_hidden there breaks the peek
|
||||
// lift animation on the article above).
|
||||
{
|
||||
borderBottomLeftRadius: a.rounded_lg.borderRadius,
|
||||
borderBottomRightRadius: a.rounded_lg.borderRadius,
|
||||
},
|
||||
gtPhone && [a.flex_row, a.gap_sm],
|
||||
interactedOuter && t.atoms.bg_contrast_25,
|
||||
preview && a.pointer_events_none,
|
||||
|
||||
@@ -66,12 +66,12 @@ export function useOpenLink() {
|
||||
}).catch(err => {
|
||||
if (__DEV__)
|
||||
logger.error('Could not open web browser', {message: err})
|
||||
Linking.openURL(url)
|
||||
void Linking.openURL(url)
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
Linking.openURL(url)
|
||||
void Linking.openURL(url)
|
||||
},
|
||||
[ax, enabled, inAppBrowserConsentControl, t, dialogContext],
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user