diff --git a/src/backend/env/features.rs b/src/backend/env/features.rs index 39393efc..e69966c3 100644 --- a/src/backend/env/features.rs +++ b/src/backend/env/features.rs @@ -16,6 +16,9 @@ use super::{ State, }; +const STATUS_IMPLEMENTED: u8 = 1; +const STATUS_OPEN: u8 = 0; + #[derive(Default, Serialize, Deserialize)] pub struct Feature { // Users who voted for increased priority @@ -33,7 +36,7 @@ pub fn features<'a>( now: Time, ) -> Box), Token, Feature)> + 'a> { let transform_feature = move |(post_id, feature): (&PostId, Feature)| { - if feature.last_activity + YEAR <= now { + if feature.status == STATUS_OPEN && feature.last_activity + YEAR <= now { return None; } let tokens = feature @@ -101,7 +104,7 @@ pub fn create_feature(caller: Principal, post_id: PostId, now: Time) -> Result<( post_id, Feature { supporters: Default::default(), - status: 0, + status: STATUS_OPEN, last_activity: now, }, ) @@ -121,7 +124,7 @@ pub fn create_feature(caller: Principal, post_id: PostId, now: Time) -> Result<( pub fn close_feature(state: &mut State, post_id: PostId) -> Result<(), String> { let mut feature = state.memory.features.remove(&post_id)?; - feature.status = 1; + feature.status = STATUS_IMPLEMENTED; state .memory .features diff --git a/src/backend/env/mod.rs b/src/backend/env/mod.rs index a565d167..a2fb3b25 100644 --- a/src/backend/env/mod.rs +++ b/src/backend/env/mod.rs @@ -311,19 +311,33 @@ impl State { return Err("seed too long".into()); } - let user = self.principal_to_user_mut(caller).ok_or("user not found")?; - user.change_credits( - CONFIG.feature_cost, - CreditsDelta::Minus, - "profile privacy change", - )?; - let len = user.posts.len(); + let (username, len, is_deactivated) = { + let user = self.principal_to_user_mut(caller).ok_or("user not found")?; + user.change_credits( + CONFIG.feature_cost, + CreditsDelta::Minus, + "profile privacy change", + )?; + let len = user.posts.len(); + let username = user.name.clone(); + user.deactivated = !user.deactivated; + let is_deactivated = user.deactivated; - user.deactivated = !user.deactivated; + for post_id in user.posts.clone() { + Post::crypt(self, post_id, &seed); + } - for post_id in user.posts.clone() { - Post::crypt(self, post_id, &seed); - } + (username, len, is_deactivated) + }; + + let _ = self.system_message( + if is_deactivated { + format!("Account @{} was deactivated. 😢", username) + } else { + format!("Account @{} is active again! 🎉", username) + }, + CONFIG.dao_realm.into(), + ); Ok(len) } @@ -802,7 +816,7 @@ impl State { let tipper_id = tipper.id; let tipper_name = tipper.name.clone(); // DoS protection - self.charge(tipper_id, CONFIG.tipping_cost, "tipping".to_string())?; // DoS protection + self.charge(tipper_id, CONFIG.tipping_cost, "tipping".to_string())?; let author_id = Post::get(self, &post_id).ok_or("post not found")?.user; let author = self.users.get(&author_id).ok_or("user not found")?; token::transfer( @@ -863,7 +877,7 @@ impl State { self.set_pfp(id, Default::default()) .expect("couldn't set default pfp"); let _ = self.system_message( - format!("`@{}` joined {}!", name, CONFIG.name), + format!("@{} joined {}!", name, CONFIG.name), CONFIG.dao_realm.into(), ); Ok(id) diff --git a/src/backend/env/post.rs b/src/backend/env/post.rs index ba5a495e..3eb0c640 100644 --- a/src/backend/env/post.rs +++ b/src/backend/env/post.rs @@ -561,6 +561,7 @@ impl Post { let user_id = user.id; let controversial = user.controversial(); let user_balance = user.balance; + let is_organic = user.organic(); let tags = tags(CONFIG.max_tag_length, &body).collect::>(); let mut post = Post::new( user_id, @@ -662,7 +663,10 @@ impl Post { _ => (), }; - notify_about(state, &post); + // Don't notify about system messages + if is_organic { + notify_about(state, &post); + } if post.parent.is_none() { state.root_posts_index.push(post.id); diff --git a/src/backend/env/realms.rs b/src/backend/env/realms.rs index fee79d92..53add6c3 100644 --- a/src/backend/env/realms.rs +++ b/src/backend/env/realms.rs @@ -150,7 +150,7 @@ pub fn create_realm( let _ = state.system_message( format!( - "Realm [{}](/#/realm/{0}) was created by `@{}`", + "Realm [{}](/#/realm/{0}) was created by @{}", realm_id, user_name ), CONFIG.dao_realm.into(), diff --git a/src/backend/env/tip.rs b/src/backend/env/tip.rs index cb04989f..07a8ed63 100644 --- a/src/backend/env/tip.rs +++ b/src/backend/env/tip.rs @@ -79,10 +79,21 @@ fn memo_to_u64(memo: Vec) -> Result { pub async fn add_tip( post_id: PostId, - canister_id: Principal, + canister_id: String, caller: Principal, start_index: u64, ) -> Result { + mutate(|state| match state.principal_to_user(caller) { + Some(user) => { + // DoS protection + state.charge(user.id, CONFIG.tipping_cost, "tipping".to_string())?; + Ok::<(), String>(()) + } + _ => Err("user not found".into()), + })?; + + let canister_id = + Principal::from_text(&canister_id).map_err(|_| "invalid canister id".to_string())?; match try_tip(post_id, canister_id, caller, start_index).await { Ok(tip) => Ok(tip), Err(e) => { diff --git a/src/backend/env/user.rs b/src/backend/env/user.rs index 290a2404..f831f490 100644 --- a/src/backend/env/user.rs +++ b/src/backend/env/user.rs @@ -586,14 +586,12 @@ impl User { if *revenue > 0 && credits_needed > 0 { let e8s_needed = credits_needed * e8s_for_one_xdr / CONFIG.credits_per_xdr; let top_up_e8s = e8s_needed.min(*revenue); - let credits = - top_up_e8s as f32 / e8s_for_one_xdr as f32 * CONFIG.credits_per_xdr as f32; + let credits = (top_up_e8s as f32 / e8s_for_one_xdr as f32 + * CONFIG.credits_per_xdr as f32) as Credits; *revenue = (*revenue).saturating_sub(top_up_e8s); - self.change_credits( - credits as Credits, - CreditsDelta::Plus, - "credits top-up from revenue", - )?; + if credits > 0 { + self.change_credits(credits, CreditsDelta::Plus, "credits top-up from revenue")?; + } } Ok(()) } diff --git a/src/backend/updates.rs b/src/backend/updates.rs index 5b49d4e5..d6e3a55a 100644 --- a/src/backend/updates.rs +++ b/src/backend/updates.rs @@ -109,44 +109,7 @@ fn post_upgrade() { } #[allow(clippy::all)] -fn sync_post_upgrade_fixtures() { - mutate(|state| { - // Update last_activity for features - let ids = state - .memory - .features - .iter() - .map(|(id, _)| id) - .cloned() - .collect::>(); - for id in ids.into_iter() { - let mut feature = state.memory.features.remove(&id).unwrap(); - let post = Post::get(state, &id).expect("post not found for feature"); - feature.last_activity = post.timestamp(); - state - .memory - .features - .insert(id, feature) - .expect("couldn't re-insert feature"); - } - - // Cleanup old logs - state.logger.clean_up(time() - MONTH * 6); - - // Clear feeds if they exceed 200 chars in total - for u in state.users.values_mut() { - if u.feeds - .iter() - .flat_map(|feed| feed.iter()) - .map(|tag| tag.len()) - .sum::() - >= 200 - { - u.feeds.clear(); - } - } - }); -} +fn sync_post_upgrade_fixtures() {} #[allow(clippy::all)] async fn async_post_upgrade_fixtures() {} @@ -745,9 +708,7 @@ fn cancel_bid() { #[export_name = "canister_update add_external_icrc_transaction"] fn add_external_icrc_transaction() { - let (canister_id_as_str, start_index, post_id): (String, u64, PostId) = parse(&arg_data_raw()); - - let canister_id = Principal::from_text(canister_id_as_str).unwrap(); + let (canister_id, start_index, post_id): (String, u64, PostId) = parse(&arg_data_raw()); spawn(async move { reply(add_tip(post_id, canister_id, read(caller), start_index).await) }); } diff --git a/src/frontend/src/post.tsx b/src/frontend/src/post.tsx index c35eafe6..88b1ca4c 100644 --- a/src/frontend/src/post.tsx +++ b/src/frontend/src/post.tsx @@ -207,8 +207,7 @@ export const PostView = ({ ); const isComment = post ? !isRoot(post) : false; - const expanded = - isComment || focused || (!isFeedItem && !repost) || isThreadView; + const expanded = focused || isThreadView || prime; const costTable = reactionCosts(); const isInactive = React.useMemo( () => diff --git a/src/frontend/src/realms.tsx b/src/frontend/src/realms.tsx index e0300c2a..1bdb0ee3 100644 --- a/src/frontend/src/realms.tsx +++ b/src/frontend/src/realms.tsx @@ -791,13 +791,14 @@ export const RealmHeader = ({ ) ) return; - return window.api + await window.api .call( "toggle_realm_membership", name, ) .then(window.reloadUser) .then(loadRealm); + location.href = `#/realm/${name}`; }} /> )} @@ -805,15 +806,16 @@ export const RealmHeader = ({ - window.api + onClick={async () => { + await window.api .call( "toggle_realm_membership", name, ) .then(window.reloadUser) - .then(loadRealm) - } + .then(loadRealm); + location.href = `#/home`; + }} /> )}