typing-avatars: add AvatarStack component and typing-indicator patch
This commit is contained in:
parent
99ce783b96
commit
7b182ff950
3 changed files with 90 additions and 1 deletions
35
plugins/typing-avatars/src/components/AvatarStack.tsx
Normal file
35
plugins/typing-avatars/src/components/AvatarStack.tsx
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import React from "react";
|
||||
import { View, Image, StyleSheet } from "react-native";
|
||||
import { getTypingAvatarURL } from "../lib/avatarUrl";
|
||||
|
||||
const SIZE = 20;
|
||||
const OVERLAP = 8;
|
||||
|
||||
// No text label and no tap handler by design - purely decorative avatars replacing the
|
||||
// "X is typing..." text entirely. If a lot of people are typing, this wraps to a second
|
||||
// overlapping row rather than clipping or capping with a "+N" badge.
|
||||
export default function AvatarStack({ typingUserIds, guildId }: { typingUserIds: string[]; guildId: string | undefined }) {
|
||||
if (!typingUserIds?.length) return null;
|
||||
|
||||
return (
|
||||
<View style={st.row}>
|
||||
{typingUserIds.map((userId, i) => {
|
||||
const uri = getTypingAvatarURL(guildId, userId, SIZE);
|
||||
if (!uri) return null;
|
||||
return (
|
||||
<Image
|
||||
key={userId}
|
||||
source={{ uri }}
|
||||
style={[st.avatar, i > 0 ? st.overlap : null]}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const st = StyleSheet.create({
|
||||
row: { flexDirection: "row", flexWrap: "wrap", alignItems: "center" },
|
||||
avatar: { width: SIZE, height: SIZE, borderRadius: SIZE / 2 },
|
||||
overlap: { marginLeft: -OVERLAP },
|
||||
});
|
||||
|
|
@ -1,11 +1,14 @@
|
|||
import { logger } from "@vendetta";
|
||||
import { applyPatches } from "@shared/lib/patcher";
|
||||
import patchTypingIndicator from "./patches/typingIndicator";
|
||||
|
||||
let unpatchAll: () => void = () => {};
|
||||
|
||||
export default {
|
||||
onLoad: () => {
|
||||
unpatchAll = applyPatches("TypingAvatars", logger, {});
|
||||
unpatchAll = applyPatches("TypingAvatars", logger, {
|
||||
"typing indicator avatars": patchTypingIndicator
|
||||
});
|
||||
},
|
||||
onUnload: () => unpatchAll()
|
||||
};
|
||||
|
|
|
|||
51
plugins/typing-avatars/src/patches/typingIndicator.tsx
Normal file
51
plugins/typing-avatars/src/patches/typingIndicator.tsx
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
import { React } from "@vendetta/metro/common";
|
||||
import { findInReactTree } from "@vendetta/utils";
|
||||
import { registerTypeDetector, registerIntercept, patchCreateElement } from "@shared/lib/createElementIntercept";
|
||||
import AvatarStack from "../components/AvatarStack";
|
||||
|
||||
// Mirrors role-color-everywhere/src/patches/typingWrapper.ts for the detection/wrapping part
|
||||
// (already proven live in this repo), but replaces the label's content with AvatarStack instead
|
||||
// of recoloring the existing per-user text nodes. This only needs props.typingUserIds directly,
|
||||
// so unlike the color-matching version it isn't affected by the "several people are typing..."
|
||||
// collapsed-string case.
|
||||
export default function patchTypingIndicator(): () => void {
|
||||
const cleanups: (() => void)[] = [];
|
||||
patchCreateElement(cleanups);
|
||||
|
||||
registerTypeDetector(
|
||||
"typing-avatars-indicator",
|
||||
(type) => typeof type === "function" ? type.name === "TypingIndicatorInner" : type?.type?.name === "TypingIndicatorInner",
|
||||
(TypingIndicatorInner: any) => {
|
||||
const inner = typeof TypingIndicatorInner === "function" ? TypingIndicatorInner : TypingIndicatorInner.type;
|
||||
|
||||
const PatchedTypingIndicatorInner = (props: any) => {
|
||||
const ret = inner(props);
|
||||
|
||||
try {
|
||||
const label = findInReactTree(
|
||||
ret,
|
||||
(n: any) =>
|
||||
Array.isArray(n?.props?.children) &&
|
||||
n.props.children.some((c: any) => typeof c === "string" && c.includes("typing..."))
|
||||
);
|
||||
if (!label?.props) return ret;
|
||||
|
||||
const typingUserIds: string[] = props?.typingUserIds ?? [];
|
||||
if (!typingUserIds.length) return ret;
|
||||
|
||||
label.props.children = (
|
||||
<AvatarStack typingUserIds={typingUserIds} guildId={props?.channel?.guild_id} />
|
||||
);
|
||||
} catch {
|
||||
// Leave the default "X is typing..." text alone.
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
registerIntercept(TypingIndicatorInner, PatchedTypingIndicatorInner);
|
||||
}
|
||||
);
|
||||
|
||||
return () => cleanups.forEach((fn) => fn());
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue