v3: follows, notifications, invite-only registration, unread markers

This commit is contained in:
khannurien
2026-03-21 18:42:47 +00:00
parent 7c098e7c4c
commit 608c6bc6a8
55 changed files with 4743 additions and 884 deletions

View File

@@ -41,6 +41,7 @@ export interface User {
isAdmin: boolean;
createdAt: Date;
avatarMime?: string;
invitedByUsername?: string;
}
export interface LoginUserRequest {
@@ -51,6 +52,7 @@ export interface LoginUserRequest {
export interface RegisterUserRequest {
username: string;
password: string;
inviteToken: string;
}
export interface UpdateUserRequest {
@@ -70,7 +72,8 @@ export function isRegisterUserRequest(
): obj is RegisterUserRequest {
return !!obj && typeof obj === "object" &&
"username" in obj && typeof obj.username === "string" &&
"password" in obj && typeof obj.password === "string";
"password" in obj && typeof obj.password === "string" &&
"inviteToken" in obj && typeof obj.inviteToken === "string";
}
export function isUpdateUserRequest(obj: unknown): obj is UpdateUserRequest {
@@ -101,6 +104,19 @@ export function isAuthPayload(obj: unknown): obj is AuthPayload {
"exp" in obj && typeof obj.exp === "number";
}
export interface InvitePayload {
purpose: "invite";
inviterId: string;
exp: number;
}
export function isInvitePayload(obj: unknown): obj is InvitePayload {
return !!obj && typeof obj === "object" &&
"purpose" in obj && (obj as Record<string, unknown>).purpose === "invite" &&
"inviterId" in obj &&
typeof (obj as Record<string, unknown>).inviterId === "string";
}
/**
* API
*/
@@ -171,11 +187,14 @@ export interface CreateCommentRequest {
parentId?: string;
}
export function isCreateCommentRequest(obj: unknown): obj is CreateCommentRequest {
export function isCreateCommentRequest(
obj: unknown,
): obj is CreateCommentRequest {
if (!obj || typeof obj !== "object") return false;
const o = obj as Record<string, unknown>;
return typeof o.body === "string" && (o.body as string).trim().length > 0 &&
(!("parentId" in o) || typeof o.parentId === "string" || o.parentId === null);
(!("parentId" in o) || typeof o.parentId === "string" ||
o.parentId === null);
}
/**
@@ -191,6 +210,7 @@ export interface Playlist {
createdAt: Date;
imageMime?: string;
dumpCount?: number;
ownerUsername?: string;
}
export interface PlaylistWithDumps extends Playlist {
@@ -345,3 +365,72 @@ export interface PingMessage {
export interface PongMessage {
type: "pong";
}
/**
* Follows
*/
export interface FollowStatus {
followedUserIds: string[];
followedPlaylistIds: string[];
}
/**
* Notifications
*/
export type NotificationType =
| "playlist_followed"
| "user_followed"
| "user_dump_posted"
| "playlist_dump_added"
| "dump_upvoted";
export interface PlaylistFollowedData {
followerId: string;
followerUsername: string;
playlistId: string;
playlistTitle: string;
}
export interface UserFollowedData {
followerId: string;
followerUsername: string;
}
export interface UserDumpPostedData {
dumperId: string;
dumperUsername: string;
dumpId: string;
dumpTitle: string;
}
export interface PlaylistDumpAddedData {
dumpId: string;
dumpTitle: string;
playlistId: string;
playlistTitle: string;
}
export interface DumpUpvotedData {
voterId: string;
voterUsername: string;
dumpId: string;
dumpTitle: string;
}
export type NotificationData =
| PlaylistFollowedData
| UserFollowedData
| UserDumpPostedData
| PlaylistDumpAddedData
| DumpUpvotedData;
export interface Notification {
id: string;
userId: string;
type: NotificationType;
data: NotificationData;
read: boolean;
createdAt: Date;
}