diff --git a/internal/payload/outgoing.go b/internal/payload/outgoing.go index 7d4475555..a758b9d8a 100644 --- a/internal/payload/outgoing.go +++ b/internal/payload/outgoing.go @@ -128,7 +128,7 @@ func Channel(ch *samTypes.Channel) *outgoing.Channel { Topic: ch.Topic, Type: string(ch.Type), Members: Uint64stoa(ch.Members), - View: ChannelView(ch.View), + Unread: Unread(ch.Unread), CanJoin: ch.CanJoin, CanPart: ch.CanPart, @@ -174,14 +174,14 @@ func ChannelMembers(members samTypes.ChannelMemberSet) *outgoing.ChannelMemberSe return &retval } -func ChannelView(v *samTypes.ChannelView) *outgoing.ChannelView { +func Unread(v *samTypes.Unread) *outgoing.Unread { if v == nil { return nil } - return &outgoing.ChannelView{ - LastMessageID: Uint64toa(v.LastMessageID), - NewMessagesCount: v.NewMessagesCount, + return &outgoing.Unread{ + LastMessageID: v.LastMessageID, + Count: v.Count, } } diff --git a/internal/payload/outgoing/channel.go b/internal/payload/outgoing/channel.go index 01e95500f..08a948597 100644 --- a/internal/payload/outgoing/channel.go +++ b/internal/payload/outgoing/channel.go @@ -31,13 +31,13 @@ type ( Channel struct { // Channel to part (nil) for ALL channels - ID string `json:"ID"` - Name string `json:"name"` - Topic string `json:"topic"` - Type string `json:"type"` - LastMessageID string `json:"lastMessageID"` - Members []string `json:"members,omitempty"` - View *ChannelView `json:"view,omitempty"` + ID string `json:"ID"` + Name string `json:"name"` + Topic string `json:"topic"` + Type string `json:"type"` + LastMessageID string `json:"lastMessageID"` + Members []string `json:"members,omitempty"` + Unread *Unread `json:"unread,omitempty"` CanJoin bool `json:"canJoin"` CanPart bool `json:"canPart"` diff --git a/internal/payload/outgoing/channel_view.go b/internal/payload/outgoing/channel_view.go deleted file mode 100644 index 517b9113b..000000000 --- a/internal/payload/outgoing/channel_view.go +++ /dev/null @@ -1,9 +0,0 @@ -package outgoing - -type ( - ChannelView struct { - // Channel to part (nil) for ALL channels - LastMessageID string `json:"lastMessageID"` - NewMessagesCount uint32 `json:"newMessagesCount"` - } -) diff --git a/internal/payload/outgoing/unread.go b/internal/payload/outgoing/unread.go new file mode 100644 index 000000000..4da9f99df --- /dev/null +++ b/internal/payload/outgoing/unread.go @@ -0,0 +1,9 @@ +package outgoing + +type ( + Unread struct { + // Channel to part (nil) for ALL channels + LastMessageID uint64 `json:"lastMessageID,string,omitempty"` + Count uint32 `json:"count"` + } +) diff --git a/sam/db/mysql/statik.go b/sam/db/mysql/statik.go index 69b755f2c..367f7a583 100644 --- a/sam/db/mysql/statik.go +++ b/sam/db/mysql/statik.go @@ -8,7 +8,7 @@ import ( ) func Data() string { - return "PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1a\x00 \x0020180704080000.base.up.sqlUT\x05\x00\x01\x80Cm8-- all known organisations (crust instances) and our relation towards them\nCREATE TABLE organisations (\n id BIGINT UNSIGNED NOT NULL,\n fqn TEXT NOT NULL, -- fully qualified name of the organisation\n name TEXT NOT NULL, -- display name of the organisation\n\n created_at DATETIME NOT NULL DEFAULT NOW(),\n updated_at DATETIME NULL,\n archived_at DATETIME NULL,\n deleted_at DATETIME NULL, -- organisation soft delete\n\n PRIMARY KEY (id)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n-- Keeps all known teams\nCREATE TABLE teams (\n id BIGINT UNSIGNED NOT NULL,\n name TEXT NOT NULL, -- display name of the team\n handle TEXT NOT NULL, -- team handle string\n\n created_at DATETIME NOT NULL DEFAULT NOW(),\n updated_at DATETIME NULL,\n archived_at DATETIME NULL,\n deleted_at DATETIME NULL, -- team soft delete\n\n PRIMARY KEY (id)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n-- Keeps all known channels\nCREATE TABLE channels (\n id BIGINT UNSIGNED NOT NULL,\n name TEXT NOT NULL, -- display name of the channel\n topic TEXT NOT NULL,\n meta JSON NOT NULL,\n\n type ENUM ('private', 'public', 'group') NOT NULL DEFAULT 'public',\n\n rel_organisation BIGINT UNSIGNED NOT NULL REFERENCES organisation(id),\n rel_creator BIGINT UNSIGNED NOT NULL,\n\n created_at DATETIME NOT NULL DEFAULT NOW(),\n updated_at DATETIME NULL,\n archived_at DATETIME NULL,\n deleted_at DATETIME NULL, -- channel soft delete\n\n rel_last_message BIGINT UNSIGNED NOT NULL DEFAULT 0,\n\n PRIMARY KEY (id)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n-- Keeps team memberships\nCREATE TABLE team_members (\n rel_team BIGINT UNSIGNED NOT NULL REFERENCES organisation(id),\n rel_user BIGINT UNSIGNED NOT NULL,\n\n PRIMARY KEY (rel_team, rel_user)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n-- handles channel membership\nCREATE TABLE channel_members (\n rel_channel BIGINT UNSIGNED NOT NULL REFERENCES channels(id),\n rel_user BIGINT UNSIGNED NOT NULL,\n\n type ENUM ('owner', 'member', 'invitee') NOT NULL DEFAULT 'member',\n\n created_at DATETIME NOT NULL DEFAULT NOW(),\n updated_at DATETIME NULL,\n\n PRIMARY KEY (rel_channel, rel_user)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE channel_views (\n rel_channel BIGINT UNSIGNED NOT NULL REFERENCES channels(id),\n rel_user BIGINT UNSIGNED NOT NULL,\n\n -- timestamp of last view, should be enough to find out which messaghr\n viewed_at DATETIME NOT NULL DEFAULT NOW(),\n\n -- new messages count since last view\n new_since INT UNSIGNED NOT NULL DEFAULT 0,\n\n PRIMARY KEY (rel_user, rel_channel)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE channel_pins (\n rel_channel BIGINT UNSIGNED NOT NULL REFERENCES channels(id),\n rel_message BIGINT UNSIGNED NOT NULL REFERENCES messages(id),\n rel_user BIGINT UNSIGNED NOT NULL,\n\n created_at DATETIME NOT NULL DEFAULT NOW(),\n\n PRIMARY KEY (rel_channel, rel_message)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE messages (\n id BIGINT UNSIGNED NOT NULL,\n type TEXT,\n message TEXT NOT NULL,\n meta JSON,\n rel_user BIGINT UNSIGNED NOT NULL,\n rel_channel BIGINT UNSIGNED NOT NULL REFERENCES channels(id),\n reply_to BIGINT UNSIGNED NULL REFERENCES messages(id),\n\n created_at DATETIME NOT NULL DEFAULT NOW(),\n updated_at DATETIME NULL,\n deleted_at DATETIME NULL,\n\n PRIMARY KEY (id)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE reactions (\n id BIGINT UNSIGNED NOT NULL,\n rel_user BIGINT UNSIGNED NOT NULL,\n rel_message BIGINT UNSIGNED NOT NULL REFERENCES messages(id),\n rel_channel BIGINT UNSIGNED NOT NULL REFERENCES channels(id),\n reaction TEXT NOT NULL,\n\n created_at DATETIME NOT NULL DEFAULT NOW(),\n\n PRIMARY KEY (id)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE attachments (\n id BIGINT UNSIGNED NOT NULL,\n rel_user BIGINT UNSIGNED NOT NULL,\n\n url VARCHAR(512),\n preview_url VARCHAR(512),\n\n size INT UNSIGNED,\n mimetype VARCHAR(255),\n name TEXT,\n\n meta JSON,\n\n created_at DATETIME NOT NULL DEFAULT NOW(),\n updated_at DATETIME NULL,\n deleted_at DATETIME NULL,\n\n PRIMARY KEY (id)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE message_attachment (\n rel_message BIGINT UNSIGNED NOT NULL REFERENCES messages(id),\n rel_attachment BIGINT UNSIGNED NOT NULL REFERENCES attachment(id),\n\n PRIMARY KEY (rel_message)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE event_queue (\n id BIGINT UNSIGNED NOT NULL,\n origin BIGINT UNSIGNED NOT NULL,\n subscriber TEXT,\n payload JSON,\n\n PRIMARY KEY (id)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE event_queue_synced (\n origin BIGINT UNSIGNED NOT NULL,\n rel_last BIGINT UNSIGNED NOT NULL,\n\n PRIMARY KEY (origin)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\nPK\x07\x08\xd2g\xcd\xce\x9f\x15\x00\x00\x9f\x15\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00$\x00 \x0020181009080000.altering_types.up.sqlUT\x05\x00\x01\x80Cm8update channels set type = 'group' where type = 'direct';\nalter table channels CHANGE type type enum('private', 'public', 'group');\nalter table channel_members CHANGE type type enum('owner', 'member', 'invitee');\nPK\x07\x08E1\xf5\xa4\xd7\x00\x00\x00\xd7\x00\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00#\x00 \x0020181013080000.channel_views.up.sqlUT\x05\x00\x01\x80Cm8ALTER TABLE channel_views DROP viewed_at;\nALTER TABLE channel_views ADD rel_last_message_id BIGINT UNSIGNED;\nALTER TABLE channel_views CHANGE new_since new_messages_count INT UNSIGNED;\n\n-- Table structure after these changes:\n-- +---------------------+---------------------+------+-----+---------+-------+\n-- | Field | Type | Null | Key | Default | Extra |\n-- +---------------------+---------------------+------+-----+---------+-------+\n-- | rel_channel | bigint(20) unsigned | NO | PRI | NULL | |\n-- | rel_user | bigint(20) unsigned | NO | PRI | NULL | |\n-- | rel_last_message_id | bigint(20) unsigned | YES | | NULL | |\n-- | new_messages_count | int(10) unsigned | NO | | 0 | |\n-- +---------------------+---------------------+------+-----+---------+-------+\n\n-- Prefill with data\nINSERT INTO channel_views (rel_channel, rel_user, rel_last_message_id)\n SELECT cm.rel_channel, cm.rel_user, max(m.ID)\n FROM channel_members AS cm INNER JOIN messages AS m ON (m.rel_channel = cm.rel_channel)\n GROUP BY cm.rel_channel, cm.rel_user;\n\nPK\x07\x08`\xcbP\xf9t\x04\x00\x00t\x04\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1d\x00 \x0020181013080000.replies.up.sqlUT\x05\x00\x01\x80Cm8ALTER TABLE messages CHANGE reply_to reply_to BIGINT UNSIGNED NOT NULL DEFAULT 0;\nALTER TABLE messages ADD replies INT UNSIGNED NOT NULL DEFAULT 0;\nPK\x07\x08m\xedWA\x94\x00\x00\x00\x94\x00\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00(\x00 \x0020181101080000.pins_and_reactions.up.sqlUT\x05\x00\x01\x80Cm8DROP TABLE channel_pins;\nDROP TABLE reactions;\n\nCREATE TABLE message_flags (\n id BIGINT UNSIGNED NOT NULL,\n rel_channel BIGINT UNSIGNED NOT NULL,\n rel_message BIGINT UNSIGNED NOT NULL,\n rel_user BIGINT UNSIGNED NOT NULL,\n flag TEXT,\n\n created_at DATETIME NOT NULL DEFAULT NOW(),\n\n PRIMARY KEY (id)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\nPK\x07\x08eA\x1eo\x90\x01\x00\x00\x90\x01\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1e\x00 \x0020181107080000.mentions.up.sqlUT\x05\x00\x01\x80Cm8CREATE TABLE mentions (\n id BIGINT UNSIGNED NOT NULL,\n rel_channel BIGINT UNSIGNED NOT NULL,\n rel_message BIGINT UNSIGNED NOT NULL,\n rel_user BIGINT UNSIGNED NOT NULL,\n rel_mentioned_by BIGINT UNSIGNED NOT NULL,\n\n created_at DATETIME NOT NULL DEFAULT NOW(),\n\n PRIMARY KEY (id)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE INDEX lookup_mentions ON mentions (rel_mentioned_by)\nPK\x07\x08\xfb\xe8\x9b\x98\xac\x01\x00\x00\xac\x01\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00 \x00migrations.sqlUT\x05\x00\x01\x80Cm8CREATE TABLE IF NOT EXISTS `migrations` (\n `project` varchar(16) NOT NULL COMMENT 'sam, crm, ...',\n `filename` varchar(255) NOT NULL COMMENT 'yyyymmddHHMMSS.sql',\n `statement_index` int(11) NOT NULL COMMENT 'Statement number from SQL file',\n `status` TEXT NOT NULL COMMENT 'ok or full error message',\n PRIMARY KEY (`project`,`filename`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nPK\x07\x08\x0d\xa5T2x\x01\x00\x00x\x01\x00\x00PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(\xd2g\xcd\xce\x9f\x15\x00\x00\x9f\x15\x00\x00\x1a\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00\x00\x00\x0020180704080000.base.up.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(E1\xf5\xa4\xd7\x00\x00\x00\xd7\x00\x00\x00$\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xf0\x15\x00\x0020181009080000.altering_types.up.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(`\xcbP\xf9t\x04\x00\x00t\x04\x00\x00#\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\"\x17\x00\x0020181013080000.channel_views.up.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(m\xedWA\x94\x00\x00\x00\x94\x00\x00\x00\x1d\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xf0\x1b\x00\x0020181013080000.replies.up.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(eA\x1eo\x90\x01\x00\x00\x90\x01\x00\x00(\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xd8\x1c\x00\x0020181101080000.pins_and_reactions.up.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(\xfb\xe8\x9b\x98\xac\x01\x00\x00\xac\x01\x00\x00\x1e\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xc7\x1e\x00\x0020181107080000.mentions.up.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(\x0d\xa5T2x\x01\x00\x00x\x01\x00\x00\x0e\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xc8 \x00\x00migrations.sqlUT\x05\x00\x01\x80Cm8PK\x05\x06\x00\x00\x00\x00\x07\x00\x07\x00S\x02\x00\x00\x85\"\x00\x00\x00\x00" + return "PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1a\x00 \x0020180704080000.base.up.sqlUT\x05\x00\x01\x80Cm8-- all known organisations (crust instances) and our relation towards them\nCREATE TABLE organisations (\n id BIGINT UNSIGNED NOT NULL,\n fqn TEXT NOT NULL, -- fully qualified name of the organisation\n name TEXT NOT NULL, -- display name of the organisation\n\n created_at DATETIME NOT NULL DEFAULT NOW(),\n updated_at DATETIME NULL,\n archived_at DATETIME NULL,\n deleted_at DATETIME NULL, -- organisation soft delete\n\n PRIMARY KEY (id)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n-- Keeps all known teams\nCREATE TABLE teams (\n id BIGINT UNSIGNED NOT NULL,\n name TEXT NOT NULL, -- display name of the team\n handle TEXT NOT NULL, -- team handle string\n\n created_at DATETIME NOT NULL DEFAULT NOW(),\n updated_at DATETIME NULL,\n archived_at DATETIME NULL,\n deleted_at DATETIME NULL, -- team soft delete\n\n PRIMARY KEY (id)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n-- Keeps all known channels\nCREATE TABLE channels (\n id BIGINT UNSIGNED NOT NULL,\n name TEXT NOT NULL, -- display name of the channel\n topic TEXT NOT NULL,\n meta JSON NOT NULL,\n\n type ENUM ('private', 'public', 'group') NOT NULL DEFAULT 'public',\n\n rel_organisation BIGINT UNSIGNED NOT NULL REFERENCES organisation(id),\n rel_creator BIGINT UNSIGNED NOT NULL,\n\n created_at DATETIME NOT NULL DEFAULT NOW(),\n updated_at DATETIME NULL,\n archived_at DATETIME NULL,\n deleted_at DATETIME NULL, -- channel soft delete\n\n rel_last_message BIGINT UNSIGNED NOT NULL DEFAULT 0,\n\n PRIMARY KEY (id)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n-- Keeps team memberships\nCREATE TABLE team_members (\n rel_team BIGINT UNSIGNED NOT NULL REFERENCES organisation(id),\n rel_user BIGINT UNSIGNED NOT NULL,\n\n PRIMARY KEY (rel_team, rel_user)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n-- handles channel membership\nCREATE TABLE channel_members (\n rel_channel BIGINT UNSIGNED NOT NULL REFERENCES channels(id),\n rel_user BIGINT UNSIGNED NOT NULL,\n\n type ENUM ('owner', 'member', 'invitee') NOT NULL DEFAULT 'member',\n\n created_at DATETIME NOT NULL DEFAULT NOW(),\n updated_at DATETIME NULL,\n\n PRIMARY KEY (rel_channel, rel_user)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE channel_views (\n rel_channel BIGINT UNSIGNED NOT NULL REFERENCES channels(id),\n rel_user BIGINT UNSIGNED NOT NULL,\n\n -- timestamp of last view, should be enough to find out which messaghr\n viewed_at DATETIME NOT NULL DEFAULT NOW(),\n\n -- new messages count since last view\n new_since INT UNSIGNED NOT NULL DEFAULT 0,\n\n PRIMARY KEY (rel_user, rel_channel)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE channel_pins (\n rel_channel BIGINT UNSIGNED NOT NULL REFERENCES channels(id),\n rel_message BIGINT UNSIGNED NOT NULL REFERENCES messages(id),\n rel_user BIGINT UNSIGNED NOT NULL,\n\n created_at DATETIME NOT NULL DEFAULT NOW(),\n\n PRIMARY KEY (rel_channel, rel_message)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE messages (\n id BIGINT UNSIGNED NOT NULL,\n type TEXT,\n message TEXT NOT NULL,\n meta JSON,\n rel_user BIGINT UNSIGNED NOT NULL,\n rel_channel BIGINT UNSIGNED NOT NULL REFERENCES channels(id),\n reply_to BIGINT UNSIGNED NULL REFERENCES messages(id),\n\n created_at DATETIME NOT NULL DEFAULT NOW(),\n updated_at DATETIME NULL,\n deleted_at DATETIME NULL,\n\n PRIMARY KEY (id)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE reactions (\n id BIGINT UNSIGNED NOT NULL,\n rel_user BIGINT UNSIGNED NOT NULL,\n rel_message BIGINT UNSIGNED NOT NULL REFERENCES messages(id),\n rel_channel BIGINT UNSIGNED NOT NULL REFERENCES channels(id),\n reaction TEXT NOT NULL,\n\n created_at DATETIME NOT NULL DEFAULT NOW(),\n\n PRIMARY KEY (id)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE attachments (\n id BIGINT UNSIGNED NOT NULL,\n rel_user BIGINT UNSIGNED NOT NULL,\n\n url VARCHAR(512),\n preview_url VARCHAR(512),\n\n size INT UNSIGNED,\n mimetype VARCHAR(255),\n name TEXT,\n\n meta JSON,\n\n created_at DATETIME NOT NULL DEFAULT NOW(),\n updated_at DATETIME NULL,\n deleted_at DATETIME NULL,\n\n PRIMARY KEY (id)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE message_attachment (\n rel_message BIGINT UNSIGNED NOT NULL REFERENCES messages(id),\n rel_attachment BIGINT UNSIGNED NOT NULL REFERENCES attachment(id),\n\n PRIMARY KEY (rel_message)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE event_queue (\n id BIGINT UNSIGNED NOT NULL,\n origin BIGINT UNSIGNED NOT NULL,\n subscriber TEXT,\n payload JSON,\n\n PRIMARY KEY (id)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE event_queue_synced (\n origin BIGINT UNSIGNED NOT NULL,\n rel_last BIGINT UNSIGNED NOT NULL,\n\n PRIMARY KEY (origin)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\nPK\x07\x08\xd2g\xcd\xce\x9f\x15\x00\x00\x9f\x15\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00$\x00 \x0020181009080000.altering_types.up.sqlUT\x05\x00\x01\x80Cm8update channels set type = 'group' where type = 'direct';\nalter table channels CHANGE type type enum('private', 'public', 'group');\nalter table channel_members CHANGE type type enum('owner', 'member', 'invitee');\nPK\x07\x08E1\xf5\xa4\xd7\x00\x00\x00\xd7\x00\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00#\x00 \x0020181013080000.channel_views.up.sqlUT\x05\x00\x01\x80Cm8ALTER TABLE channel_views DROP viewed_at;\nALTER TABLE channel_views ADD rel_last_message_id BIGINT UNSIGNED;\nALTER TABLE channel_views CHANGE new_since new_messages_count INT UNSIGNED;\n\n-- Table structure after these changes:\n-- +---------------------+---------------------+------+-----+---------+-------+\n-- | Field | Type | Null | Key | Default | Extra |\n-- +---------------------+---------------------+------+-----+---------+-------+\n-- | rel_channel | bigint(20) unsigned | NO | PRI | NULL | |\n-- | rel_user | bigint(20) unsigned | NO | PRI | NULL | |\n-- | rel_last_message_id | bigint(20) unsigned | YES | | NULL | |\n-- | new_messages_count | int(10) unsigned | NO | | 0 | |\n-- +---------------------+---------------------+------+-----+---------+-------+\n\n-- Prefill with data\nINSERT INTO channel_views (rel_channel, rel_user, rel_last_message_id)\n SELECT cm.rel_channel, cm.rel_user, max(m.ID)\n FROM channel_members AS cm INNER JOIN messages AS m ON (m.rel_channel = cm.rel_channel)\n GROUP BY cm.rel_channel, cm.rel_user;\n\nPK\x07\x08`\xcbP\xf9t\x04\x00\x00t\x04\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1d\x00 \x0020181013080000.replies.up.sqlUT\x05\x00\x01\x80Cm8ALTER TABLE messages CHANGE reply_to reply_to BIGINT UNSIGNED NOT NULL DEFAULT 0;\nALTER TABLE messages ADD replies INT UNSIGNED NOT NULL DEFAULT 0;\nPK\x07\x08m\xedWA\x94\x00\x00\x00\x94\x00\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00(\x00 \x0020181101080000.pins_and_reactions.up.sqlUT\x05\x00\x01\x80Cm8DROP TABLE channel_pins;\nDROP TABLE reactions;\n\nCREATE TABLE message_flags (\n id BIGINT UNSIGNED NOT NULL,\n rel_channel BIGINT UNSIGNED NOT NULL,\n rel_message BIGINT UNSIGNED NOT NULL,\n rel_user BIGINT UNSIGNED NOT NULL,\n flag TEXT,\n\n created_at DATETIME NOT NULL DEFAULT NOW(),\n\n PRIMARY KEY (id)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\nPK\x07\x08eA\x1eo\x90\x01\x00\x00\x90\x01\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1e\x00 \x0020181107080000.mentions.up.sqlUT\x05\x00\x01\x80Cm8CREATE TABLE mentions (\n id BIGINT UNSIGNED NOT NULL,\n rel_channel BIGINT UNSIGNED NOT NULL,\n rel_message BIGINT UNSIGNED NOT NULL,\n rel_user BIGINT UNSIGNED NOT NULL,\n rel_mentioned_by BIGINT UNSIGNED NOT NULL,\n\n created_at DATETIME NOT NULL DEFAULT NOW(),\n\n PRIMARY KEY (id)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE INDEX lookup_mentions ON mentions (rel_mentioned_by)\nPK\x07\x08\xfb\xe8\x9b\x98\xac\x01\x00\x00\xac\x01\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1d\x00 \x0020181115080000.unreads.up.sqlUT\x05\x00\x01\x80Cm8ALTER TABLE channel_views RENAME TO unreads;\n\nALTER TABLE unreads ADD rel_reply_to BIGINT UNSIGNED NOT NULL AFTER rel_channel;\nALTER TABLE unreads CHANGE rel_channel rel_channel BIGINT UNSIGNED NOT NULL DEFAULT 0;\nALTER TABLE unreads CHANGE rel_user rel_user BIGINT UNSIGNED NOT NULL DEFAULT 0;\nALTER TABLE unreads CHANGE rel_last_message_id rel_last_message BIGINT UNSIGNED NOT NULL DEFAULT 0;\nALTER TABLE unreads CHANGE new_messages_count count INT UNSIGNED NOT NULL DEFAULT 0;\n\nPK\x07\x08jf1Q+\x02\x00\x00+\x02\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00 \x00migrations.sqlUT\x05\x00\x01\x80Cm8CREATE TABLE IF NOT EXISTS `migrations` (\n `project` varchar(16) NOT NULL COMMENT 'sam, crm, ...',\n `filename` varchar(255) NOT NULL COMMENT 'yyyymmddHHMMSS.sql',\n `statement_index` int(11) NOT NULL COMMENT 'Statement number from SQL file',\n `status` TEXT NOT NULL COMMENT 'ok or full error message',\n PRIMARY KEY (`project`,`filename`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nPK\x07\x08\x0d\xa5T2x\x01\x00\x00x\x01\x00\x00PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(\xd2g\xcd\xce\x9f\x15\x00\x00\x9f\x15\x00\x00\x1a\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00\x00\x00\x0020180704080000.base.up.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(E1\xf5\xa4\xd7\x00\x00\x00\xd7\x00\x00\x00$\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xf0\x15\x00\x0020181009080000.altering_types.up.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(`\xcbP\xf9t\x04\x00\x00t\x04\x00\x00#\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\"\x17\x00\x0020181013080000.channel_views.up.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(m\xedWA\x94\x00\x00\x00\x94\x00\x00\x00\x1d\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xf0\x1b\x00\x0020181013080000.replies.up.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(eA\x1eo\x90\x01\x00\x00\x90\x01\x00\x00(\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xd8\x1c\x00\x0020181101080000.pins_and_reactions.up.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(\xfb\xe8\x9b\x98\xac\x01\x00\x00\xac\x01\x00\x00\x1e\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xc7\x1e\x00\x0020181107080000.mentions.up.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(jf1Q+\x02\x00\x00+\x02\x00\x00\x1d\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xc8 \x00\x0020181115080000.unreads.up.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(\x0d\xa5T2x\x01\x00\x00x\x01\x00\x00\x0e\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81G#\x00\x00migrations.sqlUT\x05\x00\x01\x80Cm8PK\x05\x06\x00\x00\x00\x00\x08\x00\x08\x00\xa7\x02\x00\x00\x04%\x00\x00\x00\x00" } func init() { diff --git a/sam/db/schema/mysql/20181115080000.unreads.up.sql b/sam/db/schema/mysql/20181115080000.unreads.up.sql new file mode 100644 index 000000000..be7d52800 --- /dev/null +++ b/sam/db/schema/mysql/20181115080000.unreads.up.sql @@ -0,0 +1,8 @@ +ALTER TABLE channel_views RENAME TO unreads; + +ALTER TABLE unreads ADD rel_reply_to BIGINT UNSIGNED NOT NULL AFTER rel_channel; +ALTER TABLE unreads CHANGE rel_channel rel_channel BIGINT UNSIGNED NOT NULL DEFAULT 0; +ALTER TABLE unreads CHANGE rel_user rel_user BIGINT UNSIGNED NOT NULL DEFAULT 0; +ALTER TABLE unreads CHANGE rel_last_message_id rel_last_message BIGINT UNSIGNED NOT NULL DEFAULT 0; +ALTER TABLE unreads CHANGE new_messages_count count INT UNSIGNED NOT NULL DEFAULT 0; + diff --git a/sam/docs/README.md b/sam/docs/README.md index 5e76a2c1e..5338691b4 100644 --- a/sam/docs/README.md +++ b/sam/docs/README.md @@ -308,6 +308,21 @@ The following event types may be sent with a message event: | channelID | uint64 | PATH | Channel ID | N/A | YES | | message | string | POST | Message contents (markdown) | N/A | YES | +## Mark message in channel (or thread) as unread + +#### Method + +| URI | Protocol | Method | Authentication | +| --- | -------- | ------ | -------------- | +| `/channels/{channelID}/messages/{messageID}/unread` | HTTP/S | POST | Client ID, Session ID | + +#### Request parameters + +| Parameter | Type | Method | Description | Default | Required? | +| --------- | ---- | ------ | ----------- | ------- | --------- | +| messageID | uint64 | PATH | Message ID | N/A | YES | +| channelID | uint64 | PATH | Channel ID | N/A | YES | + ## Pin message to channel (public bookmark) #### Method diff --git a/sam/docs/src/spec.json b/sam/docs/src/spec.json index 90e574bbd..2951c51d6 100644 --- a/sam/docs/src/spec.json +++ b/sam/docs/src/spec.json @@ -390,6 +390,22 @@ ] } }, + { + "name": "markAsUnread", + "path": "/{messageID}/unread", + "method": "POST", + "title": "Mark message in channel (or thread) as unread", + "parameters": { + "path": [ + { + "name": "messageID", + "type": "uint64", + "required": true, + "title": "Message ID" + } + ] + } + }, { "name": "pinCreate", "path": "/{messageID}/pin", @@ -619,4 +635,4 @@ } ] } -] \ No newline at end of file +] diff --git a/sam/docs/src/spec/message.json b/sam/docs/src/spec/message.json index fd8799587..31614115f 100644 --- a/sam/docs/src/spec/message.json +++ b/sam/docs/src/spec/message.json @@ -132,6 +132,22 @@ ] } }, + { + "Name": "markAsUnread", + "Method": "POST", + "Title": "Mark message in channel (or thread) as unread", + "Path": "/{messageID}/unread", + "Parameters": { + "path": [ + { + "name": "messageID", + "required": true, + "title": "Message ID", + "type": "uint64" + } + ] + } + }, { "Name": "pinCreate", "Method": "POST", diff --git a/sam/repository/channel_views.go b/sam/repository/channel_views.go index b5dd3f5af..8a6242d66 100644 --- a/sam/repository/channel_views.go +++ b/sam/repository/channel_views.go @@ -13,8 +13,8 @@ type ( ChannelViewRepository interface { With(ctx context.Context, db *factory.DB) ChannelViewRepository - Find(filter *types.ChannelViewFilter) (types.ChannelViewSet, error) - Record(userID, channelID, lastMessageID uint64, count uint32) error + Find(filter *types.UnreadFilter) (types.UnreadSet, error) + Record(userID, channelID, replyTo, lastMessageID uint64, count uint32) error Inc(channelID, userID uint64) error Dec(channelID, userID uint64) error } @@ -26,17 +26,17 @@ type ( const ( // Fetching channel members of all channels a specific user has access to - sqlChannelViewsSelect = `SELECT rel_channel, rel_user, new_messages_count, rel_last_message_id - FROM channel_views + sqlChannelViewsSelect = `SELECT rel_channel, rel_user, count, rel_last_message + FROM unreads WHERE true ` - sqlChannelViewsIncCount = `UPDATE channel_views - SET new_messages_count = new_messages_count + 1 + sqlChannelViewsIncCount = `UPDATE unreads + SET count = count + 1 WHERE rel_channel = ? AND rel_user <> ?` - sqlChannelViewsDecCount = `UPDATE channel_views - SET new_messages_count = new_messages_count - 1 - WHERE rel_channel = ? AND rel_user <> ? AND new_messages_count > 0` + sqlChannelViewsDecCount = `UPDATE unreads + SET count = count - 1 + WHERE rel_channel = ? AND rel_user <> ? AND count > 0` ) // ChannelView creates new instance of channel member repository @@ -55,9 +55,9 @@ func (r *channelViews) With(ctx context.Context, db *factory.DB) ChannelViewRepo // // If channelID > 0 it returns members of a specific channel // If userID > 0 it returns members of all channels this user is member of -func (r *channelViews) Find(filter *types.ChannelViewFilter) (types.ChannelViewSet, error) { +func (r *channelViews) Find(filter *types.UnreadFilter) (types.UnreadSet, error) { params := make([]interface{}, 0) - vv := types.ChannelViewSet{} + vv := types.UnreadSet{} sql := sqlChannelViewsSelect if filter != nil { @@ -72,15 +72,16 @@ func (r *channelViews) Find(filter *types.ChannelViewFilter) (types.ChannelViewS } // Records channel view -func (r *channelViews) Record(userID, channelID, lastMessageID uint64, count uint32) error { - mod := &types.ChannelView{ - ChannelID: channelID, - UserID: userID, - LastMessageID: lastMessageID, - NewMessagesCount: count, +func (r *channelViews) Record(userID, channelID, replyTo, lastMessageID uint64, count uint32) error { + mod := &types.Unread{ + ChannelID: channelID, + UserID: userID, + ReplyTo: replyTo, + LastMessageID: lastMessageID, + Count: count, } - return r.db().Replace("channel_views", mod) + return r.db().Replace("unreads", mod) } // Increments unread (new) message count on a channel for all but one user diff --git a/sam/rest/handlers/message.go b/sam/rest/handlers/message.go index 1bf1230c0..0858abcfb 100644 --- a/sam/rest/handlers/message.go +++ b/sam/rest/handlers/message.go @@ -33,6 +33,7 @@ type MessageAPI interface { Delete(context.Context, *request.MessageDelete) (interface{}, error) ReplyGet(context.Context, *request.MessageReplyGet) (interface{}, error) ReplyCreate(context.Context, *request.MessageReplyCreate) (interface{}, error) + MarkAsUnread(context.Context, *request.MessageMarkAsUnread) (interface{}, error) PinCreate(context.Context, *request.MessagePinCreate) (interface{}, error) PinRemove(context.Context, *request.MessagePinRemove) (interface{}, error) BookmarkCreate(context.Context, *request.MessageBookmarkCreate) (interface{}, error) @@ -49,6 +50,7 @@ type Message struct { Delete func(http.ResponseWriter, *http.Request) ReplyGet func(http.ResponseWriter, *http.Request) ReplyCreate func(http.ResponseWriter, *http.Request) + MarkAsUnread func(http.ResponseWriter, *http.Request) PinCreate func(http.ResponseWriter, *http.Request) PinRemove func(http.ResponseWriter, *http.Request) BookmarkCreate func(http.ResponseWriter, *http.Request) @@ -101,6 +103,13 @@ func NewMessage(mh MessageAPI) *Message { return mh.ReplyCreate(r.Context(), params) }) }, + MarkAsUnread: func(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + params := request.NewMessageMarkAsUnread() + resputil.JSON(w, params.Fill(r), func() (interface{}, error) { + return mh.MarkAsUnread(r.Context(), params) + }) + }, PinCreate: func(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() params := request.NewMessagePinCreate() @@ -156,6 +165,7 @@ func (mh *Message) MountRoutes(r chi.Router, middlewares ...func(http.Handler) h r.Delete("/{messageID}", mh.Delete) r.Get("/{messageID}/replies", mh.ReplyGet) r.Post("/{messageID}/replies", mh.ReplyCreate) + r.Post("/{messageID}/unread", mh.MarkAsUnread) r.Post("/{messageID}/pin", mh.PinCreate) r.Delete("/{messageID}/pin", mh.PinRemove) r.Post("/{messageID}/bookmark", mh.BookmarkCreate) diff --git a/sam/rest/message.go b/sam/rest/message.go index d449aef25..1ed4db8d8 100644 --- a/sam/rest/message.go +++ b/sam/rest/message.go @@ -69,6 +69,10 @@ func (ctrl *Message) Delete(ctx context.Context, r *request.MessageDelete) (inte return nil, ctrl.svc.msg.With(ctx).Delete(r.MessageID) } +func (ctrl *Message) MarkAsUnread(ctx context.Context, r *request.MessageMarkAsUnread) (interface{}, error) { + return nil, ctrl.svc.msg.With(ctx).MarkAsUnread(r.MessageID) +} + func (ctrl *Message) PinCreate(ctx context.Context, r *request.MessagePinCreate) (interface{}, error) { return nil, ctrl.svc.msg.With(ctx).Pin(r.MessageID) } diff --git a/sam/rest/request/message.go b/sam/rest/request/message.go index a430fc5d3..65716221e 100644 --- a/sam/rest/request/message.go +++ b/sam/rest/request/message.go @@ -316,6 +316,51 @@ func (m *MessageReplyCreate) Fill(r *http.Request) (err error) { var _ RequestFiller = NewMessageReplyCreate() +// Message markAsUnread request parameters +type MessageMarkAsUnread struct { + MessageID uint64 `json:",string"` + ChannelID uint64 `json:",string"` +} + +func NewMessageMarkAsUnread() *MessageMarkAsUnread { + return &MessageMarkAsUnread{} +} + +func (m *MessageMarkAsUnread) Fill(r *http.Request) (err error) { + if strings.ToLower(r.Header.Get("content-type")) == "application/json" { + err = json.NewDecoder(r.Body).Decode(m) + + switch { + case err == io.EOF: + err = nil + case err != nil: + return errors.Wrap(err, "error parsing http request body") + } + } + + if err = r.ParseForm(); err != nil { + return err + } + + get := map[string]string{} + post := map[string]string{} + urlQuery := r.URL.Query() + for name, param := range urlQuery { + get[name] = string(param[0]) + } + postVars := r.Form + for name, param := range postVars { + post[name] = string(param[0]) + } + + m.MessageID = parseUInt64(chi.URLParam(r, "messageID")) + m.ChannelID = parseUInt64(chi.URLParam(r, "channelID")) + + return err +} + +var _ RequestFiller = NewMessageMarkAsUnread() + // Message pinCreate request parameters type MessagePinCreate struct { MessageID uint64 `json:",string"` diff --git a/sam/service/channel.go b/sam/service/channel.go index dbbc0b475..477a1baaa 100644 --- a/sam/service/channel.go +++ b/sam/service/channel.go @@ -5,7 +5,6 @@ import ( "fmt" "time" - "github.com/davecgh/go-spew/spew" "github.com/pkg/errors" "github.com/crusttech/crust/internal/auth" @@ -147,7 +146,6 @@ func (svc *channel) preloadMembers(cc types.ChannelSet) (err error) { if mm, err = svc.cmember.Find(&types.ChannelMemberFilter{ComembersOf: userID}); err != nil { return } else { - spew.Dump(mm) err = cc.Walk(func(ch *types.Channel) error { ch.Members = mm.MembersOf(ch.ID) ch.Member = mm.FindByChannelID(ch.ID).FindByUserID(userID) @@ -161,11 +159,11 @@ func (svc *channel) preloadMembers(cc types.ChannelSet) (err error) { func (svc *channel) preloadViews(cc types.ChannelSet) error { var userID = auth.GetIdentityFromContext(svc.ctx).Identity() - if vv, err := svc.cview.Find(&types.ChannelViewFilter{UserID: userID}); err != nil { + if vv, err := svc.cview.Find(&types.UnreadFilter{UserID: userID}); err != nil { return err } else { cc.Walk(func(ch *types.Channel) error { - ch.View = vv.FindByChannelId(ch.ID) + ch.Unread = vv.FindByChannelId(ch.ID) return nil }) } @@ -744,7 +742,7 @@ func (svc *channel) DeleteMember(channelID uint64, memberIDs ...uint64) (err err func (svc *channel) RecordView(userID, channelID, lastMessageID uint64) error { return svc.db.Transaction(func() (err error) { - return svc.cview.Record(userID, channelID, lastMessageID, 0) + return svc.cview.Record(userID, channelID, lastMessageID, 0, 0) }) } diff --git a/sam/service/message.go b/sam/service/message.go index 676be81ca..77c20d69e 100644 --- a/sam/service/message.go +++ b/sam/service/message.go @@ -43,6 +43,8 @@ type ( React(messageID uint64, reaction string) error RemoveReaction(messageID uint64, reaction string) error + MarkAsUnread(messageID uint64) error + Pin(messageID uint64) error RemovePin(messageID uint64) error @@ -306,6 +308,27 @@ func (svc *message) Delete(ID uint64) error { }) } +// Pin message to the channel +func (svc *message) MarkAsUnread(messageID uint64) error { + var currentUserID uint64 = repository.Identity(svc.ctx) + + return svc.db.Transaction(func() (err error) { + // Broadcast queue + var message *types.Message + + message, err = svc.message.FindMessageByID(messageID) + if err != nil { + return err + } + + if message.ReplyTo > 0 { + return svc.cview.Record(currentUserID, message.ChannelID, message.ReplyTo, messageID, 0) + } else { + return svc.cview.Record(currentUserID, message.ChannelID, 0, messageID, 0) + } + }) +} + // React on a message with an emoji func (svc *message) React(messageID uint64, reaction string) error { return svc.flag(messageID, reaction, false) diff --git a/sam/types/channel.go b/sam/types/channel.go index 93abd3c06..3e33f3dd1 100644 --- a/sam/types/channel.go +++ b/sam/types/channel.go @@ -37,7 +37,7 @@ type ( Member *ChannelMember `json:"-" db:"-"` Members []uint64 `json:"-" db:"-"` - View *ChannelView `json:"-" db:"-"` + Unread *Unread `json:"-" db:"-"` } ChannelFilter struct { diff --git a/sam/types/channel_view.go b/sam/types/channel_view.go deleted file mode 100644 index 444fbe8b1..000000000 --- a/sam/types/channel_view.go +++ /dev/null @@ -1,37 +0,0 @@ -package types - -type ( - ChannelView struct { - ChannelID uint64 `db:"rel_channel"` - UserID uint64 `db:"rel_user"` - LastMessageID uint64 `db:"rel_last_message_id"` - - NewMessagesCount uint32 `db:"new_messages_count"` - } - - ChannelViewFilter struct { - UserID uint64 - } - - ChannelViewSet []*ChannelView -) - -func (mm ChannelViewSet) Walk(w func(*ChannelView) error) (err error) { - for i := range mm { - if err = w(mm[i]); err != nil { - return - } - } - - return -} - -func (uu ChannelViewSet) FindByChannelId(channelID uint64) *ChannelView { - for i := range uu { - if uu[i].ChannelID == channelID { - return uu[i] - } - } - - return nil -} diff --git a/sam/types/unread.go b/sam/types/unread.go new file mode 100644 index 000000000..24c52a300 --- /dev/null +++ b/sam/types/unread.go @@ -0,0 +1,38 @@ +package types + +type ( + Unread struct { + ChannelID uint64 `db:"rel_channel"` + ReplyTo uint64 `db:"rel_reply_to"` + UserID uint64 `db:"rel_user"` + LastMessageID uint64 `db:"rel_last_message"` + + Count uint32 `db:"count"` + } + + UnreadFilter struct { + UserID uint64 + } + + UnreadSet []*Unread +) + +func (mm UnreadSet) Walk(w func(*Unread) error) (err error) { + for i := range mm { + if err = w(mm[i]); err != nil { + return + } + } + + return +} + +func (uu UnreadSet) FindByChannelId(channelID uint64) *Unread { + for i := range uu { + if uu[i].ChannelID == channelID { + return uu[i] + } + } + + return nil +}