492 lines
20 KiB
TypeScript
492 lines
20 KiB
TypeScript
// Metro module 15367 (f15368).
|
|
import _slicedToArray from "../../../../_bundle/_slicedToArray";
|
|
import { useState, useMemo, useRef, useCallback, useContext } from "react";
|
|
import { Pressable, View } from "react-native";
|
|
import AccessibilityStore from "../../../a11y/AccessibilityStore";
|
|
import ChannelStore from "../../../../stores/ChannelStore";
|
|
import RelationshipStore from "../../../../stores/RelationshipStore";
|
|
import UserStore from "../../../../stores/UserStore";
|
|
import ICYMIStore from "../../ICYMIStore";
|
|
import { AnalyticsObjectTypes, AnalyticsObjects, AnalyticsPages } from "../../../../Constants";
|
|
import { createStyles } from "../../../../design/components/Styles/native/createStyles";
|
|
import Module712 from "../../../../_bundle/Module712";
|
|
import { urlMatchesFileExtension } from "../../../messages/MediaFormatTesters";
|
|
import { GIF_RE_IOS } from "../../../../ConstantsIOS";
|
|
import { useStateFromStores } from "../../../../_bundle/Module589";
|
|
import Video from "../../../../components_native/common/Video";
|
|
import ReanimatedRexport, { useAnimatedStyle } from "../../../reanimated/ReanimatedRexport";
|
|
import * as _timing from "../../../../design/animation/reanimated/timing/timing";
|
|
import { createThumbhashImageFromPlaceholder } from "../../../thumbhash/ThumbhashUtils";
|
|
import FastImage from "../../../../components_native/common/FastImage";
|
|
import { Text } from "../../../../design/components/Text/native/Text";
|
|
import { intl, t } from "../../../../intl/index.native";
|
|
import { PlayIcon } from "../../../../design/components/Icon/native/redesign/generated/PlayIcon";
|
|
import { isVideo } from "../../../../utils/native/UploadUtils";
|
|
import { PressableOpacity } from "../../../../design/void/Pressables/native/Pressables";
|
|
import ICYMIActionCreators from "../../ICYMIActionCreators";
|
|
import { VoiceXIcon } from "../../../../design/components/Icon/native/redesign/generated/VoiceXIcon";
|
|
import { VoiceNormalIcon } from "../../../../design/components/Icon/native/redesign/generated/VoiceNormalIcon";
|
|
import { ICYMIContext } from "../ICYMIContext";
|
|
import { extractMediaSourcesFromMessage, flattenSource } from "../../../media_viewer/native/MediaSourceUtil";
|
|
import { GRAVITY_VALID_EMBED_TYPES } from "../../ICYMITypes";
|
|
import { isNotNullish } from "../../../../utils/GlobalUtils";
|
|
import { partition } from "../../../../_bundle/Module12";
|
|
import { ack } from "../../../../actions/ReadStateActionCreators";
|
|
import { openMediaModal } from "../../../media_viewer/native/components/openMediaModal";
|
|
import { computeChannelName } from "../../../channel/useChannelName";
|
|
let useStyles, __initData, MediaMosaicImage, Media, OneImageRow, ThreeImagesRow, TwoImagesRow, ThreeImages, FourImages, SingleImage, GravityAttachmentMediaMosaic;
|
|
const MediaMosaicVideo = function MediaMosaicVideo(props) {
|
|
const { source: source3, height, width: width2, autoplay, style } = props;
|
|
const styles = useStyles();
|
|
let muted = useStateFromStores([ICYMIStore], () => ICYMIStore.videosMuted());
|
|
let videoURI = source3.videoURI;
|
|
if (videoURI == null) {
|
|
videoURI = source3.sourceURI;
|
|
}
|
|
if (videoURI == null) {
|
|
videoURI = source3.uri;
|
|
}
|
|
const props2 = {
|
|
src: {
|
|
videoURI
|
|
},
|
|
height,
|
|
width: width2,
|
|
postponeRender: false,
|
|
paused: !autoplay,
|
|
muted,
|
|
resizeMode: "cover",
|
|
style: [styles.media, style],
|
|
videoStyle: styles.video,
|
|
disableFocus: null
|
|
};
|
|
if (!muted) {
|
|
muted = source3.isGIFV;
|
|
}
|
|
props2.disableFocus = muted;
|
|
return <Video {...props2} />;
|
|
};
|
|
MediaMosaicImage = function MediaMosaicImage(props) {
|
|
const { source, dimensions: dimensions2, style, isSpoiler } = props;
|
|
const styles = useStyles();
|
|
const [state, setState] = useState(false);
|
|
const c = function c() {
|
|
return {
|
|
opacity: _timing.withTiming(state ? 0 : 1, {
|
|
duration: 150
|
|
})
|
|
};
|
|
};
|
|
c.__closure = {
|
|
withTiming: _timing.withTiming,
|
|
imageFinishedLoading: state
|
|
};
|
|
c.__workletHash = 7803531897566;
|
|
c.__initData = __initData;
|
|
return <>
|
|
{[<ReanimatedRexport.View style={[useAnimatedStyle(c), styles.thumbhashMedia]}>
|
|
<FastImage source={useMemo(() => {
|
|
if (source.placeholder != null) {
|
|
return {
|
|
uri: createThumbhashImageFromPlaceholder(source.placeholder),
|
|
width: source.width,
|
|
height: source.height
|
|
};
|
|
}
|
|
}, [source.height, source.placeholder, source.width])} style={[style, styles.media, dimensions2]} />
|
|
</ReanimatedRexport.View>, <ReanimatedRexport.Image key={source.uri} source={source} style={[styles.media, style, dimensions2]} onLoadEnd={() => setState(true)} blurRadius={isSpoiler ? 100 : 0} />]}
|
|
</>;
|
|
};
|
|
Media = function Media(props) {
|
|
let stateFromStores;
|
|
const { source: source3, dimensions: dimensions2, initialIndex, handlePressMedia, style, visible: visible2 } = props;
|
|
stateFromStores = void 0;
|
|
const styles = useStyles();
|
|
const ref = useRef(null);
|
|
let spoiler = source3.spoiler;
|
|
if (spoiler == null) {
|
|
spoiler = false;
|
|
}
|
|
const [state2, setState2] = useState(spoiler);
|
|
const callback2 = useCallback(() => {
|
|
if (state2) {
|
|
setState2(false);
|
|
} else {
|
|
handlePressMedia({
|
|
ref,
|
|
initialIndex
|
|
});
|
|
}
|
|
}, [handlePressMedia, initialIndex, state2]);
|
|
stateFromStores = useStateFromStores([ICYMIStore], () => ICYMIStore.videosMuted());
|
|
const stateFromStores3 = useStateFromStores([AccessibilityStore], () => AccessibilityStore.useReducedMotion);
|
|
const r72 = [state2 ? <View style={styles.centerContainer}>
|
|
<View style={styles.spoilerText}>
|
|
<Text maxFontSizeMultiplier={1} variant="heading-md/semibold" color="mobile-text-heading-primary">
|
|
{intl.string(t["F+x38C"]).toUpperCase()}
|
|
</Text>
|
|
</View>
|
|
</View> : state2, source3.videoURI != null && !state2 && !(source3.isGIFV || urlMatchesFileExtension(source3.sourceURI, GIF_RE_IOS)) && stateFromStores3 && <View style={styles.centerContainer}>
|
|
<View style={styles.videoIcon}>
|
|
<PlayIcon color={Module712.colors.REDESIGN_BUTTON_TERTIARY_TEXT} size="lg" />
|
|
</View>
|
|
</View>, isVideo(source3.uri) ? source3.isGIFV || urlMatchesFileExtension(source3.sourceURI, GIF_RE_IOS) ? null : <View style={styles.absoluteContainer}>
|
|
<PressableOpacity style={[styles.muteIcon, stateFromStores ? styles.iconBg : styles.iconBgSelected]} onPress={() => ICYMIActionCreators.setVideosMuted(!stateFromStores)} activeOpacity={0.8}>
|
|
{stateFromStores ? <VoiceXIcon color={Module712.colors.INTERACTIVE_TEXT_DEFAULT} size="sm" /> : <VoiceNormalIcon color={Module712.colors.BLACK} size="sm" />}
|
|
</PressableOpacity>
|
|
</View> : null, !(!isVideo(source3.uri) && !(source3.isGIFV || urlMatchesFileExtension(source3.sourceURI, GIF_RE_IOS))) && source3.videoURI != null ? <MediaMosaicVideo source={source3} height={dimensions2.height} width={dimensions2.width} style={style} autoplay={!!visible2} /> : <MediaMosaicImage source={source3} style={style} dimensions={dimensions2} isSpoiler={state2} />];
|
|
return <Pressable ref={ref} onPress={callback2} style={dimensions2}>{r72}</Pressable>;
|
|
};
|
|
OneImageRow = function OneImageRow(props) {
|
|
const { source: source3, handlePressMedia: handlePressMedia5 } = props;
|
|
let widthOverride3 = props.widthOverride;
|
|
const styles = useStyles();
|
|
const context = useContext(ICYMIContext);
|
|
if (widthOverride3 == null) {
|
|
widthOverride3 = context?.width - context.inset - 2 * context.margin;
|
|
}
|
|
return <View style={[styles.imageRow, styles.topRow]}>
|
|
<Media handlePressMedia={handlePressMedia5} initialIndex={0} source={source3} dimensions={{
|
|
width: widthOverride3,
|
|
height: widthOverride3 / 1.5
|
|
}} />
|
|
</View>;
|
|
};
|
|
ThreeImagesRow = function ThreeImagesRow(props) {
|
|
const sources2 = props.sources;
|
|
let start = props.start;
|
|
let end = props.end;
|
|
const { offset, handlePressMedia: handlePressMedia2 } = props;
|
|
let widthOverride3 = props.widthOverride;
|
|
const styles = useStyles();
|
|
const context = useContext(ICYMIContext);
|
|
if (widthOverride3 == null) {
|
|
widthOverride3 = context?.width - context.inset - 2 * context.margin;
|
|
}
|
|
let c21917_2 = widthOverride3;
|
|
const style = [styles.imageRow, , ,];
|
|
if (start) {
|
|
start = styles.topRow;
|
|
}
|
|
style[1] = start;
|
|
if (end) {
|
|
end = styles.bottomRow;
|
|
}
|
|
style[2] = end;
|
|
return <View style={style}>
|
|
{sources2.map((item, index) => <Media key={offset + index} handlePressMedia={handlePressMedia2} initialIndex={offset + index} source={item} dimensions={{
|
|
width: (c21917_2 - 8) / 3,
|
|
height: (c21917_2 - 8) / 3
|
|
}} />)}
|
|
</View>;
|
|
};
|
|
TwoImagesRow = function TwoImagesRow(props) {
|
|
const { sources: sources2, handlePressMedia: handlePressMedia3 } = props;
|
|
let end = props.end;
|
|
let widthOverride3 = props.widthOverride;
|
|
const styles = useStyles();
|
|
const context = useContext(ICYMIContext);
|
|
if (widthOverride3 == null) {
|
|
widthOverride3 = context?.width - context.inset - 2 * context.margin;
|
|
}
|
|
const dimensions = {
|
|
width: (widthOverride3 - 4) / 2,
|
|
height: (widthOverride3 - 4) / 2 / 0.75
|
|
};
|
|
const style = [styles.imageRow, styles.topRow, ,];
|
|
if (end) {
|
|
end = styles.bottomRow;
|
|
}
|
|
style[2] = end;
|
|
return <View style={style}>
|
|
{sources2.map((item, index) => <Media key={index} handlePressMedia={handlePressMedia3} initialIndex={index} source={item} dimensions={dimensions} />)}
|
|
</View>;
|
|
};
|
|
ThreeImages = function ThreeImages(props) {
|
|
const { sources: sources2, handlePressMedia: handlePressMedia5 } = props;
|
|
let widthOverride3 = props.widthOverride;
|
|
const styles = useStyles();
|
|
const context = useContext(ICYMIContext);
|
|
if (widthOverride3 == null) {
|
|
widthOverride3 = context?.width - context.inset - 2 * context.margin;
|
|
}
|
|
return <View style={[styles.imagesContainer, styles.imageRow]}>
|
|
<View style={styles.leftColumn}>
|
|
<Media handlePressMedia={handlePressMedia5} initialIndex={0} source={sources2[0]} dimensions={{
|
|
width: 2 * widthOverride3 / 3 - 4,
|
|
height: 2 * widthOverride3 / 3
|
|
}} />
|
|
</View>
|
|
<View style={styles.rightColumn}>
|
|
<Media handlePressMedia={handlePressMedia5} initialIndex={1} source={sources2[1]} dimensions={{
|
|
width: widthOverride3 / 3,
|
|
height: widthOverride3 / 3
|
|
}} />
|
|
<Media handlePressMedia={handlePressMedia5} initialIndex={2} source={sources2[2]} dimensions={{
|
|
width: widthOverride3 / 3,
|
|
height: widthOverride3 / 3
|
|
}} />
|
|
</View>
|
|
</View>;
|
|
};
|
|
FourImages = function FourImages(props) {
|
|
const { sources: sources2, handlePressMedia: handlePressMedia5 } = props;
|
|
let widthOverride3 = props.widthOverride;
|
|
const styles = useStyles();
|
|
const context = useContext(ICYMIContext);
|
|
if (widthOverride3 == null) {
|
|
widthOverride3 = context?.width - context.inset - 2 * context.margin;
|
|
}
|
|
const dimensions2 = {
|
|
width: widthOverride3 / 2 - 4,
|
|
height: (widthOverride3 / 2 - 4) / 1.5
|
|
};
|
|
return <View style={styles.imagesContainer}>
|
|
<View style={[styles.imageRow, styles.topRow]}>
|
|
<Media handlePressMedia={handlePressMedia5} initialIndex={0} source={sources2[0]} dimensions={dimensions2} />
|
|
<Media handlePressMedia={handlePressMedia5} initialIndex={1} source={sources2[1]} dimensions={dimensions2} />
|
|
</View>
|
|
<View style={[styles.imageRow, styles.bottomRow]}>
|
|
<Media handlePressMedia={handlePressMedia5} initialIndex={2} source={sources2[2]} dimensions={dimensions2} />
|
|
<Media handlePressMedia={handlePressMedia5} initialIndex={3} source={sources2[3]} dimensions={dimensions2} />
|
|
</View>
|
|
</View>;
|
|
};
|
|
SingleImage = function SingleImage(props) {
|
|
const { source: source2, initialIndex: initialIndex2, handlePressMedia: handlePressMedia5, visible: visible2 } = props;
|
|
let widthOverride3 = props.widthOverride;
|
|
const styles = useStyles();
|
|
const context = useContext(ICYMIContext);
|
|
if (widthOverride3 == null) {
|
|
widthOverride3 = context?.width - context.inset - 2 * context.margin;
|
|
}
|
|
const width = widthOverride3;
|
|
return <View style={styles.imagesContainer}>
|
|
<Media handlePressMedia={handlePressMedia5} initialIndex={initialIndex2} source={source2} dimensions={useMemo(() => {
|
|
const r8 = source2.width / source2.height;
|
|
if (r8 >= 1) {
|
|
const width3 = Math.min(source2.width, width);
|
|
return {
|
|
height: width3 / r8,
|
|
width: width3
|
|
};
|
|
}
|
|
const height = Math.min(source2.height, 330);
|
|
const width2 = height * r8;
|
|
return width2 > width ? {
|
|
width,
|
|
height: width / r8
|
|
} : {
|
|
width: width2,
|
|
height
|
|
};
|
|
}, [source2.width, source2.height, width])} style={styles.singleImage} visible={visible2} />
|
|
</View>;
|
|
};
|
|
GravityAttachmentMediaMosaic = function GravityAttachmentMediaMosaic(props) {
|
|
const { sources, handlePressMedia: handlePressMedia4, visible: visible2, widthOverride } = props;
|
|
const length = sources.length;
|
|
const styles = useStyles();
|
|
const memo = useMemo(() => {
|
|
const r3 = length % 3;
|
|
let r0 = r3 !== 0 ? r3 : 3;
|
|
const r4 = [];
|
|
r4.push(sources.slice(0, r0));
|
|
while (r0 < length) {
|
|
const r15 = r0 + 3;
|
|
r4.push(sources.slice(r0, r15));
|
|
r0 = r15;
|
|
}
|
|
return r4;
|
|
}, [length, sources]);
|
|
return length !== 0 ? length === 1 ? <SingleImage widthOverride={widthOverride} initialIndex={0} handlePressMedia={handlePressMedia4} source={sources[0]} visible={visible2} /> : length === 3 ? <ThreeImages widthOverride={widthOverride} handlePressMedia={handlePressMedia4} sources={sources} /> : length === 4 ? <FourImages widthOverride={widthOverride} handlePressMedia={handlePressMedia4} sources={sources} /> : <View style={styles.imagesContainer}>
|
|
{memo.map((item, index) => {
|
|
if (item.length === 1) {
|
|
return <OneImageRow key={index} handlePressMedia={handlePressMedia4} source={item[0]} />;
|
|
}
|
|
if (item.length === 2) {
|
|
return <TwoImagesRow key={index} widthOverride={widthOverride} sources={item} handlePressMedia={handlePressMedia4} end={index === memo.length - 1} />;
|
|
}
|
|
let offset2 = 0;
|
|
const start = index === 0;
|
|
if (!start) {
|
|
offset2 = memo[0].length + 3 * (index - 1);
|
|
}
|
|
return <ThreeImagesRow key={index} widthOverride={widthOverride} handlePressMedia={handlePressMedia4} offset={offset2} sources={item} start={start} end={index === memo.length - 1} />;
|
|
})}
|
|
</View> : null;
|
|
};
|
|
useStyles = createStyles(() => ({
|
|
media: {
|
|
borderRadius: Module712.radii.xs
|
|
},
|
|
video: {
|
|
backgroundColor: Module712.colors.BACKGROUND_SURFACE_HIGH
|
|
},
|
|
thumbhashMedia: {
|
|
position: "absolute",
|
|
top: 0,
|
|
left: 0,
|
|
zIndex: 1
|
|
},
|
|
container: {
|
|
gap: 4
|
|
},
|
|
imagesContainer: {
|
|
justifyContent: "center",
|
|
gap: 4,
|
|
width: "100%"
|
|
},
|
|
imageRow: {
|
|
flexDirection: "row",
|
|
gap: 4
|
|
},
|
|
topRow: {
|
|
overflow: "hidden",
|
|
borderTopEndRadius: Module712.radii.lg,
|
|
borderTopStartRadius: Module712.radii.lg
|
|
},
|
|
bottomRow: {
|
|
overflow: "hidden",
|
|
borderBottomEndRadius: Module712.radii.lg,
|
|
borderBottomStartRadius: Module712.radii.lg
|
|
},
|
|
videoIcon: {
|
|
backgroundColor: Module712.colors.BACKGROUND_BASE_LOW,
|
|
borderRadius: Module712.radii.round,
|
|
padding: 16
|
|
},
|
|
muteIcon: {
|
|
position: "absolute",
|
|
borderRadius: Module712.radii.round,
|
|
padding: Module712.space.PX_4,
|
|
bottom: 8,
|
|
right: 8
|
|
},
|
|
spoilerText: {
|
|
backgroundColor: Module712.colors.SPOILER_HIDDEN_BACKGROUND,
|
|
borderRadius: Module712.radii.lg,
|
|
paddingHorizontal: Module712.space.PX_12,
|
|
paddingVertical: 6
|
|
},
|
|
leftColumn: {
|
|
overflow: "hidden",
|
|
borderTopStartRadius: Module712.radii.lg,
|
|
borderBottomStartRadius: Module712.radii.lg
|
|
},
|
|
rightColumn: {
|
|
overflow: "hidden",
|
|
borderTopEndRadius: Module712.radii.lg,
|
|
borderBottomEndRadius: Module712.radii.lg,
|
|
gap: 4
|
|
},
|
|
singleImage: {
|
|
overflow: "hidden",
|
|
borderRadius: Module712.radii.lg
|
|
},
|
|
centerContainer: {
|
|
position: "absolute",
|
|
width: "100%",
|
|
height: "100%",
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
zIndex: 2
|
|
},
|
|
absoluteContainer: {
|
|
position: "absolute",
|
|
width: "100%",
|
|
height: "100%",
|
|
zIndex: 2
|
|
},
|
|
iconBg: {
|
|
backgroundColor: Module712.colors.BACKGROUND_BASE_LOWER
|
|
},
|
|
iconBgSelected: {
|
|
backgroundColor: Module712.colors.WHITE
|
|
}
|
|
}));
|
|
__initData = {
|
|
code: "function ICYMIMediaMosaicTsx1(){const{withTiming,imageFinishedLoading}=this.__closure;return{opacity:withTiming(imageFinishedLoading?0:1,{duration:150})};}"
|
|
};
|
|
export default function ICYMIMediaMosaic(props) {
|
|
let r45;
|
|
let setState3, visible, allMediaSources, nonEmbedSources, callback;
|
|
const { message, widthOverride: widthOverride2, visible: visible2, itemType } = props;
|
|
setState3 = void 0;
|
|
visible = void 0;
|
|
allMediaSources = void 0;
|
|
nonEmbedSources = void 0;
|
|
callback = void 0;
|
|
const styles = useStyles();
|
|
const stateFromStores2 = useStateFromStores([ChannelStore], () => ChannelStore.getChannel(message.getChannelId()));
|
|
let state;
|
|
[state, setState3] = useState(false);
|
|
const r13 = state ? !state : visible2;
|
|
L7: {
|
|
visible = r13;
|
|
const memo2 = useMemo(() => {
|
|
const allMediaSources2 = extractMediaSourcesFromMessage(message, message, void 0, GRAVITY_VALID_EMBED_TYPES).map((item) => flattenSource(item)).filter(isNotNullish);
|
|
const r32 = _slicedToArray(partition(allMediaSources2, (a0) => a0.accessoryType === "embed"), 2);
|
|
return {
|
|
allMediaSources: allMediaSources2,
|
|
nonEmbedSources: r32[1],
|
|
embedSources: r32[0]
|
|
};
|
|
}, [message]);
|
|
allMediaSources = memo2.allMediaSources;
|
|
nonEmbedSources = memo2.nonEmbedSources;
|
|
const embedSources = memo2.embedSources;
|
|
callback = useCallback(({ ref: ref2, initialIndex: initialIndex2 }) => {
|
|
ICYMIActionCreators.itemInteracted(message.id, "message", "press_media");
|
|
ICYMIActionCreators.feedItemActioned({
|
|
itemId: message.id,
|
|
itemType,
|
|
actionParameters: {
|
|
actionGestureType: "press",
|
|
actionTargetElement: "media_mosaic",
|
|
actionIntentType: "open",
|
|
actionDestinationType: null
|
|
}
|
|
});
|
|
ack(message.channel_id, {
|
|
page: AnalyticsPages.ICYMI,
|
|
object: AnalyticsObjects.ACK_MEDIA_VIEWED,
|
|
objectType: AnalyticsObjectTypes.ACK_SEMI_AUTOMATIC
|
|
}, true, true, message.id);
|
|
const initialSources = [];
|
|
let c28009_0 = initialSources;
|
|
allMediaSources.forEach((allMediaSource) => {
|
|
c28009_0.push({
|
|
...allMediaSource,
|
|
embedURI: void 0
|
|
});
|
|
});
|
|
setState3(true);
|
|
openMediaModal({
|
|
disableDownload: false,
|
|
initialSources,
|
|
initialIndex: initialIndex2,
|
|
analyticsSource: "Channel",
|
|
channelId: message.channel_id,
|
|
contextName: stateFromStores2 != null ? computeChannelName(stateFromStores2, UserStore, RelationshipStore) : "",
|
|
contextIcon: void 0,
|
|
originViewOrOriginLayout: ref2.current,
|
|
onClose: () => setState3(false)
|
|
});
|
|
}, [message.channel_id, message.id, allMediaSources, stateFromStores2, itemType]);
|
|
if (nonEmbedSources.length === 0) {
|
|
const length2 = embedSources.length;
|
|
r45 = null;
|
|
if (length2 === 0) {
|
|
break L7;
|
|
}
|
|
}
|
|
r45 = <View key={`message-image-${message.id}`} style={styles.container}>
|
|
<GravityAttachmentMediaMosaic widthOverride={widthOverride2} sources={nonEmbedSources} handlePressMedia={callback} visible={visible} />
|
|
{embedSources == null ? void 0 : embedSources.map((embedSource, index) => <SingleImage key={`gif-${index}`} widthOverride={widthOverride2} handlePressMedia={callback} initialIndex={index + nonEmbedSources.length} source={embedSource} visible={visible} />)}
|
|
</View>;
|
|
}
|
|
return r45;
|
|
};
|
|
export { GravityAttachmentMediaMosaic };
|