+61
-3
internal/db/db.go
+61
-3
internal/db/db.go
···
178
actor_did text not null,
179
subject_uri text not null,
180
181
-
state text not null default 'unread' check(state in ('unread', 'read')),
182
-
type text not null check(type in ('follow', 'reaction', 'comment')),
183
184
created_at text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
185
···
217
is_deleted boolean not null default false,
218
created_at text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
219
220
-
foreign key (did) references profiles(did) on delete cascade
221
unique (did, rkey)
222
);
223
···
234
if err != nil {
235
return nil, fmt.Errorf("failed to execute db create statement: %w", err)
236
}
237
238
return &DB{
239
db,
···
178
actor_did text not null,
179
subject_uri text not null,
180
181
+
state integer not null default 0,
182
+
type text not null,
183
184
created_at text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
185
···
217
is_deleted boolean not null default false,
218
created_at text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
219
220
+
foreign key (did) references profiles(did) on delete cascade,
221
unique (did, rkey)
222
);
223
···
234
if err != nil {
235
return nil, fmt.Errorf("failed to execute db create statement: %w", err)
236
}
237
+
238
+
// This migration removes the type constraint on the notification type as
239
+
// it was painful to add new types. It also changes state to an integer
240
+
// check instead of text.
241
+
runMigration(conn, logger, "simplify-notification-constraints", func(tx *sql.Tx) error {
242
+
// Create new table with state as integer and no type constraint
243
+
_, err := tx.Exec(`
244
+
create table if not exists notifications_new (
245
+
id integer primary key autoincrement,
246
+
247
+
recipient_did text not null,
248
+
actor_did text not null,
249
+
subject_uri text not null,
250
+
251
+
state integer not null default 0,
252
+
type text not null,
253
+
254
+
created_at text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
255
+
256
+
foreign key (recipient_did) references profiles(did) on delete cascade,
257
+
foreign key (actor_did) references profiles(did) on delete cascade
258
+
);
259
+
`)
260
+
if err != nil {
261
+
return err
262
+
}
263
+
264
+
// Copy data, converting state from text to integer
265
+
_, err = tx.Exec(`
266
+
insert into notifications_new (id, recipient_did, actor_did, subject_uri, state, type, created_at)
267
+
select
268
+
id,
269
+
recipient_did,
270
+
actor_did,
271
+
subject_uri,
272
+
case state
273
+
when 'unread' then 0
274
+
when 'read' then 1
275
+
else 0
276
+
end,
277
+
type,
278
+
created_at
279
+
from notifications;
280
+
`)
281
+
if err != nil {
282
+
return err
283
+
}
284
+
285
+
// Drop old table
286
+
_, err = tx.Exec(`drop table notifications`)
287
+
if err != nil {
288
+
return err
289
+
}
290
+
291
+
// Rename new table
292
+
_, err = tx.Exec(`alter table notifications_new rename to notifications`)
293
+
return err
294
+
})
295
296
return &DB{
297
db,
+6
-6
internal/db/notification.go
+6
-6
internal/db/notification.go
···
16
NotificationTypeReply NotificationType = "reply"
17
)
18
19
-
type NotificationState string
20
21
const (
22
-
NotificationStateUnread NotificationState = "unread"
23
-
NotificationStateRead NotificationState = "read"
24
)
25
26
type NotificationWithBskyHandle struct {
···
124
}
125
126
func GetUnreadNotificationCount(e Execer, recipientDid string) (int, error) {
127
-
query := `select count(*) from notifications where recipient_did = ? and state = 'unread';`
128
129
var count int
130
row := e.QueryRow(query, recipientDid)
···
138
func MarkAllNotificationsAsRead(e Execer, did string) error {
139
query := `
140
update notifications
141
-
set state = 'read'
142
-
where recipient_did = ? and state = 'unread';
143
`
144
145
_, err := e.Exec(query, did)
···
16
NotificationTypeReply NotificationType = "reply"
17
)
18
19
+
type NotificationState int
20
21
const (
22
+
NotificationStateUnread NotificationState = 0
23
+
NotificationStateRead NotificationState = 1
24
)
25
26
type NotificationWithBskyHandle struct {
···
124
}
125
126
func GetUnreadNotificationCount(e Execer, recipientDid string) (int, error) {
127
+
query := `select count(*) from notifications where recipient_did = ? and state = 0;`
128
129
var count int
130
row := e.QueryRow(query, recipientDid)
···
138
func MarkAllNotificationsAsRead(e Execer, did string) error {
139
query := `
140
update notifications
141
+
set state = 1
142
+
where recipient_did = ? and state = 0;
143
`
144
145
_, err := e.Exec(query, did)
-26
migrations/update_notification_type.sql
-26
migrations/update_notification_type.sql
···
1
-
-- This script should be used and updated whenever a new notification type
2
-
-- constraint needs to be added.
3
-
4
-
BEGIN TRANSACTION;
5
-
6
-
ALTER TABLE notifications RENAME TO notifications_old;
7
-
8
-
CREATE TABLE notifications (
9
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
10
-
recipient_did TEXT NOT NULL,
11
-
actor_did TEXT NOT NULL,
12
-
subject_uri TEXT NOT NULL,
13
-
state TEXT NOT NULL DEFAULT 'unread' CHECK(state IN ('unread', 'read')),
14
-
type TEXT NOT NULL CHECK(type IN ('follow', 'reaction', 'comment', 'reply')),
15
-
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
16
-
FOREIGN KEY (recipient_did) REFERENCES profiles(did) ON DELETE CASCADE,
17
-
FOREIGN KEY (actor_did) REFERENCES profiles(did) ON DELETE CASCADE
18
-
);
19
-
20
-
INSERT INTO notifications (id, recipient_did, actor_did, subject_uri, state, type, created_at)
21
-
SELECT id, recipient_did, actor_did, subject_uri, state, type, created_at
22
-
FROM notifications_old;
23
-
24
-
DROP TABLE notifications_old;
25
-
26
-
COMMIT;
···