hicli/database,web/roomlist: show marked unread status

This commit is contained in:
Tulir Asokan 2024-11-16 23:45:31 +02:00
parent ead1365c12
commit 80f9a8bb6b
11 changed files with 49 additions and 9 deletions

View file

@ -23,7 +23,7 @@ const (
getRoomBaseQuery = `
SELECT room_id, creation_content, tombstone_content, name, name_quality, avatar, explicit_avatar, topic, canonical_alias,
lazy_load_summary, encryption_event, has_member_list, preview_event_rowid, sorting_timestamp,
unread_highlights, unread_notifications, unread_messages, prev_batch
unread_highlights, unread_notifications, unread_messages, marked_unread, prev_batch
FROM room
`
getRoomsBySortingTimestampQuery = getRoomBaseQuery + `WHERE sorting_timestamp < $1 AND sorting_timestamp > 0 ORDER BY sorting_timestamp DESC LIMIT $2`
@ -50,7 +50,8 @@ const (
unread_highlights = COALESCE($15, room.unread_highlights),
unread_notifications = COALESCE($16, room.unread_notifications),
unread_messages = COALESCE($17, room.unread_messages),
prev_batch = COALESCE($18, room.prev_batch)
marked_unread = COALESCE($18, room.marked_unread),
prev_batch = COALESCE($19, room.prev_batch)
WHERE room_id = $1
`
setRoomPrevBatchQuery = `
@ -157,6 +158,7 @@ type Room struct {
PreviewEventRowID EventRowID `json:"preview_event_rowid"`
SortingTimestamp jsontime.UnixMilli `json:"sorting_timestamp"`
UnreadCounts
MarkedUnread *bool `json:"marked_unread,omitempty"`
PrevBatch string `json:"prev_batch"`
}
@ -220,6 +222,10 @@ func (r *Room) CheckChangesAndCopyInto(other *Room) (hasChanges bool) {
other.UnreadMessages = r.UnreadMessages
hasChanges = true
}
if r.MarkedUnread != other.MarkedUnread {
other.MarkedUnread = r.MarkedUnread
hasChanges = true
}
if r.PrevBatch != "" && other.PrevBatch == "" {
other.PrevBatch = r.PrevBatch
hasChanges = true
@ -248,6 +254,7 @@ func (r *Room) Scan(row dbutil.Scannable) (*Room, error) {
&r.UnreadHighlights,
&r.UnreadNotifications,
&r.UnreadMessages,
&r.MarkedUnread,
&prevBatch,
)
if err != nil {
@ -278,6 +285,7 @@ func (r *Room) sqlVariables() []any {
r.UnreadHighlights,
r.UnreadNotifications,
r.UnreadMessages,
r.MarkedUnread,
dbutil.StrPtr(r.PrevBatch),
}
}

View file

@ -1,4 +1,4 @@
-- v0 -> v6 (compatible with v5+): Latest revision
-- v0 -> v7 (compatible with v5+): Latest revision
CREATE TABLE account (
user_id TEXT NOT NULL PRIMARY KEY,
device_id TEXT NOT NULL,
@ -29,6 +29,7 @@ CREATE TABLE room (
unread_highlights INTEGER NOT NULL DEFAULT 0,
unread_notifications INTEGER NOT NULL DEFAULT 0,
unread_messages INTEGER NOT NULL DEFAULT 0,
marked_unread INTEGER NOT NULL DEFAULT false,
prev_batch TEXT,

View file

@ -0,0 +1,2 @@
-- v7 (compatible with v5+): Add room column for marking unread
ALTER TABLE room ADD COLUMN marked_unread INTEGER NOT NULL DEFAULT false;

View file

@ -151,6 +151,12 @@ func (h *HiClient) SendMessage(
}
func (h *HiClient) MarkRead(ctx context.Context, roomID id.RoomID, eventID id.EventID, receiptType event.ReceiptType) error {
room, err := h.DB.Room.Get(ctx, roomID)
if err != nil {
return fmt.Errorf("failed to get room metadata: %w", err)
} else if room == nil {
return fmt.Errorf("unknown room")
}
content := &mautrix.ReqSetReadMarkers{
FullyRead: eventID,
}
@ -161,10 +167,16 @@ func (h *HiClient) MarkRead(ctx context.Context, roomID id.RoomID, eventID id.Ev
} else {
return fmt.Errorf("invalid receipt type: %v", receiptType)
}
err := h.Client.SetReadMarkers(ctx, roomID, content)
err = h.Client.SetReadMarkers(ctx, roomID, content)
if err != nil {
return fmt.Errorf("failed to mark event as read: %w", err)
}
if ptr.Val(room.MarkedUnread) {
err = h.Client.SetRoomAccountData(ctx, roomID, event.AccountDataMarkedUnread.Type, &event.MarkedUnreadEventContent{Unread: false})
if err != nil {
return fmt.Errorf("failed to mark room as read: %w", err)
}
}
return nil
}

View file

@ -21,6 +21,7 @@ import (
"go.mau.fi/util/emojirunes"
"go.mau.fi/util/exzerolog"
"go.mau.fi/util/jsontime"
"go.mau.fi/util/ptr"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/crypto"
"maunium.net/go/mautrix/crypto/olm"
@ -772,6 +773,10 @@ func (h *HiClient) processStateAndTimeline(
updatedRoom.Avatar = &dmAvatarURL
}
}
mu, ok := accountData[event.AccountDataMarkedUnread]
if ok {
updatedRoom.MarkedUnread = ptr.Ptr(gjson.GetBytes(mu.Content, "unread").Bool())
}
if len(receipts) > 0 {
err = h.DB.Receipt.PutMany(ctx, room.ID, receipts...)

View file

@ -50,6 +50,7 @@ export interface RoomListEntry {
unread_messages: number
unread_notifications: number
unread_highlights: number
marked_unread: boolean
}
export class StateStore {
@ -102,6 +103,7 @@ export class StateStore {
entry.meta.unread_messages !== oldEntry.meta.current.unread_messages ||
entry.meta.unread_notifications !== oldEntry.meta.current.unread_notifications ||
entry.meta.unread_highlights !== oldEntry.meta.current.unread_highlights ||
entry.meta.marked_unread !== oldEntry.meta.current.marked_unread ||
entry.meta.preview_event_rowid !== oldEntry.meta.current.preview_event_rowid ||
entry.events.findIndex(evt => evt.rowid === entry.meta.preview_event_rowid) !== -1
}
@ -129,6 +131,7 @@ export class StateStore {
unread_messages: entry.meta.unread_messages,
unread_notifications: entry.meta.unread_notifications,
unread_highlights: entry.meta.unread_highlights,
marked_unread: entry.meta.marked_unread,
}
}

View file

@ -64,6 +64,7 @@ export interface DBRoom {
unread_highlights: number
unread_notifications: number
unread_messages: number
marked_unread: boolean
prev_batch: string
}

View file

@ -44,6 +44,7 @@
--unread-counter-text-color: var(--inverted-text-color);
--unread-counter-message-bg: rgba(0, 0, 0, 0.35);
--unread-counter-notification-bg: rgba(50, 150, 0, 0.7);
--unread-counter-marked-unread-bg: var(--unread-counter-notification-bg);
--unread-counter-highlight-bg: rgba(200, 0, 0, 0.7);
--sender-color-0: #a4041d;

View file

@ -68,8 +68,9 @@ const EntryInner = ({ room }: InnerProps) => {
<div className="room-name">{room.name}</div>
{previewText && <div className="message-preview" title={previewText}>{croppedPreviewText}</div>}
</div>
{room.unread_messages ? <div className="room-entry-unreads">
{(room.unread_messages || room.marked_unread) ? <div className="room-entry-unreads">
<div className={`unread-count ${
room.marked_unread ? "marked-unread" : ""} ${
room.unread_notifications ? "notified" : ""} ${
room.unread_highlights ? "highlighted" : ""}`}
>

View file

@ -117,7 +117,7 @@ div.room-entry {
line-height: 1;
font-size: .75em;
&.notified, &.highlighted {
&.notified, &.marked-unread, &.highlighted {
width: 1.5rem;
height: 1.5rem;
padding-bottom: 0;
@ -125,7 +125,11 @@ div.room-entry {
font-weight: bold;
}
&.notified:not(.highlighted) {
&.marked-unread {
background-color: var(--unread-counter-marked-unread-bg);
}
&.notified {
background-color: var(--unread-counter-notification-bg);
}

View file

@ -75,10 +75,12 @@ const TimelineView = () => {
&& focused
&& newestEvent
&& newestEvent.timeline_rowid > 0
&& room.readUpToRow < newestEvent.timeline_rowid
&& newestEvent.sender !== client.userID
&& (room.meta.current.marked_unread
|| (room.readUpToRow < newestEvent.timeline_rowid
&& newestEvent.sender !== client.userID))
) {
room.readUpToRow = newestEvent.timeline_rowid
room.meta.current.marked_unread = false
const receiptType = roomCtx.store.preferences.send_read_receipts ? "m.read" : "m.read.private"
client.rpc.markRead(room.roomID, newestEvent.event_id, receiptType).then(
() => console.log("Marked read up to", newestEvent.event_id, newestEvent.timeline_rowid),