magnetar/fe_calckey/frontend/client/src/scripts-mag/mag-util.ts

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;
});
}