Skip to content

Commit

Permalink
Add support for a message activity feed
Browse files Browse the repository at this point in the history
  • Loading branch information
megrogan committed Oct 9, 2024
1 parent fc74ff8 commit d986594
Show file tree
Hide file tree
Showing 35 changed files with 488 additions and 70 deletions.
6 changes: 5 additions & 1 deletion backend/canisters/user/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## [unreleased]

### Added

- Add support for expiring access gates ([#6401](https://github.com/open-chat-labs/open-chat/pull/6401))
- Add support for a message activity feed ([#6539](https://github.com/open-chat-labs/open-chat/pull/6539))

## [[2.0.1374](https://github.com/open-chat-labs/open-chat/releases/tag/v2.0.1374-user)] - 2024-10-07

### Added
Expand All @@ -24,7 +29,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Refactor transfers to differentiate between transfers that failed due to c2c error vs transfer error ([#6500](https://github.com/open-chat-labs/open-chat/pull/6500))
- Refactor ICPSwap and Sonic swap clients ([#6505](https://github.com/open-chat-labs/open-chat/pull/6505))
- Award more daily CHIT when on 100 or 365 day streaks ([#6522](https://github.com/open-chat-labs/open-chat/pull/6522))
- Add support for expiring access gates ([#6401](https://github.com/open-chat-labs/open-chat/pull/6401))

### Fixed

Expand Down
50 changes: 50 additions & 0 deletions backend/canisters/user/api/can.did
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,14 @@ type MarkAchievementsSeenResponse = variant {
Success;
};

type MarkMessageActivityFeedReadArgs = record {
read_up_to : TimestampMillis;
};

type MarkMessageActivityFeedReadResponse = variant {
Success;
};

type MarkReadArgs = record {
messages_read : vec ChatMessagesRead;
community_messages_read : vec CommunityMessagesRead;
Expand Down Expand Up @@ -918,6 +926,7 @@ type InitialStateResponse = variant {
is_unique_person : bool;
wallet_config: WalletConfig;
referrals : vec Referral;
message_activity_summary : MessageActivitySummary;
};
};

Expand Down Expand Up @@ -981,10 +990,17 @@ type UpdatesResponse = variant {
is_unique_person : opt bool;
wallet_config: opt WalletConfig;
referrals : vec Referral;
message_activity_summary : opt MessageActivitySummary;
};
SuccessNoUpdates;
};

type MessageActivitySummary = record {
read_up_to : TimestampMillis;
latest_event : TimestampMillis;
unread_count : nat32;
};

type DirectChatsUpdates = record {
added : vec DirectChatSummary;
updated : vec DirectChatSummaryUpdates;
Expand Down Expand Up @@ -1248,6 +1264,38 @@ type Referral = record {
status : ReferralStatus;
};

type MessageActivityFeedArgs = record {
since : TimestampMillis;
max : nat32;
};

type MessageActivityFeedResponse = variant {
Success: record {
events : vec MessageActivityEvent;
total : nat32;
};
};

type MessageActivityEvent = record {
chat : Chat;
thread_root_message_index : opt MessageIndex;
message_index : MessageIndex;
activity : MessageActivity;
timestamp : TimestampMillis;
user_id : UserId;
};

type MessageActivity = variant {
Mention;
Reaction;
QuoteReply;
ThreadReply;
Tip;
Crypto;
PollVote;
P2PSwapAccepted;
};

service : {
send_message_v2 : (SendMessageV2Args) -> (SendMessageResponse);
edit_message_v2 : (EditMessageV2Args) -> (EditMessageResponse);
Expand All @@ -1257,6 +1305,7 @@ service : {
remove_reaction : (RemoveReactionArgs) -> (RemoveReactionResponse);
tip_message : (TipMessageArgs) -> (TipMessageResponse);
mark_achievements_seen : (MarkAchievementsSeenArgs) -> (MarkAchievementsSeenResponse);
mark_message_activity_feed_read : (MarkMessageActivityFeedReadArgs) -> (MarkMessageActivityFeedReadResponse);
mark_read : (MarkReadArgs) -> (MarkReadResponse);
block_user : (BlockUserArgs) -> (BlockUserResponse);
unblock_user : (UnblockUserArgs) -> (UnblockUserResponse);
Expand Down Expand Up @@ -1314,6 +1363,7 @@ service : {
token_swap_status : (TokenSwapStatusArgs) -> (TokenSwapStatusResponse) query;
local_user_index : (EmptyArgs) -> (LocalUserIndexResponse) query;
chit_events : (ChitEventsArgs) -> (ChitEventsResponse) query;
message_activity_feed : (MessageActivityFeedArgs) -> (MessageActivityFeedResponse) query;

cached_btc_address : (EmptyArgs) -> (CachedBtcAddressResponse) query;
btc_address : (EmptyArgs) -> (BtcAddressResponse);
Expand Down
53 changes: 52 additions & 1 deletion backend/canisters/user/api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,8 +356,59 @@ pub struct Referrals {
pub referrals: Vec<Referral>,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
#[derive(CandidType, Serialize, Deserialize, Clone, Debug)]
pub struct ExternalAchievementAwarded {
pub name: String,
pub chit_reward: u32,
}

#[ts_export(user)]
#[derive(CandidType, Serialize, Deserialize, Clone, Debug)]
pub struct MessageActivityEvent {
pub chat: Chat,
pub thread_root_message_index: Option<MessageIndex>,
pub message_index: MessageIndex,
pub activity: MessageActivity,
pub timestamp: TimestampMillis,
pub user_id: UserId,
}

impl MessageActivityEvent {
pub fn matches(&self, event: &MessageActivityEvent) -> bool {
self.chat == event.chat
&& self.thread_root_message_index == event.thread_root_message_index
&& self.message_index == event.message_index
&& self.activity == event.activity
}
}

#[ts_export(user)]
#[derive(CandidType, Serialize, Deserialize, Clone, Eq, PartialEq, Debug)]
pub enum MessageActivity {
Mention,
Reaction,
QuoteReply,
ThreadReply,
Tip,
Crypto,
PollVote,
P2PSwapAccepted,
}

#[ts_export(user)]
#[derive(CandidType, Serialize, Deserialize, Debug)]
pub struct MessageActivitySummary {
pub read_up_to: TimestampMillis,
pub latest_event: TimestampMillis,
pub unread_count: u32,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum CommunityCanisterEvent {
MessageActivity(MessageActivityEvent),
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum GroupCanisterEvent {
MessageActivity(MessageActivityEvent),
}
2 changes: 2 additions & 0 deletions backend/canisters/user/api/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use ts_export::generate_ts_method;

#[allow(deprecated)]
fn main() {
generate_candid_method!(user, message_activity_feed, query);
generate_candid_method!(user, bio, query);
generate_candid_method!(user, cached_btc_address, query);
generate_candid_method!(user, chit_events, query);
Expand Down Expand Up @@ -46,6 +47,7 @@ fn main() {
generate_candid_method!(user, leave_group, update);
generate_candid_method!(user, manage_favourite_chats, update);
generate_candid_method!(user, mark_achievements_seen, update);
generate_candid_method!(user, mark_message_activity_feed_read, update);
generate_candid_method!(user, mark_read, update);
generate_candid_method!(user, mute_notifications, update);
generate_candid_method!(user, pin_chat_v2, update);
Expand Down
3 changes: 2 additions & 1 deletion backend/canisters/user/api/src/queries/initial_state.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{Referral, WalletConfig};
use crate::{MessageActivitySummary, Referral, WalletConfig};
use candid::CandidType;
use serde::{Deserialize, Serialize};
use ts_export::ts_export;
Expand Down Expand Up @@ -38,6 +38,7 @@ pub struct SuccessResult {
pub is_unique_person: bool,
pub wallet_config: WalletConfig,
pub referrals: Vec<Referral>,
pub message_activity_summary: MessageActivitySummary,
}

#[ts_export(user, initial_state)]
Expand Down
26 changes: 26 additions & 0 deletions backend/canisters/user/api/src/queries/message_activity_feed.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use candid::CandidType;
use serde::{Deserialize, Serialize};
use ts_export::ts_export;
use types::TimestampMillis;

use crate::MessageActivityEvent;

#[ts_export(user, activity_feed)]
#[derive(CandidType, Serialize, Deserialize, Debug)]
pub struct Args {
pub since: TimestampMillis,
pub max: u32,
}

#[ts_export(user, activity_feed)]
#[derive(CandidType, Serialize, Deserialize, Debug)]
pub enum Response {
Success(SuccessResult),
}

#[ts_export(user, activity_feed)]
#[derive(CandidType, Serialize, Deserialize, Debug)]
pub struct SuccessResult {
pub events: Vec<MessageActivityEvent>,
pub total: u32,
}
1 change: 1 addition & 0 deletions backend/canisters/user/api/src/queries/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub mod events_window;
pub mod hot_group_exclusions;
pub mod initial_state;
pub mod local_user_index;
pub mod message_activity_feed;
pub mod messages_by_message_index;
pub mod public_profile;
pub mod saved_crypto_accounts;
Expand Down
3 changes: 2 additions & 1 deletion backend/canisters/user/api/src/queries/updates.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{Referral, WalletConfig};
use crate::{MessageActivitySummary, Referral, WalletConfig};
use candid::CandidType;
use serde::{Deserialize, Serialize};
use ts_export::ts_export;
Expand Down Expand Up @@ -48,6 +48,7 @@ pub struct SuccessResult {
pub is_unique_person: Option<bool>,
pub wallet_config: Option<WalletConfig>,
pub referrals: Vec<Referral>,
pub message_activity_summary: Option<MessageActivitySummary>,
}

#[ts_export(user, updates)]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use crate::CommunityCanisterEvent;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug)]
pub struct Args {
pub events: Vec<CommunityCanisterEvent>,
}

#[derive(Serialize, Deserialize, Debug)]
pub enum Response {
Success,
Blocked,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use crate::GroupCanisterEvent;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug)]
pub struct Args {
pub events: Vec<GroupCanisterEvent>,
}

#[derive(Serialize, Deserialize, Debug)]
pub enum Response {
Success,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use candid::CandidType;
use serde::{Deserialize, Serialize};
use ts_export::ts_export;
use types::TimestampMillis;

#[ts_export(user, mark_read)]
#[derive(CandidType, Serialize, Deserialize, Debug)]
pub struct Args {
pub read_up_to: TimestampMillis,
}

#[ts_export(user, mark_read)]
#[derive(CandidType, Serialize, Deserialize, Debug)]
pub enum Response {
Success,
}
3 changes: 3 additions & 0 deletions backend/canisters/user/api/src/updates/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ pub mod c2c_handle_bot_messages;
pub mod c2c_mark_community_updated_for_user;
pub mod c2c_mark_group_updated_for_user;
pub mod c2c_notify_achievement;
pub mod c2c_notify_community_canister_events;
pub mod c2c_notify_community_deleted;
pub mod c2c_notify_events;
pub mod c2c_notify_group_canister_events;
pub mod c2c_notify_group_deleted;
pub mod c2c_notify_user_canister_events;
pub mod c2c_remove_from_community;
Expand All @@ -38,6 +40,7 @@ pub mod leave_community;
pub mod leave_group;
pub mod manage_favourite_chats;
pub mod mark_achievements_seen;
pub mod mark_message_activity_feed_read;
pub mod mark_read;
pub mod mute_notifications;
pub mod pin_chat_v2;
Expand Down
16 changes: 16 additions & 0 deletions backend/canisters/user/impl/src/guards.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,27 @@ pub fn caller_is_escrow_canister() -> Result<(), String> {
pub fn caller_is_known_group_or_community_canister() -> Result<(), String> {
if read_state(|state| state.is_caller_known_group_canister() || state.is_caller_known_community_canister()) {
Ok(())
} else {
Err("Caller is not a known group or community canister".to_owned())
}
}

pub fn caller_is_known_group_canister() -> Result<(), String> {
if read_state(|state| state.is_caller_known_group_canister()) {
Ok(())
} else {
Err("Caller is not a known group canister".to_owned())
}
}

pub fn caller_is_known_community_canister() -> Result<(), String> {
if read_state(|state| state.is_caller_known_community_canister()) {
Ok(())
} else {
Err("Caller is not a known community canister".to_owned())
}
}

pub fn caller_is_video_call_operator() -> Result<(), String> {
if read_state(|state| state.is_caller_video_call_operator()) {
Ok(())
Expand Down
8 changes: 4 additions & 4 deletions backend/canisters/user/impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use fire_and_forget_handler::FireAndForgetHandler;
use model::chit::ChitEarnedEvents;
use model::contacts::Contacts;
use model::favourite_chats::FavouriteChats;
use model::message_activity_events::MessageActivityEvents;
use model::referrals::Referrals;
use model::streak::Streak;
use notifications_canister::c2c_push_notification;
Expand Down Expand Up @@ -243,17 +244,15 @@ struct Data {
pub chit_events: ChitEarnedEvents,
pub streak: Streak,
pub achievements: HashSet<Achievement>,
#[serde(default)]
pub external_achievements: HashSet<String>,
pub achievements_last_seen: TimestampMillis,
pub unique_person_proof: Option<UniquePersonProof>,
#[serde(default)]
pub wallet_config: Timestamped<WalletConfig>,
pub rng_seed: [u8; 32],
#[serde(default)]
pub referred_by: Option<UserId>,
#[serde(default)]
pub referrals: Referrals,
#[serde(default)]
pub message_activity_events: MessageActivityEvents,
}

impl Data {
Expand Down Expand Up @@ -321,6 +320,7 @@ impl Data {
wallet_config: Timestamped::default(),
referred_by,
referrals: Referrals::default(),
message_activity_events: MessageActivityEvents::default(),
}
}

Expand Down
Loading

0 comments on commit d986594

Please sign in to comment.