Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Priority notification setting #2648

Merged
merged 18 commits into from
Jul 23, 2024
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
7 changes: 7 additions & 0 deletions .changeset/wise-ghosts-look.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@atproto/bsync": patch
"@atproto/bsky": patch
"@atproto/api": patch
---

Support for priority notifications
1 change: 1 addition & 0 deletions lexicons/app/bsky/notification/getUnreadCount.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"parameters": {
"type": "params",
"properties": {
"priority": { "type": "boolean" },
"seenAt": { "type": "string", "format": "datetime" }
}
},
Expand Down
2 changes: 2 additions & 0 deletions lexicons/app/bsky/notification/listNotifications.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"maximum": 100,
"default": 50
},
"priority": { "type": "boolean" },
"cursor": { "type": "string" },
"seenAt": { "type": "string", "format": "datetime" }
}
Expand All @@ -29,6 +30,7 @@
"type": "array",
"items": { "type": "ref", "ref": "#notification" }
},
"priority": { "type": "boolean" },
"seenAt": { "type": "string", "format": "datetime" }
}
}
Expand Down
20 changes: 20 additions & 0 deletions lexicons/app/bsky/notification/putPreferences.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"lexicon": 1,
"id": "app.bsky.notification.putPreferences",
"defs": {
"main": {
"type": "procedure",
"description": "Set notification-related preferences for an account. Requires auth.",
"input": {
"encoding": "application/json",
"schema": {
"type": "object",
"required": ["priority"],
"properties": {
"priority": { "type": "boolean" }
}
}
}
}
}
}
2 changes: 1 addition & 1 deletion packages/api/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@atproto/api",
"version": "0.12.24",
"version": "0.12.25-next.0",
"license": "MIT",
"description": "Client library for atproto and Bluesky",
"keywords": [
Expand Down
13 changes: 13 additions & 0 deletions packages/api/src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ import * as AppBskyLabelerGetServices from './types/app/bsky/labeler/getServices
import * as AppBskyLabelerService from './types/app/bsky/labeler/service'
import * as AppBskyNotificationGetUnreadCount from './types/app/bsky/notification/getUnreadCount'
import * as AppBskyNotificationListNotifications from './types/app/bsky/notification/listNotifications'
import * as AppBskyNotificationPutPreferences from './types/app/bsky/notification/putPreferences'
import * as AppBskyNotificationRegisterPush from './types/app/bsky/notification/registerPush'
import * as AppBskyNotificationUpdateSeen from './types/app/bsky/notification/updateSeen'
import * as AppBskyRichtextFacet from './types/app/bsky/richtext/facet'
Expand Down Expand Up @@ -350,6 +351,7 @@ export * as AppBskyLabelerGetServices from './types/app/bsky/labeler/getServices
export * as AppBskyLabelerService from './types/app/bsky/labeler/service'
export * as AppBskyNotificationGetUnreadCount from './types/app/bsky/notification/getUnreadCount'
export * as AppBskyNotificationListNotifications from './types/app/bsky/notification/listNotifications'
export * as AppBskyNotificationPutPreferences from './types/app/bsky/notification/putPreferences'
export * as AppBskyNotificationRegisterPush from './types/app/bsky/notification/registerPush'
export * as AppBskyNotificationUpdateSeen from './types/app/bsky/notification/updateSeen'
export * as AppBskyRichtextFacet from './types/app/bsky/richtext/facet'
Expand Down Expand Up @@ -2816,6 +2818,17 @@ export class AppBskyNotificationNS {
})
}

putPreferences(
data?: AppBskyNotificationPutPreferences.InputSchema,
opts?: AppBskyNotificationPutPreferences.CallOptions,
): Promise<AppBskyNotificationPutPreferences.Response> {
return this._service.xrpc
.call('app.bsky.notification.putPreferences', opts?.qp, data, opts)
.catch((e) => {
throw AppBskyNotificationPutPreferences.toKnownErr(e)
})
}

