BlueSky & more on desktop
lazurite.stormlightlabs.org/
tauri
rust
typescript
bluesky
appview
atproto
solid
1import { buildProfileRoute } from "$/lib/profile";
2import type { FlaggedFollow } from "$/lib/types";
3
4export type StatusCategoryKey =
5 | "deleted"
6 | "deactivated"
7 | "suspended"
8 | "blockedBy"
9 | "blocking"
10 | "hidden"
11 | "selfFollow";
12
13export type StatusCategoryState = { visible: boolean; selected: boolean };
14
15export const FOLLOW_STATUS_DELETED = Math.trunc(1);
16export const FOLLOW_STATUS_DEACTIVATED = 1 << 1;
17export const FOLLOW_STATUS_SUSPENDED = 1 << 2;
18export const FOLLOW_STATUS_BLOCKED_BY = 1 << 3;
19export const FOLLOW_STATUS_BLOCKING = 1 << 4;
20export const FOLLOW_STATUS_HIDDEN = 1 << 5;
21export const FOLLOW_STATUS_SELF_FOLLOW = 1 << 6;
22export const EXIT_ANIMATION_MS = 220;
23
24export const STATUS_CATEGORIES: Array<{ key: StatusCategoryKey; label: string; bit: number }> = [
25 { key: "deleted", label: "Deleted", bit: FOLLOW_STATUS_DELETED },
26 { key: "deactivated", label: "Deactivated", bit: FOLLOW_STATUS_DEACTIVATED },
27 { key: "suspended", label: "Suspended", bit: FOLLOW_STATUS_SUSPENDED },
28 { key: "blockedBy", label: "Blocked by", bit: FOLLOW_STATUS_BLOCKED_BY },
29 { key: "blocking", label: "Blocking", bit: FOLLOW_STATUS_BLOCKING },
30 { key: "hidden", label: "Hidden", bit: FOLLOW_STATUS_HIDDEN },
31 { key: "selfFollow", label: "Self-follow", bit: FOLLOW_STATUS_SELF_FOLLOW },
32];
33
34export function hasStatus(status: number, bit: number) {
35 return (status & bit) !== 0;
36}
37
38export function statusChipClass(status: number) {
39 if (hasStatus(status, FOLLOW_STATUS_DELETED)) {
40 return "bg-red-400/16 text-red-300";
41 }
42 if (hasStatus(status, FOLLOW_STATUS_DEACTIVATED)) {
43 return "bg-yellow-400/16 text-yellow-300";
44 }
45 if (hasStatus(status, FOLLOW_STATUS_SUSPENDED)) {
46 return "bg-orange-400/16 text-orange-300";
47 }
48 if (hasStatus(status, FOLLOW_STATUS_BLOCKED_BY) || hasStatus(status, FOLLOW_STATUS_BLOCKING)) {
49 return "bg-violet-400/16 text-violet-300";
50 }
51 if (hasStatus(status, FOLLOW_STATUS_HIDDEN)) {
52 return "bg-pink-400/16 text-pink-300";
53 }
54 return "bg-slate-400/16 text-slate-300";
55}
56
57export type FollowHygienePhase = "idle" | "scanning" | "ready" | "unfollowing" | "done";
58
59export function getAtExplorerHref(follow: FlaggedFollow) {
60 return `#/explorer?target=${encodeURIComponent(follow.followUri)}`;
61}
62
63export function displayHandle(follow: FlaggedFollow) {
64 if (follow.handle.startsWith("did:")) {
65 return follow.handle;
66 }
67
68 return `@${follow.handle.replace(/^@/, "")}`;
69}
70
71export function getProfileHref(follow: FlaggedFollow) {
72 const actor = follow.handle.startsWith("did:") ? follow.did : follow.handle.replace(/^@/, "");
73 return `#${buildProfileRoute(actor)}`;
74}