@epic-web/workshop-presence 5.3.7 → 5.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -4,17 +4,53 @@ export declare const presenceCache: {
|
|
|
4
4
|
name: string;
|
|
5
5
|
set: (key: string, value: import("@epic-web/cachified").CacheEntry<{
|
|
6
6
|
id: string;
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
hasAccess?: boolean | null | undefined;
|
|
8
|
+
avatarUrl?: string | null | undefined;
|
|
9
|
+
imageUrlSmall?: string | null | undefined;
|
|
10
|
+
imageUrlLarge?: string | null | undefined;
|
|
11
|
+
name?: string | null | undefined;
|
|
12
|
+
location?: {
|
|
13
|
+
workshopTitle?: string | null | undefined;
|
|
14
|
+
origin?: string | null | undefined;
|
|
15
|
+
exercise?: {
|
|
16
|
+
type?: "problem" | "solution" | null | undefined;
|
|
17
|
+
exerciseNumber?: number | null | undefined;
|
|
18
|
+
stepNumber?: number | null | undefined;
|
|
19
|
+
} | null | undefined;
|
|
20
|
+
} | null | undefined;
|
|
9
21
|
}[]>) => import("@epic-web/cachified").CacheEntry<{
|
|
10
22
|
id: string;
|
|
11
|
-
|
|
12
|
-
|
|
23
|
+
hasAccess?: boolean | null | undefined;
|
|
24
|
+
avatarUrl?: string | null | undefined;
|
|
25
|
+
imageUrlSmall?: string | null | undefined;
|
|
26
|
+
imageUrlLarge?: string | null | undefined;
|
|
27
|
+
name?: string | null | undefined;
|
|
28
|
+
location?: {
|
|
29
|
+
workshopTitle?: string | null | undefined;
|
|
30
|
+
origin?: string | null | undefined;
|
|
31
|
+
exercise?: {
|
|
32
|
+
type?: "problem" | "solution" | null | undefined;
|
|
33
|
+
exerciseNumber?: number | null | undefined;
|
|
34
|
+
stepNumber?: number | null | undefined;
|
|
35
|
+
} | null | undefined;
|
|
36
|
+
} | null | undefined;
|
|
13
37
|
}[]>;
|
|
14
38
|
get: (key: string) => import("@epic-web/cachified").CacheEntry<{
|
|
15
39
|
id: string;
|
|
16
|
-
|
|
17
|
-
|
|
40
|
+
hasAccess?: boolean | null | undefined;
|
|
41
|
+
avatarUrl?: string | null | undefined;
|
|
42
|
+
imageUrlSmall?: string | null | undefined;
|
|
43
|
+
imageUrlLarge?: string | null | undefined;
|
|
44
|
+
name?: string | null | undefined;
|
|
45
|
+
location?: {
|
|
46
|
+
workshopTitle?: string | null | undefined;
|
|
47
|
+
origin?: string | null | undefined;
|
|
48
|
+
exercise?: {
|
|
49
|
+
type?: "problem" | "solution" | null | undefined;
|
|
50
|
+
exerciseNumber?: number | null | undefined;
|
|
51
|
+
stepNumber?: number | null | undefined;
|
|
52
|
+
} | null | undefined;
|
|
53
|
+
} | null | undefined;
|
|
18
54
|
}[]> | undefined;
|
|
19
55
|
delete: (key: string) => boolean;
|
|
20
56
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presence.server.d.ts","sourceRoot":"","sources":["../../src/presence.server.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,wCAAwC,CAAA;AAErE,OAAO,EAIN,KAAK,IAAI,EACT,MAAM,eAAe,CAAA;AAEtB,eAAO,MAAM,aAAa
|
|
1
|
+
{"version":3,"file":"presence.server.d.ts","sourceRoot":"","sources":["../../src/presence.server.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,wCAAwC,CAAA;AAErE,OAAO,EAIN,KAAK,IAAI,EACT,MAAM,eAAe,CAAA;AAEtB,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAC6C,CAAA;AAEvE,wBAAsB,eAAe,CACpC,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,EAClB,EAAE,OAAO,EAAE,OAAO,EAAE,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO;;;;;;;;;;;;;;;;KA2CnE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presence.server.js","sourceRoot":"","sources":["../../src/presence.server.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,SAAS,EACT,kBAAkB,GAClB,MAAM,uCAAuC,CAAA;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAA;AAEnE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EACN,cAAc,EACd,UAAU,EACV,eAAe,GAEf,MAAM,eAAe,CAAA;AAEtB,MAAM,CAAC,MAAM,aAAa,
|
|
1
|
+
{"version":3,"file":"presence.server.js","sourceRoot":"","sources":["../../src/presence.server.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tcachified,\n\tmakeSingletonCache,\n} from '@epic-web/workshop-utils/cache.server'\nimport { getPreferences } from '@epic-web/workshop-utils/db.server'\nimport { type Timings } from '@epic-web/workshop-utils/timing.server'\nimport { z } from 'zod'\nimport {\n\tPresenceSchema,\n\tUserSchema,\n\tpartykitBaseUrl,\n\ttype User,\n} from './presence.js'\n\nexport const presenceCache =\n\tmakeSingletonCache<Array<z.infer<typeof UserSchema>>>('PresenceCache')\n\nexport async function getPresentUsers(\n\tuser?: User | null,\n\t{ timings, request }: { timings?: Timings; request?: Request } = {},\n) {\n\treturn cachified({\n\t\tkey: 'presence',\n\t\tcache: presenceCache,\n\t\ttimings,\n\t\trequest,\n\t\tttl: 1000 * 60 * 5,\n\t\tswr: 1000 * 60 * 60 * 24,\n\t\tcheckValue: z.array(UserSchema),\n\t\tasync getFreshValue(context) {\n\t\t\ttry {\n\t\t\t\tconst response = await Promise.race([\n\t\t\t\t\tfetch(`${partykitBaseUrl}/presence`),\n\t\t\t\t\tnew Promise<Response>((resolve) =>\n\t\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\t\tresolve(new Response('Timeout', { status: 500 }))\n\t\t\t\t\t\t}, 500),\n\t\t\t\t\t),\n\t\t\t\t] as const)\n\t\t\t\tif (response.statusText === 'Timeout') {\n\t\t\t\t\tthrow new Error(`Timeout fetching partykit presence`)\n\t\t\t\t}\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Unexpected response from partykit: ${response.status} ${response.statusText}`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tconst presence = PresenceSchema.parse(await response.json())\n\t\t\t\tconst preferences = await getPreferences()\n\t\t\t\tconst users = presence.users\n\t\t\t\tif (preferences?.presence.optOut ?? !user) {\n\t\t\t\t\treturn uniqueUsers(users.filter((u) => u.id !== user?.id))\n\t\t\t\t} else {\n\t\t\t\t\treturn uniqueUsers([...users, user])\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// console.error(err)\n\t\t\t\tcontext.metadata.ttl = 300\n\t\t\t\treturn []\n\t\t\t}\n\t\t},\n\t})\n}\n\n// A user maybe on the same page in multiple tabs\n// so let's make sure we only show them once\nfunction uniqueUsers(users: Array<User>) {\n\tconst seen = new Set()\n\treturn users.filter(Boolean).filter((user) => {\n\t\tif (seen.has(user.id)) {\n\t\t\treturn false\n\t\t}\n\t\tseen.add(user.id)\n\t\treturn true\n\t})\n}\n"]}
|
package/dist/esm/server.js
CHANGED
|
@@ -155,9 +155,9 @@ function sortUsers(users) {
|
|
|
155
155
|
}
|
|
156
156
|
function getScore(user) {
|
|
157
157
|
let score = 0;
|
|
158
|
-
if (user.
|
|
158
|
+
if (user.imageUrlSmall)
|
|
159
159
|
score += 1;
|
|
160
|
-
if (user.
|
|
160
|
+
if (user.imageUrlSmall?.includes('discordapp'))
|
|
161
161
|
score += 0.5;
|
|
162
162
|
if (user.name)
|
|
163
163
|
score += 1;
|
package/dist/esm/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/server.ts"],"names":[],"mappings":"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","sourcesContent":["import type * as Party from 'partykit/server'\nimport { z } from 'zod'\nimport { UserSchema } from './presence.js'\n\ntype User = z.infer<typeof UserSchema>\nconst ConnectionStateSchema = z\n\t.object({\n\t\tuser: UserSchema.nullable().optional(),\n\t})\n\t.nullable()\n\ntype ConnectionState = z.infer<typeof ConnectionStateSchema>\n\nconst MessageSchema = z\n\t.object({\n\t\ttype: z.literal('remove-user'),\n\t\tpayload: z.object({ id: z.string() }),\n\t})\n\t.or(z.object({ type: z.literal('add-user'), payload: UserSchema }))\n\t.or(\n\t\tz.object({\n\t\t\ttype: z.literal('add-anonymous-user'),\n\t\t\tpayload: z.object({ id: z.string() }),\n\t\t}),\n\t)\n\t.or(\n\t\tz.object({\n\t\t\ttype: z.literal('presence'),\n\t\t\tpayload: z.object({ users: z.array(UserSchema) }),\n\t\t}),\n\t)\ntype Message = z.infer<typeof MessageSchema>\n\nexport default (class Server implements Party.Server {\n\toptions: Party.ServerOptions = {\n\t\thibernate: true,\n\t}\n\n\treadonly party: Party.Room\n\n\tconstructor(party: Party.Room) {\n\t\tthis.party = party\n\t}\n\n\tonClose() {\n\t\tthis.updateUsers()\n\t}\n\n\tonError() {\n\t\tthis.updateUsers()\n\t}\n\n\tupdateUsers() {\n\t\tconst presenceMessage = JSON.stringify(this.getPresenceMessage())\n\t\tfor (const connection of this.party.getConnections()) {\n\t\t\tconnection.send(presenceMessage)\n\t\t}\n\t}\n\n\tgetPresenceMessage() {\n\t\treturn {\n\t\t\ttype: 'presence',\n\t\t\tpayload: { users: this.getUsers() },\n\t\t} satisfies Message\n\t}\n\n\tgetUsers() {\n\t\tconst users = new Map<string, z.infer<typeof UserSchema>>()\n\n\t\tfor (const connection of this.party.getConnections()) {\n\t\t\tconst state = getConnectionState(connection)\n\t\t\tif (state?.user) {\n\t\t\t\tusers.set(state.user.id, state.user)\n\t\t\t}\n\t\t}\n\n\t\treturn sortUsers(Array.from(users.values()))\n\t}\n\n\tonMessage(message: string, sender: Party.Connection) {\n\t\tconst result = MessageSchema.safeParse(JSON.parse(message))\n\t\tif (!result.success) return\n\n\t\tif (result.data.type === 'add-user') {\n\t\t\tshallowMergeConnectionState(sender, { user: result.data.payload })\n\t\t\tthis.updateUsers()\n\t\t} else if (result.data.type === 'remove-user') {\n\t\t\tsetConnectionState(sender, null)\n\t\t\tthis.updateUsers()\n\t\t} else if (result.data.type === 'add-anonymous-user') {\n\t\t\tsetConnectionState(sender, { user: result.data.payload })\n\t\t}\n\t}\n\n\tonRequest(req: Party.Request): Response | Promise<Response> {\n\t\tconst url = new URL(req.url)\n\t\tif (url.pathname.endsWith('/presence')) {\n\t\t\treturn Response.json(this.getPresenceMessage().payload)\n\t\t}\n\t\tif (url.pathname.endsWith('/show')) {\n\t\t\tconst users = this.getUsers()\n\t\t\tconst workshopUsers = organizeUsersByWorkshop(users)\n\t\t\treturn new Response(\n\t\t\t\t`\n\t\t\t\t<!DOCTYPE html>\n\t\t\t\t<html lang=\"en\">\n\t\t\t\t<head>\n\t\t\t\t\t<meta charset=\"UTF-8\">\n\t\t\t\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n\t\t\t\t\t<meta http-equiv=\"refresh\" content=\"5\">\n\t\t\t\t\t<title>Epic Web Presence</title>\n\t\t\t\t\t<style>\n\t\t\t\t\t\tbody { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }\n\t\t\t\t\t\th1, h2 { color: #333; }\n\t\t\t\t\t\tul { padding: 0; }\n\t\t\t\t\t\tli { list-style: none; margin-bottom: 10px; }\n\t\t\t\t\t\t.user-avatar { width: 64px; height: 64px; border-radius: 50%; vertical-align: middle; margin-right: 10px; }\n\t\t\t\t\t</style>\n\t\t\t\t</head>\n\t\t\t\t<body>\n\t\t\t\t\t<h1>Epic Web Presence</h1>\n\t\t\t\t\t<p>Total Users: ${users.length}</p>\n\t\t\t\t\t${Object.entries(workshopUsers)\n\t\t\t\t\t\t.map(\n\t\t\t\t\t\t\t([workshop, workshopUsers]) => `\n\t\t\t\t\t\t\t<h2>${workshop} (${workshopUsers.length})</h2>\n\t\t\t\t\t\t\t<ul>\n\t\t\t\t\t\t\t\t${workshopUsers.map(generateUserListItem).join('')}\n\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t`,\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.join('')}\n\t\t\t\t</body>\n\t\t\t\t</html>\n\t\t\t\t`,\n\t\t\t\t{\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t'Content-Type': 'text/html',\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t)\n\t\t}\n\t\treturn new Response('not found', { status: 404 })\n\t}\n} satisfies Party.Worker)\n\nfunction shallowMergeConnectionState(\n\tconnection: Party.Connection,\n\tstate: ConnectionState,\n) {\n\tsetConnectionState(connection, (prev) => ({ ...prev, ...state }))\n}\n\nfunction setConnectionState(\n\tconnection: Party.Connection,\n\tstate:\n\t\t| ConnectionState\n\t\t| ((prev: ConnectionState | null) => ConnectionState | null),\n) {\n\tif (typeof state !== 'function') {\n\t\treturn connection.setState(state)\n\t}\n\tconnection.setState((prev: unknown) => {\n\t\tconst prevParseResult = ConnectionStateSchema.safeParse(prev)\n\t\tif (prevParseResult.success) {\n\t\t\treturn state(prevParseResult.data)\n\t\t} else {\n\t\t\treturn state(null)\n\t\t}\n\t})\n}\n\nfunction getConnectionState(connection: Party.Connection) {\n\tconst result = ConnectionStateSchema.safeParse(connection.state)\n\tif (result.success) {\n\t\treturn result.data\n\t} else {\n\t\tsetConnectionState(connection, null)\n\t\treturn null\n\t}\n}\n\nfunction sortUsers(users: Array<User>) {\n\treturn [...users].sort((a, b) => {\n\t\tconst aScore = getScore(a)\n\t\tconst bScore = getScore(b)\n\t\tif (aScore === bScore) return 0\n\t\treturn aScore > bScore ? -1 : 1\n\t})\n}\n\nfunction getScore(user: User) {\n\tlet score = 0\n\tif (user.avatarUrl) score += 1\n\tif (user.avatarUrl?.includes('discordapp')) score += 0.5\n\tif (user.name) score += 1\n\treturn score\n}\n\nfunction organizeUsersByWorkshop(users: Array<User>) {\n\tconst workshopUsers: Record<string, Array<User>> = {}\n\n\tfor (const user of users) {\n\t\tconst workshop = user.location?.workshopTitle ?? 'Unknown Workshop'\n\t\tif (!workshopUsers[workshop]) {\n\t\t\tworkshopUsers[workshop] = []\n\t\t}\n\t\tworkshopUsers[workshop]?.push(user)\n\t}\n\n\t// Sort users within each workshop by exercise and step number\n\tfor (const workshop in workshopUsers) {\n\t\tworkshopUsers[workshop]?.sort((a, b) => {\n\t\t\tconst aExercise = a.location?.exercise?.exerciseNumber ?? 0\n\t\t\tconst bExercise = b.location?.exercise?.exerciseNumber ?? 0\n\t\t\tif (aExercise !== bExercise) return aExercise - bExercise\n\n\t\t\tconst aStep = a.location?.exercise?.stepNumber ?? 0\n\t\t\tconst bStep = b.location?.exercise?.stepNumber ?? 0\n\t\t\treturn aStep - bStep\n\t\t})\n\t}\n\n\treturn workshopUsers\n}\n\nfunction generateUserListItem(user: User) {\n\tconst imageUrl = user.imageUrlLarge ?? user.avatarUrl ?? '/avatar.png'\n\tconst name = user.name ?? 'Anonymous'\n\tconst location = user.location?.exercise\n\t\t? [\n\t\t\t\t`Exercise ${user.location.exercise.exerciseNumber}`,\n\t\t\t\tuser.location.exercise.stepNumber &&\n\t\t\t\t\t`Step ${user.location.exercise.stepNumber}`,\n\t\t\t]\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.join(', ')\n\t\t: user.location?.origin\n\t\t\t? user.location.origin\n\t\t\t: 'Unknown location'\n\n\tconst accessLabel =\n\t\ttypeof user.hasAccess === 'boolean' ? (user.hasAccess ? '🔑' : '🆓') : ''\n\n\treturn `\n\t\t<li>\n\t\t\t<img class=\"user-avatar\" src=\"${imageUrl}\" alt=\"${name}\" />\n\t\t\t<strong>${name}</strong> - ${location} ${accessLabel}\n\t\t</li>\n\t`\n}\n"]}
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/server.ts"],"names":[],"mappings":"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","sourcesContent":["import type * as Party from 'partykit/server'\nimport { z } from 'zod'\nimport { UserSchema } from './presence.js'\n\ntype User = z.infer<typeof UserSchema>\nconst ConnectionStateSchema = z\n\t.object({\n\t\tuser: UserSchema.nullable().optional(),\n\t})\n\t.nullable()\n\ntype ConnectionState = z.infer<typeof ConnectionStateSchema>\n\nconst MessageSchema = z\n\t.object({\n\t\ttype: z.literal('remove-user'),\n\t\tpayload: z.object({ id: z.string() }),\n\t})\n\t.or(z.object({ type: z.literal('add-user'), payload: UserSchema }))\n\t.or(\n\t\tz.object({\n\t\t\ttype: z.literal('add-anonymous-user'),\n\t\t\tpayload: z.object({ id: z.string() }),\n\t\t}),\n\t)\n\t.or(\n\t\tz.object({\n\t\t\ttype: z.literal('presence'),\n\t\t\tpayload: z.object({ users: z.array(UserSchema) }),\n\t\t}),\n\t)\ntype Message = z.infer<typeof MessageSchema>\n\nexport default (class Server implements Party.Server {\n\toptions: Party.ServerOptions = {\n\t\thibernate: true,\n\t}\n\n\treadonly party: Party.Room\n\n\tconstructor(party: Party.Room) {\n\t\tthis.party = party\n\t}\n\n\tonClose() {\n\t\tthis.updateUsers()\n\t}\n\n\tonError() {\n\t\tthis.updateUsers()\n\t}\n\n\tupdateUsers() {\n\t\tconst presenceMessage = JSON.stringify(this.getPresenceMessage())\n\t\tfor (const connection of this.party.getConnections()) {\n\t\t\tconnection.send(presenceMessage)\n\t\t}\n\t}\n\n\tgetPresenceMessage() {\n\t\treturn {\n\t\t\ttype: 'presence',\n\t\t\tpayload: { users: this.getUsers() },\n\t\t} satisfies Message\n\t}\n\n\tgetUsers() {\n\t\tconst users = new Map<string, z.infer<typeof UserSchema>>()\n\n\t\tfor (const connection of this.party.getConnections()) {\n\t\t\tconst state = getConnectionState(connection)\n\t\t\tif (state?.user) {\n\t\t\t\tusers.set(state.user.id, state.user)\n\t\t\t}\n\t\t}\n\n\t\treturn sortUsers(Array.from(users.values()))\n\t}\n\n\tonMessage(message: string, sender: Party.Connection) {\n\t\tconst result = MessageSchema.safeParse(JSON.parse(message))\n\t\tif (!result.success) return\n\n\t\tif (result.data.type === 'add-user') {\n\t\t\tshallowMergeConnectionState(sender, { user: result.data.payload })\n\t\t\tthis.updateUsers()\n\t\t} else if (result.data.type === 'remove-user') {\n\t\t\tsetConnectionState(sender, null)\n\t\t\tthis.updateUsers()\n\t\t} else if (result.data.type === 'add-anonymous-user') {\n\t\t\tsetConnectionState(sender, { user: result.data.payload })\n\t\t}\n\t}\n\n\tonRequest(req: Party.Request): Response | Promise<Response> {\n\t\tconst url = new URL(req.url)\n\t\tif (url.pathname.endsWith('/presence')) {\n\t\t\treturn Response.json(this.getPresenceMessage().payload)\n\t\t}\n\t\tif (url.pathname.endsWith('/show')) {\n\t\t\tconst users = this.getUsers()\n\t\t\tconst workshopUsers = organizeUsersByWorkshop(users)\n\t\t\treturn new Response(\n\t\t\t\t`\n\t\t\t\t<!DOCTYPE html>\n\t\t\t\t<html lang=\"en\">\n\t\t\t\t<head>\n\t\t\t\t\t<meta charset=\"UTF-8\">\n\t\t\t\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n\t\t\t\t\t<meta http-equiv=\"refresh\" content=\"5\">\n\t\t\t\t\t<title>Epic Web Presence</title>\n\t\t\t\t\t<style>\n\t\t\t\t\t\tbody { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }\n\t\t\t\t\t\th1, h2 { color: #333; }\n\t\t\t\t\t\tul { padding: 0; }\n\t\t\t\t\t\tli { list-style: none; margin-bottom: 10px; }\n\t\t\t\t\t\t.user-avatar { width: 64px; height: 64px; border-radius: 50%; vertical-align: middle; margin-right: 10px; }\n\t\t\t\t\t</style>\n\t\t\t\t</head>\n\t\t\t\t<body>\n\t\t\t\t\t<h1>Epic Web Presence</h1>\n\t\t\t\t\t<p>Total Users: ${users.length}</p>\n\t\t\t\t\t${Object.entries(workshopUsers)\n\t\t\t\t\t\t.map(\n\t\t\t\t\t\t\t([workshop, workshopUsers]) => `\n\t\t\t\t\t\t\t<h2>${workshop} (${workshopUsers.length})</h2>\n\t\t\t\t\t\t\t<ul>\n\t\t\t\t\t\t\t\t${workshopUsers.map(generateUserListItem).join('')}\n\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t`,\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.join('')}\n\t\t\t\t</body>\n\t\t\t\t</html>\n\t\t\t\t`,\n\t\t\t\t{\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t'Content-Type': 'text/html',\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t)\n\t\t}\n\t\treturn new Response('not found', { status: 404 })\n\t}\n} satisfies Party.Worker)\n\nfunction shallowMergeConnectionState(\n\tconnection: Party.Connection,\n\tstate: ConnectionState,\n) {\n\tsetConnectionState(connection, (prev) => ({ ...prev, ...state }))\n}\n\nfunction setConnectionState(\n\tconnection: Party.Connection,\n\tstate:\n\t\t| ConnectionState\n\t\t| ((prev: ConnectionState | null) => ConnectionState | null),\n) {\n\tif (typeof state !== 'function') {\n\t\treturn connection.setState(state)\n\t}\n\tconnection.setState((prev: unknown) => {\n\t\tconst prevParseResult = ConnectionStateSchema.safeParse(prev)\n\t\tif (prevParseResult.success) {\n\t\t\treturn state(prevParseResult.data)\n\t\t} else {\n\t\t\treturn state(null)\n\t\t}\n\t})\n}\n\nfunction getConnectionState(connection: Party.Connection) {\n\tconst result = ConnectionStateSchema.safeParse(connection.state)\n\tif (result.success) {\n\t\treturn result.data\n\t} else {\n\t\tsetConnectionState(connection, null)\n\t\treturn null\n\t}\n}\n\nfunction sortUsers(users: Array<User>) {\n\treturn [...users].sort((a, b) => {\n\t\tconst aScore = getScore(a)\n\t\tconst bScore = getScore(b)\n\t\tif (aScore === bScore) return 0\n\t\treturn aScore > bScore ? -1 : 1\n\t})\n}\n\nfunction getScore(user: User) {\n\tlet score = 0\n\tif (user.imageUrlSmall) score += 1\n\tif (user.imageUrlSmall?.includes('discordapp')) score += 0.5\n\tif (user.name) score += 1\n\treturn score\n}\n\nfunction organizeUsersByWorkshop(users: Array<User>) {\n\tconst workshopUsers: Record<string, Array<User>> = {}\n\n\tfor (const user of users) {\n\t\tconst workshop = user.location?.workshopTitle ?? 'Unknown Workshop'\n\t\tif (!workshopUsers[workshop]) {\n\t\t\tworkshopUsers[workshop] = []\n\t\t}\n\t\tworkshopUsers[workshop]?.push(user)\n\t}\n\n\t// Sort users within each workshop by exercise and step number\n\tfor (const workshop in workshopUsers) {\n\t\tworkshopUsers[workshop]?.sort((a, b) => {\n\t\t\tconst aExercise = a.location?.exercise?.exerciseNumber ?? 0\n\t\t\tconst bExercise = b.location?.exercise?.exerciseNumber ?? 0\n\t\t\tif (aExercise !== bExercise) return aExercise - bExercise\n\n\t\t\tconst aStep = a.location?.exercise?.stepNumber ?? 0\n\t\t\tconst bStep = b.location?.exercise?.stepNumber ?? 0\n\t\t\treturn aStep - bStep\n\t\t})\n\t}\n\n\treturn workshopUsers\n}\n\nfunction generateUserListItem(user: User) {\n\tconst imageUrl = user.imageUrlLarge ?? user.avatarUrl ?? '/avatar.png'\n\tconst name = user.name ?? 'Anonymous'\n\tconst location = user.location?.exercise\n\t\t? [\n\t\t\t\t`Exercise ${user.location.exercise.exerciseNumber}`,\n\t\t\t\tuser.location.exercise.stepNumber &&\n\t\t\t\t\t`Step ${user.location.exercise.stepNumber}`,\n\t\t\t]\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.join(', ')\n\t\t: user.location?.origin\n\t\t\t? user.location.origin\n\t\t\t: 'Unknown location'\n\n\tconst accessLabel =\n\t\ttypeof user.hasAccess === 'boolean' ? (user.hasAccess ? '🔑' : '🆓') : ''\n\n\treturn `\n\t\t<li>\n\t\t\t<img class=\"user-avatar\" src=\"${imageUrl}\" alt=\"${name}\" />\n\t\t\t<strong>${name}</strong> - ${location} ${accessLabel}\n\t\t</li>\n\t`\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@epic-web/workshop-presence",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.4.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"deploy": "partykit deploy"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@epic-web/workshop-utils": "5.
|
|
14
|
+
"@epic-web/workshop-utils": "5.4.0",
|
|
15
15
|
"zod": "^3.23.8"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|