registerPush(
data?: AppBskyNotificationRegisterPush.InputSchema,
opts?: AppBskyNotificationRegisterPush.CallOptions,
Expand Down
33 changes: 33 additions & 0 deletions packages/api/src/client/lexicons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8341,6 +8341,9 @@ export const schemaDict = {
parameters: {
type: 'params',
properties: {
priority: {
type: 'boolean',
},
seenAt: {
type: 'string',
format: 'datetime',
Expand Down Expand Up @@ -8379,6 +8382,9 @@ export const schemaDict = {
maximum: 100,
default: 50,
},
priority: {
type: 'boolean',
},
cursor: {
type: 'string',
},
Expand All @@ -8404,6 +8410,9 @@ export const schemaDict = {
ref: 'lex:app.bsky.notification.listNotifications#notification',
},
},
priority: {
type: 'boolean',
},
seenAt: {
type: 'string',
format: 'datetime',
Expand Down Expand Up @@ -8475,6 +8484,29 @@ export const schemaDict = {
},
},
},
AppBskyNotificationPutPreferences: {
lexicon: 1,
id: 'app.bsky.notification.putPreferences',
defs: {
main: {
type: 'procedure',
description:
'Set notification-related preferences for an account. Requires auth.',
input: {
encoding: 'application/json',
schema: {
type: 'object',
required: ['priority'],
properties: {
priority: {
type: 'boolean',
},
},
},
},
},
},
},
AppBskyNotificationRegisterPush: {
lexicon: 1,
id: 'app.bsky.notification.registerPush',
Expand Down Expand Up @@ -11769,6 +11801,7 @@ export const ids = {
AppBskyNotificationGetUnreadCount: 'app.bsky.notification.getUnreadCount',
AppBskyNotificationListNotifications:
'app.bsky.notification.listNotifications',
AppBskyNotificationPutPreferences: 'app.bsky.notification.putPreferences',
AppBskyNotificationRegisterPush: 'app.bsky.notification.registerPush',
AppBskyNotificationUpdateSeen: 'app.bsky.notification.updateSeen',
AppBskyRichtextFacet: 'app.bsky.richtext.facet',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { lexicons } from '../../../../lexicons'
import { CID } from 'multiformats/cid'

export interface QueryParams {
priority?: boolean
seenAt?: string
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import * as ComAtprotoLabelDefs from '../../../com/atproto/label/defs'

export interface QueryParams {
limit?: number
priority?: boolean
cursor?: string
seenAt?: string
}
Expand All @@ -20,6 +21,7 @@ export type InputSchema = undefined
export interface OutputSchema {
cursor?: string
notifications: Notification[]
priority?: boolean
seenAt?: string
[k: string]: unknown
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* GENERATED CODE - DO NOT MODIFY
*/
import { Headers, XRPCError } from '@atproto/xrpc'
import { ValidationResult, BlobRef } from '@atproto/lexicon'
import { isObj, hasProp } from '../../../../util'
import { lexicons } from '../../../../lexicons'
import { CID } from 'multiformats/cid'

export interface QueryParams {}

export interface InputSchema {
priority: boolean
[k: string]: unknown
}

export interface CallOptions {
headers?: Headers
qp?: QueryParams
encoding: 'application/json'
}

export interface Response {
success: boolean
headers: Headers
}

export function toKnownErr(e: any) {
if (e instanceof XRPCError) {
}
return e
}
8 changes: 7 additions & 1 deletion packages/bsky/proto/bsky.proto
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,7 @@ message ActorInfo {
string allow_incoming_chats_from = 8;
string upstream_status = 9;
google.protobuf.Timestamp created_at = 10;
bool priority_notifications = 11;
}

message GetActorsResponse {
Expand Down Expand Up @@ -648,6 +649,7 @@ message GetNotificationsRequest {
string actor_did = 1;
int32 limit = 2;
string cursor = 3;
bool priority = 4;
}

message Notification {
Expand All @@ -656,6 +658,7 @@ message Notification {
string reason = 3;
string reason_subject = 4;
google.protobuf.Timestamp timestamp = 5;
bool priority = 6;
}

message GetNotificationsResponse {
Expand All @@ -668,6 +671,7 @@ message GetNotificationsResponse {
message UpdateNotificationSeenRequest {
string actor_did = 1;
google.protobuf.Timestamp timestamp = 2;
bool priority = 3;
}

message UpdateNotificationSeenResponse {}
Expand All @@ -676,6 +680,7 @@ message UpdateNotificationSeenResponse {}
// - hydrating read state onto notifications
message GetNotificationSeenRequest {
string actor_did = 1;
bool priority = 2;
}

message GetNotificationSeenResponse {
Expand All @@ -686,6 +691,7 @@ message GetNotificationSeenResponse {
// - `getUnreadCount`
message GetUnreadNotificationCountRequest {
string actor_did = 1;
bool priority = 2;
}

message GetUnreadNotificationCountResponse {
Expand Down Expand Up @@ -1203,7 +1209,7 @@ service Service {
rpc CreateActorMutelistSubscription(CreateActorMutelistSubscriptionRequest) returns (CreateActorMutelistSubscriptionResponse);
rpc DeleteActorMutelistSubscription(DeleteActorMutelistSubscriptionRequest) returns (DeleteActorMutelistSubscriptionResponse);
rpc ClearActorMutelistSubscriptions(ClearActorMutelistSubscriptionsRequest) returns (ClearActorMutelistSubscriptionsResponse);

rpc CreateThreadMute(CreateThreadMuteRequest) returns (CreateThreadMuteResponse);
rpc DeleteThreadMute(DeleteThreadMuteRequest) returns (DeleteThreadMuteResponse);
rpc ClearThreadMutes(ClearThreadMutesRequest) returns (ClearThreadMutesResponse);
Expand Down
7 changes: 7 additions & 0 deletions packages/bsky/src/api/app/bsky/notification/getUnreadCount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@ const skeleton = async (
if (params.seenAt) {
throw new InvalidRequestError('The seenAt parameter is unsupported')
}
const priority = params.priority ?? (await getPriority(ctx, params.viewer))
const res = await ctx.hydrator.dataplane.getUnreadNotificationCount({
actorDid: params.viewer,
priority,
})
return {
count: res.count,
Expand Down Expand Up @@ -72,3 +74,8 @@ type Params = QueryParams & {
type SkeletonState = {
count: number
}

const getPriority = async (ctx: Context, did: string) => {
const actors = await ctx.hydrator.actor.getActors([did])
return !!actors.get(did)?.priorityNotifications
}
19 changes: 17 additions & 2 deletions packages/bsky/src/api/app/bsky/notification/listNotifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,20 @@ const skeleton = async (
throw new InvalidRequestError('The seenAt parameter is unsupported')
}
const viewer = params.hydrateCtx.viewer
const priority = params.priority ?? (await getPriority(ctx, viewer))
if (clearlyBadCursor(params.cursor)) {
return { notifs: [] }
return { notifs: [], priority }
}
const [res, lastSeenRes] = await Promise.all([
ctx.hydrator.dataplane.getNotifications({
actorDid: viewer,
priority,
cursor: params.cursor,
limit: params.limit,
}),
ctx.hydrator.dataplane.getNotificationSeen({
actorDid: viewer,
priority,
}),
])
// @NOTE for the first page of results if there's no last-seen time, consider top notification unread
Expand All @@ -72,6 +75,7 @@ const skeleton = async (
return {
notifs: res.notifications,
cursor: res.cursor || undefined,
priority,
lastSeenNotifs: lastSeenDate?.toISOString(),
}
}
Expand Down Expand Up @@ -105,7 +109,12 @@ const presentation = (
const notifications = mapDefined(notifs, (notif) =>
ctx.views.notification(notif, lastSeenNotifs, hydration),
)
return { notifications, cursor, seenAt: skeleton.lastSeenNotifs }
return {
notifications,
cursor,
priority: skeleton.priority,
seenAt: skeleton.lastSeenNotifs,
}
}

type Context = {
Expand All @@ -119,6 +128,12 @@ type Params = QueryParams & {

type SkeletonState = {
notifs: Notification[]
priority: boolean
lastSeenNotifs?: string
cursor?: string
}

const getPriority = async (ctx: Context, did: string) => {
const actors = await ctx.hydrator.actor.getActors([did])
return !!actors.get(did)?.priorityNotifications
}
16 changes: 16 additions & 0 deletions packages/bsky/src/api/app/bsky/notification/putPreferences.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Server } from '../../../../lexicon'
import AppContext from '../../../../context'

export default function (server: Server, ctx: AppContext) {
server.app.bsky.notification.putPreferences({
auth: ctx.authVerifier.standard,
handler: async ({ input, auth }) => {
const { priority } = input.body
const viewer = auth.credentials.iss
await ctx.bsyncClient.addNotifOperation({
actorDid: viewer,
priority,
})
},
})
}
19 changes: 14 additions & 5 deletions packages/bsky/src/api/app/bsky/notification/updateSeen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,21 @@ export default function (server: Server, ctx: AppContext) {
server.app.bsky.notification.updateSeen({
auth: ctx.authVerifier.standard,
handler: async ({ input, auth }) => {
const { seenAt } = input.body
const viewer = auth.credentials.iss
await ctx.dataplane.updateNotificationSeen({
actorDid: viewer,
timestamp: Timestamp.fromDate(new Date(seenAt)),
})
const seenAt = new Date(input.body.seenAt)
// For now we keep separate seen times behind the scenes for priority, but treat them as a single seen time.
await Promise.all([
ctx.dataplane.updateNotificationSeen({
actorDid: viewer,
timestamp: Timestamp.fromDate(seenAt),
priority: false,
}),
ctx.dataplane.updateNotificationSeen({
actorDid: viewer,
timestamp: Timestamp.fromDate(seenAt),
priority: true,
}),
])
},
})
}
Loading