Files
discord-decomp/modules/media_panel/native/MediaPlaybackPip.tsx
T
2026-11-01 12:12:55 +02:00

348 lines
15 KiB
TypeScript

// Metro module 16044 (f16045).
import { useState, useMemo, useCallback, useRef, useEffect, useContext } from "react";
import { Easing, TouchableOpacity, View, StyleSheet } from "react-native";
import ChannelStore from "../../../stores/ChannelStore";
import MessageStore from "../../../stores/MessageStore";
import RelationshipStore from "../../../stores/RelationshipStore";
import UserStore from "../../../stores/UserStore";
import { AnalyticEvents, MessageFlags, Routes } from "../../../Constants";
import { SquarePIPReferenceDimensions } from "../../voice_panel/native/pip/VoicePanelPIPConstants";
import { createStyles } from "../../../design/components/Styles/native/createStyles";
import Module712 from "../../../_bundle/Module712";
import { useToken } from "../../../design/tokens/native/useToken";
import { useStateFromStores } from "../../../_bundle/Module589";
import { computeChannelName } from "../../channel/useChannelName";
import getDisplayFilename from "../../messages/getDisplayFilename";
import { Text } from "../../../design/components/Text/native/Text";
import { Marquee } from "../../../_bundle/AnimatedChild";
import Module4611 from "../../../_bundle/Module4611";
import MessageActionCreators from "../../../actions/MessageActionCreators";
import safeTransitionTo_native from "../../links/safeTransitionTo.native";
import ReanimatedRexport, { useAnimatedStyle } from "../../reanimated/ReanimatedRexport";
import * as _timing from "../../../design/animation/reanimated/timing/timing";
import { intl, t } from "../../../intl/index.native";
import { BackgroundBlurFill } from "../../../design/components/experimental/native";
import { ArrowLargeLeftIcon } from "../../../design/components/Icon/native/redesign/generated/ArrowLargeLeftIcon";
import { XLargeIcon } from "../../../design/components/Icon/native/redesign/generated/XLargeIcon";
import AnalyticsUtils from "../../../utils/AnalyticsUtils";
import MediaPlayerManager, { useMediaPlayerManagerStore } from "../../media/native/MediaPlayerManager";
import { useShallow } from "../../../_bundle/Module3804";
import MediaPlaybackPanelStateContext from "./MediaPlaybackPanelStateContext";
import { PauseIcon } from "../../../design/components/Icon/native/redesign/generated/PauseIcon";
import { PlayIcon } from "../../../design/components/Icon/native/redesign/generated/PlayIcon";
import { AnimatedCircularProgress } from "../../../_bundle/Module14084";
let useStyles, __initData, PiPControls;
const MediaInfo = function MediaInfo(props) {
let state, setState, state2, setState2;
const { message, activeMediaPlayerSource: activeMediaPlayerSource3, isVoiceMessage: isVoiceMessage2, isControlVisible: isControlVisible2 } = props;
state = void 0;
setState = void 0;
state2 = void 0;
setState2 = void 0;
const styles = useStyles();
const token = useToken(Module712.colors.BACKGROUND_SURFACE_HIGH);
const stateFromStores3 = useStateFromStores([ChannelStore, UserStore, RelationshipStore], () => {
const channel = ChannelStore.getChannel(message?.channel_id);
return channel != null ? computeChannelName(channel, UserStore, RelationshipStore, true, true) : null;
}, [message]);
[state, setState] = useState(0);
[state2, setState2] = useState(0);
const memo = useMemo(() => state2 >= state, [state2, state]);
const r53 = message == null ? void 0 : message.getContentMessage();
if (message != null && r53 != null && activeMediaPlayerSource3 != null) {
const r74 = <Text variant="text-md/semibold" lineClamp={1} ellipsizeMode="clip" onLayout={(event) => setState2(event.nativeEvent.layout.width)}>
{isVoiceMessage2 ? message.author.username : r53.attachments.length > 0 ? activeMediaPlayerSource3.attachmentIndex != null ? getDisplayFilename(r53.attachments[activeMediaPlayerSource3.attachmentIndex]) : "" : ""}
</Text>;
return <View accessibilityElementsHidden={isControlVisible2} style={styles.infoContent} onLayout={(event) => setState(event.nativeEvent.layout.width)}>
<View style={styles.infoContainer}>
{memo ? <View style={{
flex: 1
}}>
<Marquee spacing={20} speed={0.2}>{r74}</Marquee>
<Module4611 start={{
x: 0,
y: 0
}} end={{
x: 1,
y: 0
}} locations={[0, 0.1, 0.2, 0.8, 0.9, 1]} colors={[token, token + "CC", token + "00", token + "00", token + "CC", token]} style={styles.infoContainerGradient} />
</View> : r74}
{stateFromStores3 != null && <Text variant="text-xs/medium" color="text-subtle" lineClamp={1}>{stateFromStores3}</Text>}
</View>
</View>;
}
return null;
};
PiPControls = function PiPControls(props) {
const { message: message2, handleClosePip, visible, isVoiceMessage: isVoiceMessage2 } = props;
const styles = useStyles();
const callback3 = useCallback(() => {
if (message2 != null && message2.channel_id != null && message2.id != null) {
MessageActionCreators.trackJump(message2.channel_id, message2.id, "Media PIP", {});
const channel = ChannelStore.getChannel(message2.channel_id);
safeTransitionTo_native(Routes.CHANNEL(channel == null ? void 0 : channel.getGuildId(), message2.channel_id, message2.id), {
navigationReplace: true,
openChannel: true
});
}
}, [message2]);
const c = function c() {
return {
opacity: _timing.withTiming(visible ? 1 : 0, {
duration: 200
})
};
};
c.__closure = {
withTiming: _timing.withTiming,
visible
};
c.__workletHash = 3641278982291;
c.__initData = __initData;
return <ReanimatedRexport.View style={[styles.pipControls, useAnimatedStyle(c)]}>
<BackgroundBlurFill blurAmount={0.05} />
<TouchableOpacity disabled={!visible} style={[styles.pipButton, styles.backButton]} onPress={callback3} accessible accessibilityRole="button" accessibilityLabel={isVoiceMessage2 ? intl.string(t.KTonHP) : intl.string(t["13/7kX"])}>
<ArrowLargeLeftIcon size="sm" />
</TouchableOpacity>
<TouchableOpacity disabled={!visible} style={[styles.pipButton, styles.dismissButton]} onPress={handleClosePip} accessible accessibilityRole="button" accessibilityLabel={isVoiceMessage2 ? intl.string(t["6rhrVG"]) : intl.string(t.WAI6xu)}>
<XLargeIcon size="sm" />
</TouchableOpacity>
</ReanimatedRexport.View>;
};
useStyles = createStyles({
container: {
justifyContent: "center",
alignItems: "center",
height: SquarePIPReferenceDimensions.height,
width: SquarePIPReferenceDimensions.width
},
pipControls: {
...StyleSheet.absoluteFillObject,
zIndex: 5
},
pipButton: {
position: "absolute",
top: 8,
padding: 8,
borderRadius: Module712.radii.round,
borderWidth: 1,
borderColor: Module712.colors.CONTROL_SECONDARY_BORDER_DEFAULT,
tintColor: Module712.colors.CONTROL_SECONDARY_TEXT_DEFAULT,
backgroundColor: Module712.colors.CONTROL_SECONDARY_BACKGROUND_DEFAULT,
...Module712.shadows.SHADOW_LOW_HOVER
},
dismissButton: {
right: 8
},
backButton: {
left: 8
},
infoContainer: {
justifyContent: "center",
alignItems: "center",
marginBottom: 8,
height: 34
},
infoContainerGradient: {
...StyleSheet.absoluteFillObject
},
infoContent: {
justifyContent: "center",
alignItems: "center",
alignSelf: "stretch",
marginHorizontal: 4
},
actionContainer: {
justifyContent: "center",
alignItems: "center",
width: 48,
height: 48,
zIndex: 100
},
playPauseButton: {
justifyContent: "center",
alignItems: "center",
width: 32,
height: 32,
zIndex: 100,
borderRadius: Module712.radii.round,
backgroundColor: Module712.colors.BACKGROUND_BRAND
},
progressBar: {
...StyleSheet.absoluteFillObject,
justifyContent: "center",
alignItems: "center"
}
});
__initData = {
code: "function MediaPlaybackPipTsx1(){const{withTiming,visible}=this.__closure;return{opacity:withTiming(visible?1:0,{duration:200})};}"
};
export default function MediaPlaybackPip() {
let isPlaying, activeMediaPlayerSource, closePip, stateFromStores, isVoiceMessage, state3, dismissPanel, callback, isControlVisible;
const styles = useStyles();
const ref2 = useRef(null);
const mediaPlayerManagerStore = useMediaPlayerManagerStore(useShallow((a0) => ({
isPlaying: a0.isPlaying,
progress: a0.progress,
activeMediaPlayerSource: a0.activeMediaPlayerSource,
mediaSourceMessage: a0.mediaSourceMessage,
closePip: a0.closePip
})));
isPlaying = mediaPlayerManagerStore.isPlaying;
const progress3 = mediaPlayerManagerStore.progress;
activeMediaPlayerSource = mediaPlayerManagerStore.activeMediaPlayerSource;
let mediaSourceMessage = mediaPlayerManagerStore.mediaSourceMessage;
closePip = mediaPlayerManagerStore.closePip;
const stateFromStores3 = useStateFromStores([MessageStore], () => {
const channelId = activeMediaPlayerSource?.channelId;
const messageId = activeMediaPlayerSource?.messageId;
return channelId == null ? null : messageId == null ? null : MessageStore.getMessage(channelId, messageId);
}, [activeMediaPlayerSource]);
if (stateFromStores3 != null) {
mediaSourceMessage = stateFromStores3;
}
stateFromStores = mediaSourceMessage;
const r5 = stateFromStores == null;
let r44 = void 0;
if (!r5) {
const contentMessage = stateFromStores.getContentMessage();
r44 = contentMessage == null ? void 0 : contentMessage.hasFlag(MessageFlags.IS_VOICE_MESSAGE);
}
let progress2, callback2;
isVoiceMessage = !!r44;
const activeMediaPlayerSource2 = activeMediaPlayerSource;
const progress = progress3;
const stateFromStores2 = stateFromStores;
const ref = useRef(null);
useEffect(() => {
if (ref.current == null && activeMediaPlayerSource2 != null && progress != null && stateFromStores2 != null) {
ref.current = {
initialProgress: progress,
activeMediaPlayerSource: activeMediaPlayerSource2,
message: stateFromStores2
};
}
if (ref.current != null && progress != null) {
ref.current.finalProgress = progress;
}
}, [progress, activeMediaPlayerSource, stateFromStores]);
useEffect(() => {
let c30944_0 = new Date();
return () => {
let current = ref.current;
if (current == null) {
current = {};
}
const { activeMediaPlayerSource: activeMediaPlayerSource3, message: message3, initialProgress, finalProgress } = current;
const r19 = activeMediaPlayerSource3?.attachmentIndex != null;
let r9 = null;
if (r19) {
const r20 = message3 == null;
let r21 = void 0;
if (!r20) {
const contentMessage = message3.getContentMessage();
r21 = contentMessage == null ? void 0 : contentMessage.attachments[activeMediaPlayerSource3.attachmentIndex];
}
r9 = r21;
}
const messageId = activeMediaPlayerSource3?.messageId;
const sender_user_id = message3 == null ? void 0 : message3.author.id;
const content_type = r9?.content_type;
const r33 = message3 == null;
let is_voice_message = void 0;
if (!r33) {
const contentMessage2 = message3.getContentMessage();
is_voice_message = contentMessage2 == null ? void 0 : contentMessage2.hasFlag(MessageFlags.IS_VOICE_MESSAGE);
}
const duration = finalProgress?.duration;
const total_duration_secs = duration != null ? duration / 1000 : void 0;
const time = initialProgress?.time;
const pip_playback_start_time_secs = time != null ? time / 1000 : void 0;
const time2 = finalProgress?.time;
AnalyticsUtils.track(AnalyticEvents.MEDIA_PIP_ENDED, {
message_id: messageId,
sender_user_id,
type: content_type,
is_voice_message,
total_duration_secs,
pip_playback_start_time_secs,
pip_playback_end_time_secs: time2 != null ? time2 / 1000 : void 0,
pip_opened_timestamp: c30944_0.toISOString(),
pip_closed_timestamp: new Date().toISOString()
});
};
}, []);
let [state, setState3] = useState(false);
state3 = state;
useEffect(() => {
let c28209_0;
if (state3) {
c28209_0 = setTimeout(() => {
if (isPlaying) {
setState3(false);
}
}, 3000);
}
return () => clearTimeout(c28209_0);
}, [state3, setState3, isPlaying]);
dismissPanel = useContext(MediaPlaybackPanelStateContext).dismissPanel;
callback = useCallback(() => {
dismissPanel();
closePip();
MediaPlayerManager.pauseCurrentPlayer();
}, [dismissPanel, closePip]);
progress2 = progress;
callback2 = callback;
useEffect(() => {
const isCompleted = progress2?.isCompleted;
let c30945_0;
if (isCompleted) {
c30945_0 = setTimeout(() => {
callback2();
}, 2000);
}
return () => {
clearTimeout(c30945_0);
};
}, [progress?.isCompleted, callback]);
if (!state3) {
state = progress?.isCompleted === true;
}
isControlVisible = state;
const callback3 = useCallback(() => {
if (isPlaying) {
MediaPlayerManager.pauseCurrentPlayer();
setState3(true);
} else {
MediaPlayerManager.playCurrentPlayer();
}
}, [isPlaying]);
const memo = useMemo(() => isPlaying ? <PauseIcon color={Module712.colors.WHITE} size="md" /> : <PlayIcon color={Module712.colors.WHITE} size="md" />, [isPlaying]);
const memo2 = useMemo(() => <MediaInfo message={stateFromStores} activeMediaPlayerSource={activeMediaPlayerSource} isVoiceMessage={isVoiceMessage} isControlVisible={isControlVisible} />, [stateFromStores, activeMediaPlayerSource, isVoiceMessage, isControlVisible]);
const memo3 = useMemo(() => <PiPControls message={stateFromStores} handleClosePip={callback} visible={isControlVisible} isVoiceMessage={isVoiceMessage} />, [stateFromStores, callback, isControlVisible, isVoiceMessage]);
let accessibilityLabel = isVoiceMessage ? intl.string(t.AlHqHT) : intl.string(t.RscU7I);
const r17 = isVoiceMessage ? intl.string(t["3XohGn"]) : intl.string(t.ZcgDJX);
const prefill = progress == null ? 0 : progress.time / progress.duration * 100;
const r26 = progress == null ? 0 : progress.duration - progress.time;
const props = {
style: styles.container,
activeOpacity: 1,
onPress: () => {
if (!(isPlaying ? !isPlaying : isControlVisible)) {
setState3(!state3);
}
},
accessible: false,
children: null
};
const children = [memo3, memo2, ,];
const r170 = [<AnimatedCircularProgress style={styles.progressBar} size={48} width={2} prefill={prefill} easing={Easing.out(Easing.linear)} duration={isPlaying ? r26 : 0} fill={isPlaying ? 100 : prefill} rotation={0} lineCap="round" ref={ref2} tintColor={useToken(Module712.colors.CONTROL_PRIMARY_BACKGROUND_DEFAULT)} backgroundColor={useToken(Module712.colors.BACKGROUND_MOD_MUTED)} />, ,];
if (isPlaying) {
accessibilityLabel = r17;
}
r170[1] = <TouchableOpacity style={styles.playPauseButton} onPress={callback3} accessibilityRole="button" accessibilityLabel={accessibilityLabel}>{memo}</TouchableOpacity>;
children[2] = <View style={styles.actionContainer}>{r170}</View>;
props.children = children;
return <TouchableOpacity {...props} />;
};