241 lines
6.6 KiB
TypeScript
241 lines
6.6 KiB
TypeScript
import * as Misskey from "calckey-js";
|
|
import { packed, types } from "magnetar-common";
|
|
|
|
// https://stackoverflow.com/a/50375286 Evil magic
|
|
type Dist<U> = U extends any ? (k: U) => void : never;
|
|
|
|
type UnionToIntersection<U> = Dist<U> extends (k: infer I) => void ? I : never;
|
|
// End of evil magic
|
|
|
|
type UnionMerged<A> = { [AK in keyof A]: AK extends keyof A ? A[AK] : never };
|
|
type UnionIntersectionMerge<A> = Omit<
|
|
UnionToIntersection<A>,
|
|
keyof UnionMerged<A>
|
|
> &
|
|
UnionMerged<A>;
|
|
|
|
export function magTransProperty<
|
|
A extends Record<string, any>,
|
|
AA extends keyof UnionToIntersection<A> & string,
|
|
BB extends keyof UnionToIntersection<A> & string
|
|
>(
|
|
x: A,
|
|
keyA: AA,
|
|
keyB: BB
|
|
): UnionIntersectionMerge<A>[AA] | UnionIntersectionMerge<A>[BB] {
|
|
const a = x[keyA];
|
|
|
|
if (typeof a !== "undefined") {
|
|
return a;
|
|
}
|
|
|
|
return x[keyB];
|
|
}
|
|
|
|
export function magTransMap<
|
|
A extends Record<string, any>,
|
|
AA extends keyof UnionIntersectionMerge<A> & string,
|
|
BB extends keyof UnionIntersectionMerge<A> & string,
|
|
X extends any
|
|
>(
|
|
x: A,
|
|
keyA: AA,
|
|
keyB: BB,
|
|
mapLeft: (a: UnionIntersectionMerge<A>[AA]) => X = (a) => a as X,
|
|
mapRight: (b: UnionIntersectionMerge<A>[BB]) => X = (a) => a as X
|
|
): X {
|
|
const a = x[keyA];
|
|
|
|
if (typeof a !== "undefined") {
|
|
return mapLeft(a as UnionIntersectionMerge<A>[AA]);
|
|
}
|
|
|
|
const b = x[keyB];
|
|
return typeof b === "undefined"
|
|
? b
|
|
: mapRight(b as UnionIntersectionMerge<A>[BB]);
|
|
}
|
|
|
|
type UserUnion = packed.PackUserBase | Misskey.entities.User;
|
|
|
|
export function magTransUsername(x: UserUnion): string {
|
|
return (
|
|
(x as UnionToIntersection<UserUnion>)["display_name"] ||
|
|
(x as UnionToIntersection<UserUnion>)["name"] ||
|
|
(x as UnionToIntersection<UserUnion>)["username"]
|
|
);
|
|
}
|
|
|
|
export function magReactionCount(
|
|
note: packed.PackNoteMaybeFull | Misskey.entities.Note
|
|
): number {
|
|
if (Array.isArray(note.reactions)) {
|
|
return Number(
|
|
note.reactions
|
|
.map(([, cnt]) => cnt)
|
|
.reduce((partialSum, val) => partialSum + val, 0)
|
|
);
|
|
} else {
|
|
return Object.values(note).reduce((accum, val) => accum + val, 0);
|
|
}
|
|
}
|
|
|
|
export function magHasReacted(
|
|
note: packed.PackNoteMaybeFull | Misskey.entities.Note
|
|
): boolean {
|
|
if (Array.isArray(note.reactions)) {
|
|
return note.reactions.some(([, , reacted]) => reacted === true);
|
|
} else {
|
|
return (
|
|
typeof (note as Misskey.entities.Note).myReaction !== "undefined" &&
|
|
(note as Misskey.entities.Note).myReaction !== null
|
|
);
|
|
}
|
|
}
|
|
|
|
export function magReactionSelf(
|
|
note: packed.PackNoteMaybeFull | Misskey.entities.Note
|
|
): string | null {
|
|
if (Array.isArray(note.reactions)) {
|
|
const found = note.reactions.find(
|
|
([, , reacted]) => reacted === true
|
|
)?.[0];
|
|
return typeof found !== "undefined" ? magReactionToLegacy(found) : null;
|
|
} else if ((note as Misskey.entities.Note).myReaction !== "undefined") {
|
|
return (note as Misskey.entities.Note).myReaction ?? null;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
export function noteIsMag(
|
|
note: packed.PackNoteMaybeFull | Misskey.entities.Note
|
|
): note is packed.PackNoteMaybeFull {
|
|
return "created_at" in note;
|
|
}
|
|
|
|
export function magIsRenote(
|
|
note: packed.PackNoteMaybeFull | Misskey.entities.Note
|
|
): boolean {
|
|
return (
|
|
magTransProperty(note, "renoted_note", "renote") != null &&
|
|
note.text == null &&
|
|
magTransProperty(note, "file_ids", "fileIds").length === 0 &&
|
|
note.poll == null
|
|
);
|
|
}
|
|
|
|
export function magEffectiveNote(
|
|
note: packed.PackNoteMaybeFull | Misskey.entities.Note
|
|
): packed.PackNoteMaybeFull | Misskey.entities.Note {
|
|
return magIsRenote(note)
|
|
? (magTransProperty(note, "renoted_note", "renote") as
|
|
| packed.PackNoteMaybeFull
|
|
| Misskey.entities.Note)
|
|
: note;
|
|
}
|
|
|
|
export function magLegacyVisibility(
|
|
vis: types.NoteVisibility | Misskey.entities.Note["visibility"]
|
|
): Misskey.entities.Note["visibility"];
|
|
|
|
export function magLegacyVisibility(vis: undefined): undefined;
|
|
|
|
export function magLegacyVisibility(
|
|
vis: types.NoteVisibility | Misskey.entities.Note["visibility"] | undefined
|
|
): Misskey.entities.Note["visibility"] | undefined {
|
|
if (typeof vis === "undefined") return vis;
|
|
|
|
switch (vis) {
|
|
case "Public":
|
|
return "public";
|
|
case "Home":
|
|
return "home";
|
|
case "Followers":
|
|
return "followers";
|
|
case "Direct":
|
|
return "specified";
|
|
|
|
case "public":
|
|
case "home":
|
|
case "followers":
|
|
case "specified":
|
|
return vis;
|
|
}
|
|
}
|
|
|
|
export function magConvertReaction(
|
|
reaction: string,
|
|
urlHint?: string | null
|
|
): types.Reaction {
|
|
if (reaction.endsWith("@.:")) {
|
|
reaction = reaction.replaceAll(/@\.:$/, ":");
|
|
}
|
|
|
|
if (reaction.match(/^:.+:$/)) {
|
|
const [name, host] = reaction.split("@");
|
|
return {
|
|
name,
|
|
host: host || null,
|
|
url: urlHint!,
|
|
};
|
|
} else {
|
|
return reaction;
|
|
}
|
|
}
|
|
|
|
export function magReactionToLegacy(reaction: types.Reaction | string): string {
|
|
if (typeof reaction === "string") {
|
|
return reaction;
|
|
}
|
|
|
|
if ("name" in reaction) {
|
|
if (reaction.host) {
|
|
return `:${reaction.name}@${reaction.host}:`;
|
|
} else {
|
|
return `:${reaction.name}@.:`;
|
|
}
|
|
}
|
|
|
|
if ("raw" in reaction) {
|
|
return reaction.raw;
|
|
}
|
|
|
|
return reaction;
|
|
}
|
|
|
|
export function magReactionPairToLegacy(
|
|
reaction: types.ReactionPair | [string, number]
|
|
): [string, number] {
|
|
const legacy = magReactionToLegacy(reaction[0]);
|
|
return [legacy, reaction[1]];
|
|
}
|
|
|
|
export function magReactionIndex(
|
|
reactions: types.ReactionPair[],
|
|
reactionType: types.Reaction
|
|
) {
|
|
return reactions.findIndex(([r, ,]) => {
|
|
if (typeof r !== typeof reactionType) return false;
|
|
|
|
if (typeof r === "string") {
|
|
return r === reactionType;
|
|
} else if (
|
|
"name" in r &&
|
|
"name" in (reactionType as { name: string; host: string | null })
|
|
) {
|
|
const { name, host } = reactionType as {
|
|
name: string;
|
|
host: string | null;
|
|
};
|
|
const { name: rName, host: rHost } = r;
|
|
|
|
return name === rName && (host ?? null) === (rHost ?? null);
|
|
} else if ("raw" in r && "raw" in (reactionType as { raw: string })) {
|
|
return r.raw === (reactionType as { raw: string }).raw;
|
|
}
|
|
|
|
return false;
|
|
});
|
|
}
|