Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,17 @@ export type { Reaction, ReactionType, ReactionCounts } from "./interfaces/Reacti
export type { UnifiedAppNotification, PotentiallyPopulatedUnifiedAppNotification } from "./interfaces/AppNotification";
export type { OAuthIdentity } from "./interfaces/OAuthIdentity";
export type { PushDeviceResult, SendPushResult } from "./interfaces/Push";
export { PUSH_EVENT_TYPES, MUTE_DURATIONS } from "./interfaces/Push";
export type {
PushEventType,
MuteDuration,
NotificationPreferences,
} from "./interfaces/Push";
export type { UpdateNotificationPreferencesProps } from "./modules/push/updateNotificationPreferences";
export type {
MuteConversationProps,
MuteConversationResult,
} from "./modules/chat/muteConversation";
export type { Report, CreateReportResponse } from "./interfaces/Report";
export type { File, FileImage, FileImageVariant } from "./interfaces/File";
export type {
Expand Down
8 changes: 7 additions & 1 deletion src/interfaces/ConversationMember.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@ export interface ConversationMember {
userId: string;
role: ConversationMemberRole | null;
lastReadAt: string | null;
mutedUntil: string | null;
// Personal mute state, present ONLY on the viewer's own row (omitted for
// other members). Timed mute → real ISO timestamp; muted "forever" →
// `mutedUntil: null` with `mutedForever: true` (the storage sentinel is never
// exposed as a magic date). `null` + `mutedForever: false` = not muted.
mutedUntil?: string | null;
// Explicit "forever" signal for the viewer's own row. Absent on others' rows.
mutedForever?: boolean;
isActive: boolean;
leftAt: string | null;
createdAt: string;
Expand Down
46 changes: 46 additions & 0 deletions src/interfaces/Push.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,49 @@ export interface PushDeviceResult {
export interface SendPushResult {
results: Record<string, PushDeviceResult[]>;
}

/**
* Every push event type — the full app-notification type set plus the chat
* `message` event (push-only). Mirrors the server's `PUSH_EVENT_TYPES` exactly
* (server `src/constants/push/pushEvents.ts`); these are the only names a
* `disabledTypes` set may contain.
*/
export const PUSH_EVENT_TYPES = [
"entity-comment",
"comment-reply",
"entity-mention",
"comment-mention",
"entity-upvote",
"comment-upvote",
"entity-reaction",
"comment-reaction",
"entity-reaction-milestone-specific",
"entity-reaction-milestone-total",
"comment-reaction-milestone-specific",
"comment-reaction-milestone-total",
"new-follow",
"connection-request",
"connection-accepted",
"space-membership-approved",
"event-invite",
"event-updated",
"event-cancelled",
"message",
] as const;

export type PushEventType = (typeof PUSH_EVENT_TYPES)[number];

/**
* The four client-facing conversation-mute duration choices. Send the CHOICE,
* never a raw timestamp — the server resolves it and represents "forever" via
* the explicit `mutedForever` signal on the returned member. Mirrors the
* server's `MUTE_DURATIONS` (server `src/helpers/push/muteDuration.ts`).
*/
export const MUTE_DURATIONS = ["8h", "24h", "1w", "forever"] as const;

export type MuteDuration = (typeof MUTE_DURATIONS)[number];

/** Response of read/update notification preferences. */
export interface NotificationPreferences {
disabledTypes: PushEventType[];
}
1 change: 1 addition & 0 deletions src/modules/chat/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ export { toggleReaction } from "./toggleReaction";
export { listReactions } from "./listReactions";
export { markAsRead } from "./markAsRead";
export { reportMessage } from "./reportMessage";
export { muteConversation } from "./muteConversation";
42 changes: 42 additions & 0 deletions src/modules/chat/muteConversation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { SublayHttpClient } from "../../core/client";
import { ConversationMember } from "../../interfaces/ConversationMember";
import { MuteDuration } from "../../interfaces/Push";

export interface MuteConversationProps {
conversationId: string;
/**
* The duration CHOICE (`8h` / `24h` / `1w` / `forever`), or `null` to clear
* the mute. Never a raw timestamp — the server resolves it and represents
* "forever" via the returned member's explicit `mutedForever` signal.
*/
duration: MuteDuration | null;
/**
* The acting user whose membership is muted. The service key acts on this
* user's behalf; the returned `currentMember` reflects that user's row.
*/
userId: string;
}

export interface MuteConversationResult {
currentMember: ConversationMember;
}

/**
* Set / clear a user's conversation mute.
*
* Mirrors `POST /:projectId/chat/conversations/:conversationId/mute`. The route
* is acting-user-scoped; the SDK authenticates with a service key and names the
* acting user via `userId` (like the other per-user operations).
*/
export async function muteConversation(
client: SublayHttpClient,
data: MuteConversationProps
): Promise<MuteConversationResult> {
const { conversationId, duration, userId } = data;
const response =
await client.projectInstance.post<MuteConversationResult>(
`/chat/conversations/${conversationId}/mute`,
{ duration, userId }
);
return response.data;
}
30 changes: 30 additions & 0 deletions src/modules/push/getNotificationPreferences.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { SublayHttpClient } from "../../core/client";
import { NotificationPreferences } from "../../interfaces/Push";

export interface GetNotificationPreferencesProps {
/**
* The acting user whose preferences are read. The service key acts on this
* user's behalf.
*/
userId: string;
}

/**
* Read a user's push notification preferences (the set of event types disabled
* for push). Returns an empty `disabledTypes` (all-on) when no preference row
* exists.
*
* Mirrors `GET /:projectId/push-notifications/preferences`. The route is
* acting-user-scoped; the SDK authenticates with a service key and names the
* acting user via the `userId` query param (like the other per-user reads).
*/
export async function getNotificationPreferences(
client: SublayHttpClient,
data: GetNotificationPreferencesProps
): Promise<NotificationPreferences> {
const response = await client.projectInstance.get<NotificationPreferences>(
`/push-notifications/preferences`,
{ params: { userId: data.userId } }
);
return response.data;
}
2 changes: 2 additions & 0 deletions src/modules/push/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export { send } from "./send";
export { getNotificationPreferences } from "./getNotificationPreferences";
export { updateNotificationPreferences } from "./updateNotificationPreferences";
37 changes: 37 additions & 0 deletions src/modules/push/updateNotificationPreferences.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { SublayHttpClient } from "../../core/client";
import {
NotificationPreferences,
PushEventType,
} from "../../interfaces/Push";

export interface UpdateNotificationPreferencesProps {
/**
* The set of push event types the user has opted OUT of. Server-exact type
* names only (unknown names are rejected server-side); duplicates collapse.
*/
disabledTypes: PushEventType[];
/**
* The acting user whose preferences are written. The service key acts on this
* user's behalf.
*/
userId: string;
}

/**
* Upsert a user's push notification preferences.
*
* Mirrors `PUT /:projectId/push-notifications/preferences`. The route is
* acting-user-scoped; the SDK authenticates with a service key and names the
* acting user via `userId` (like the other per-user operations).
*/
export async function updateNotificationPreferences(
client: SublayHttpClient,
data: UpdateNotificationPreferencesProps
): Promise<NotificationPreferences> {
const { disabledTypes, userId } = data;
const response = await client.projectInstance.put<NotificationPreferences>(
`/push-notifications/preferences`,
{ disabledTypes, userId }
);
return response.data;
}
Loading