diff --git a/apps/web/package.json b/apps/web/package.json index 1ba2f80..c3137f8 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,7 +1,7 @@ { "name": "@typetype/web", "private": true, - "version": "1.3.1", + "version": "1.4.0", "type": "module", "scripts": { "dev": "vite", diff --git a/apps/web/src/components/admin-session-card.tsx b/apps/web/src/components/admin-session-card.tsx index ddce41c..55c151c 100644 --- a/apps/web/src/components/admin-session-card.tsx +++ b/apps/web/src/components/admin-session-card.tsx @@ -1,4 +1,4 @@ -import { toApiUrl } from "../lib/env"; +import { getAdminUserAvatarUrl } from "../lib/admin-user-avatar"; import { getOpenMojiUrl, pickOpenMojiCode } from "../lib/openmoji"; import type { AdminSession } from "../types/admin"; import type { AuthUser } from "../types/auth"; @@ -26,9 +26,7 @@ function initials(value: string): string { } function avatarUrl(user: AuthUser | undefined, session: AdminSession): string | null { - if (user?.avatarType === "emoji" && user.avatarCode) return getOpenMojiUrl(user.avatarCode); - if (user?.avatarUrl) return toApiUrl(user.avatarUrl); - if (user) return getOpenMojiUrl(pickOpenMojiCode(`${user.id}:${user.email}`)); + if (user) return getAdminUserAvatarUrl(user); if (session.userId) return getOpenMojiUrl(pickOpenMojiCode(session.userId)); return null; } diff --git a/apps/web/src/components/admin-user-avatar.tsx b/apps/web/src/components/admin-user-avatar.tsx index cd9787d..1564820 100644 --- a/apps/web/src/components/admin-user-avatar.tsx +++ b/apps/web/src/components/admin-user-avatar.tsx @@ -1,5 +1,4 @@ -import { toApiUrl } from "../lib/env"; -import { getOpenMojiUrl, pickOpenMojiCode } from "../lib/openmoji"; +import { getAdminUserAvatarUrl } from "../lib/admin-user-avatar"; import type { AuthUser } from "../types/auth"; type AdminUserAvatarProps = { @@ -8,13 +7,7 @@ type AdminUserAvatarProps = { }; export function AdminUserAvatar({ user, className }: AdminUserAvatarProps) { - const seed = `${user.id}:${user.email}`; - const avatarUrl = - user.avatarType === "emoji" && typeof user.avatarCode === "string" && user.avatarCode.length > 0 - ? getOpenMojiUrl(user.avatarCode) - : user.avatarUrl - ? toApiUrl(user.avatarUrl) - : getOpenMojiUrl(pickOpenMojiCode(seed)); + const avatarUrl = getAdminUserAvatarUrl(user); return (
); } + if (meta && isChannelIdentityBlocked({ url: sourceUrl, name: meta.name })) { + return ( + + ); + } return (
diff --git a/apps/web/src/components/continue-watching.tsx b/apps/web/src/components/continue-watching.tsx index 0d715b9..1408a19 100644 --- a/apps/web/src/components/continue-watching.tsx +++ b/apps/web/src/components/continue-watching.tsx @@ -1,3 +1,5 @@ +import { useMemo } from "react"; +import { useBlockedFilter } from "../hooks/use-blocked-filter"; import { useHistory } from "../hooks/use-history"; import { isVideoInProgress } from "../lib/watch-progress"; import { ContinueCard } from "./continue-card"; @@ -6,10 +8,15 @@ const MAX_ITEMS = 12; export function ContinueWatching() { const { items } = useHistory(); - const displayed = items - .filter((h) => isVideoInProgress(h.progress, h.duration)) - .sort((a, b) => b.watchedAt - a.watchedAt) - .slice(0, MAX_ITEMS); + const { filter } = useBlockedFilter(); + const displayed = useMemo( + () => + filter(items) + .filter((h) => isVideoInProgress(h.progress, h.duration)) + .sort((a, b) => b.watchedAt - a.watchedAt) + .slice(0, MAX_ITEMS), + [filter, items], + ); if (displayed.length === 0) return null; return ( diff --git a/apps/web/src/components/reset-token-modal.tsx b/apps/web/src/components/reset-token-modal.tsx index ca67284..fe4913a 100644 --- a/apps/web/src/components/reset-token-modal.tsx +++ b/apps/web/src/components/reset-token-modal.tsx @@ -1,4 +1,5 @@ -import { useEffect } from "react"; +import { useEffect, useState } from "react"; +import { copyText } from "../lib/copy-text"; type ResetTokenModalProps = { email: string; @@ -8,14 +9,7 @@ type ResetTokenModalProps = { }; export function ResetTokenModal({ email, token, onClose, onCopied }: ResetTokenModalProps) { - useEffect(() => { - navigator.clipboard - .writeText(token) - .then(() => { - onCopied(); - }) - .catch(() => {}); - }, [token, onCopied]); + const [copyState, setCopyState] = useState<"idle" | "copied" | "manual">("idle"); useEffect(() => { const handleEscape = (e: KeyboardEvent) => { @@ -25,10 +19,13 @@ export function ResetTokenModal({ email, token, onClose, onCopied }: ResetTokenM return () => window.removeEventListener("keydown", handleEscape); }, [onClose]); - const handleCopy = () => { - navigator.clipboard.writeText(token).then(() => { + const handleCopy = async () => { + if (await copyText(token)) { + setCopyState("copied"); onCopied(); - }); + return; + } + setCopyState("manual"); }; return ( @@ -52,18 +49,29 @@ export function ResetTokenModal({ email, token, onClose, onCopied }: ResetTokenM

{email}

-
-

{token}

-
+