Skip to content

Commit

Permalink
Move ownership of chit balance and streak to users (#5972)
Browse files Browse the repository at this point in the history
  • Loading branch information
megrogan authored Jul 1, 2024
1 parent 06457aa commit 4663167
Show file tree
Hide file tree
Showing 30 changed files with 476 additions and 84 deletions.
1 change: 1 addition & 0 deletions backend/canisters/user/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Changed

- Added `achievements` ([#5962](https://github.com/open-chat-labs/open-chat/pull/5962))
- Maintains chit balance and streak and notifies user_index ([#5972](https://github.com/open-chat-labs/open-chat/pull/5972))

## [[2.0.1213](https://github.com/open-chat-labs/open-chat/releases/tag/v2.0.1213-user)] - 2024-06-24

Expand Down
19 changes: 19 additions & 0 deletions backend/canisters/user/api/can.did
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,16 @@ type JoinVideoCallResponse = variant {
ChatNotFound;
};

type ClaimDailyChitResponse = variant {
Success : record {
chit_earned : nat32;
chit_balance : int32;
streak : nat16;
next_claim : TimestampMillis;
};
AlreadyClaimed : TimestampMillis;
};

type TipMessageArgs = record {
chat : Chat;
recipient : UserId;
Expand Down Expand Up @@ -867,6 +877,10 @@ type InitialStateResponse = variant {
local_user_index_canister_id : CanisterId;
achievements : vec ChitEarned;
achievements_last_seen : TimestampMillis;
chit_balance : int32;
streak : nat16;
streak_ends : TimestampMillis;
next_daily_claim : TimestampMillis;
};
};

Expand Down Expand Up @@ -923,6 +937,10 @@ type UpdatesResponse = variant {
suspended : opt bool;
achievements : vec ChitEarned;
achievements_last_seen : opt TimestampMillis;
chit_balance : int32;
streak : nat16;
streak_ends : TimestampMillis;
next_daily_claim : TimestampMillis;
};
SuccessNoUpdates;
};
Expand Down Expand Up @@ -1209,6 +1227,7 @@ service : {
start_video_call : (StartVideoCallArgs) -> (StartVideoCallResponse);
join_video_call : (JoinVideoCallArgs) -> (JoinVideoCallResponse);
end_video_call : (EndVideoCallArgs) -> (EndVideoCallResponse);
claim_daily_chit : (EmptyArgs) -> (ClaimDailyChitResponse);

events : (EventsArgs) -> (EventsResponse) query;
events_by_index : (EventsByIndexArgs) -> (EventsResponse) query;
Expand Down
1 change: 1 addition & 0 deletions backend/canisters/user/api/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ fn main() {
generate_candid_method!(user, block_user, update);
generate_candid_method!(user, cancel_message_reminder, update);
generate_candid_method!(user, cancel_p2p_swap, update);
generate_candid_method!(user, claim_daily_chit, update);
generate_candid_method!(user, create_community, update);
generate_candid_method!(user, create_group, update);
generate_candid_method!(user, delete_community, update);
Expand Down
4 changes: 4 additions & 0 deletions backend/canisters/user/api/src/queries/initial_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ pub struct SuccessResult {
pub local_user_index_canister_id: CanisterId,
pub achievements: Vec<ChitEarned>,
pub achievements_last_seen: TimestampMillis,
pub chit_balance: i32,
pub streak: u16,
pub streak_ends: TimestampMillis,
pub next_daily_claim: TimestampMillis,
}

#[derive(CandidType, Serialize, Deserialize, Debug)]
Expand Down
4 changes: 4 additions & 0 deletions backend/canisters/user/api/src/queries/updates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ pub struct SuccessResult {
pub pin_number_settings: OptionUpdate<PinNumberSettings>,
pub achievements: Vec<ChitEarned>,
pub achievements_last_seen: Option<TimestampMillis>,
pub chit_balance: i32,
pub streak: u16,
pub streak_ends: TimestampMillis,
pub next_daily_claim: TimestampMillis,
}

#[derive(CandidType, Serialize, Deserialize, Clone, Debug)]
Expand Down
19 changes: 19 additions & 0 deletions backend/canisters/user/api/src/updates/claim_daily_chit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use candid::CandidType;
use serde::{Deserialize, Serialize};
use types::{Empty, TimestampMillis};

pub type Args = Empty;

#[derive(CandidType, Serialize, Deserialize, Debug)]
pub enum Response {
Success(SuccessResult),
AlreadyClaimed(TimestampMillis),
}

#[derive(CandidType, Serialize, Deserialize, Debug)]
pub struct SuccessResult {
pub chit_earned: u32,
pub chit_balance: i32,
pub streak: u16,
pub next_claim: TimestampMillis,
}
1 change: 1 addition & 0 deletions backend/canisters/user/api/src/updates/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub mod c2c_set_user_suspended;
pub mod c2c_vote_on_proposal;
pub mod cancel_message_reminder;
pub mod cancel_p2p_swap;
pub mod claim_daily_chit;
pub mod create_community;
pub mod create_group;
pub mod delete_community;
Expand Down
86 changes: 74 additions & 12 deletions backend/canisters/user/impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use notifications_canister::c2c_push_notification;
use serde::{Deserialize, Serialize};
use serde_bytes::ByteBuf;
use std::cell::RefCell;
use std::cmp::max;
use std::collections::HashSet;
use std::ops::Deref;
use std::time::Duration;
Expand All @@ -33,7 +34,8 @@ use user_canister::{NamedAccount, UserCanisterEvent};
use utils::canister_event_sync_queue::CanisterEventSyncQueue;
use utils::env::Environment;
use utils::regular_jobs::RegularJobs;
use utils::time::MINUTE_IN_MS;
use utils::streak::Streak;
use utils::time::{today, tomorrow, MINUTE_IN_MS};

mod crypto;
mod governance_clients;
Expand Down Expand Up @@ -146,10 +148,11 @@ impl RuntimeState {
}

pub fn metrics(&self) -> Metrics {
let now = self.env.now();
Metrics {
heap_memory_used: utils::memory::heap(),
stable_memory_used: utils::memory::stable(),
now: self.env.now(),
now,
cycles_balance: self.env.cycles_balance(),
wasm_version: WASM_VERSION.with_borrow(|v| **v),
git_commit_id: utils::git::git_commit_id().to_string(),
Expand All @@ -171,16 +174,11 @@ impl RuntimeState {
escrow: self.data.escrow_canister_id,
icp_ledger: Cryptocurrency::InternetComputer.ledger_canister_id().unwrap(),
},
}
}

pub fn insert_achievement(&mut self, achievement: Achievement) {
if self.data.achievements.insert(achievement.clone()) {
self.data.chit_events.push(ChitEarned {
amount: achievement.chit_reward() as i32,
timestamp: self.env.now(),
reason: ChitEarnedReason::Achievement(achievement),
});
chit_balance: self.data.chit_balance.value,
streak: self.data.streak.days(now),
streak_ends: self.data.streak.ends(),
next_daily_claim: if self.data.streak.can_claim(now) { today(now) } else { tomorrow(now) },
achievements: self.data.achievements.iter().cloned().collect(),
}
}
}
Expand Down Expand Up @@ -225,6 +223,10 @@ struct Data {
pub btc_address: Option<String>,
pub chit_events: ChitEarnedEvents,
#[serde(default)]
pub chit_balance: Timestamped<i32>,
#[serde(default)]
pub streak: Streak,
#[serde(default)]
pub achievements: HashSet<Achievement>,
#[serde(default)]
pub achievements_last_seen: TimestampMillis,
Expand Down Expand Up @@ -286,6 +288,8 @@ impl Data {
pin_number: PinNumber::default(),
btc_address: None,
chit_events: ChitEarnedEvents::default(),
chit_balance: Timestamped::default(),
streak: Streak::default(),
achievements: HashSet::new(),
achievements_last_seen: 0,
rng_seed: [0; 32],
Expand Down Expand Up @@ -331,6 +335,59 @@ impl Data {
timer_jobs.enqueue_job(TimerJob::RemoveExpiredEvents(RemoveExpiredEventsJob), expiry, now);
}
}

pub fn award_achievement_and_notify(&mut self, achievement: Achievement, now: TimestampMillis) {
if self.award_achievement(achievement, now) {
self.notify_user_index_of_chit(now);
}
}

pub fn award_achievement(&mut self, achievement: Achievement, now: TimestampMillis) -> bool {
if self.achievements.insert(achievement.clone()) {
let amount = achievement.chit_reward() as i32;
self.chit_events.push(ChitEarned {
amount,
timestamp: now,
reason: ChitEarnedReason::Achievement(achievement),
});
self.chit_balance = Timestamped::new(self.chit_balance.value + amount, now);
true
} else {
false
}
}

pub fn notify_user_index_of_chit(&mut self, now: TimestampMillis) {
let args = user_index_canister::c2c_notify_chit::Args {
timestamp: now,
chit_balance: self.chit_balance.value,
streak: self.streak.days(now),
streak_ends: self.streak.ends(),
};

self.fire_and_forget_handler.send(
self.user_index_canister_id,
"c2c_notify_chit_msgpack".to_string(),
msgpack::serialize_then_unwrap(args),
);
}

pub fn init_streak_and_chit_balance(&mut self, now: TimestampMillis) -> u16 {
let mut max_streak: u16 = 0;
self.chit_balance = Timestamped::new(0, now);

for event in self.chit_events.iter() {
self.chit_balance = Timestamped::new(self.chit_balance.value + event.amount, now);

let is_daily_claim = matches!(event.reason, ChitEarnedReason::DailyClaim);

if is_daily_claim && self.streak.claim(event.timestamp) {
max_streak = max(max_streak, self.streak.days(event.timestamp))
}
}

max_streak
}
}

#[derive(Serialize, Debug)]
Expand All @@ -351,6 +408,11 @@ pub struct Metrics {
pub video_call_operators: Vec<Principal>,
pub timer_jobs: u32,
pub canister_ids: CanisterIds,
pub chit_balance: i32,
pub streak: u16,
pub streak_ends: TimestampMillis,
pub next_daily_claim: TimestampMillis,
pub achievements: Vec<Achievement>,
}

fn run_regular_jobs() {
Expand Down
44 changes: 27 additions & 17 deletions backend/canisters/user/impl/src/lifecycle/post_upgrade.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ fn post_upgrade(args: Args) {
}
});

mutate_state(initialize_achievements);
mutate_state(initialize_chit_and_achievements);
}

fn mark_user_canister_empty() {
Expand All @@ -53,18 +53,18 @@ fn mark_user_canister_empty() {
})
}

fn initialize_achievements(state: &mut RuntimeState) {
let longest_streak = state.data.chit_events.init_streak();

fn initialize_chit_and_achievements(state: &mut RuntimeState) {
let me: UserId = state.env.canister_id().into();
let now = state.env.now();

let longest_streak = state.data.init_streak_and_chit_balance(now);

if state.data.group_chats.len() > 0 {
state.insert_achievement(Achievement::JoinedGroup);
state.data.award_achievement(Achievement::JoinedGroup, now);
}

if state.data.communities.len() > 0 {
state.insert_achievement(Achievement::JoinedCommunity);
state.data.award_achievement(Achievement::JoinedCommunity, now);
}

if state.data.direct_chats.iter().any(|c| {
Expand All @@ -73,7 +73,7 @@ fn initialize_achievements(state: &mut RuntimeState) {
.iter_latest_messages(None)
.any(|m| m.event.sender == me)
}) {
state.insert_achievement(Achievement::SentDirectMessage);
state.data.award_achievement(Achievement::SentDirectMessage, now);
}

if state.data.direct_chats.iter().any(|c| {
Expand All @@ -82,42 +82,52 @@ fn initialize_achievements(state: &mut RuntimeState) {
.iter_latest_messages(None)
.any(|m| m.event.sender == c.them)
}) {
state.insert_achievement(Achievement::ReceivedDirectMessage);
state.data.award_achievement(Achievement::ReceivedDirectMessage, now);
}

if state.data.avatar.value.is_some() {
state.insert_achievement(Achievement::SetAvatar);
state.data.award_achievement(Achievement::SetAvatar, now);
}

if !state.data.bio.value.is_empty() {
state.insert_achievement(Achievement::SetBio);
state.data.award_achievement(Achievement::SetBio, now);
}

if state.data.display_name.value.is_some() {
state.insert_achievement(Achievement::SetDisplayName);
state.data.award_achievement(Achievement::SetDisplayName, now);
}

if let Some(diamond_expires) = state.data.diamond_membership_expires_at {
state.insert_achievement(Achievement::UpgradedToDiamond);
state.data.award_achievement(Achievement::UpgradedToDiamond, now);

if (diamond_expires - now) > (5 * 365 * DAY_IN_MS) {
state.insert_achievement(Achievement::UpgradedToGoldDiamond);
state.data.award_achievement(Achievement::UpgradedToGoldDiamond, now);
}
}

if longest_streak >= 3 {
state.insert_achievement(Achievement::Streak3);
state.data.award_achievement(Achievement::Streak3, now);
}

if longest_streak >= 7 {
state.insert_achievement(Achievement::Streak7);
state.data.award_achievement(Achievement::Streak7, now);
}

if longest_streak >= 14 {
state.insert_achievement(Achievement::Streak14);
state.data.award_achievement(Achievement::Streak14, now);
}

if longest_streak >= 30 {
state.insert_achievement(Achievement::Streak30);
state.data.award_achievement(Achievement::Streak30, now);
}

if state.data.chit_balance.value > 0 {
ic_cdk_timers::set_timer(Duration::ZERO, notify_user_index_of_chit);
}
}

fn notify_user_index_of_chit() {
mutate_state(|state| {
state.data.notify_user_index_of_chit(state.env.now());
})
}
Loading

0 comments on commit 4663167

Please sign in to comment.