Skip to content

Commit

Permalink
Store chats against chat ids
Browse files Browse the repository at this point in the history
  • Loading branch information
megrogan committed Dec 21, 2020
1 parent a477cb2 commit 2054af2
Show file tree
Hide file tree
Showing 9 changed files with 109 additions and 50 deletions.
1 change: 1 addition & 0 deletions src/chats/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ crate-type = ["cdylib"]
ic-cdk = "0.2.0"
ic-cdk-macros = "0.2.0"
ic-types = "0.1.1"
highway = "0.6.3"
serde = "1.0.111"
shared = { path = "../shared", version = "0.1.0" }
5 changes: 3 additions & 2 deletions src/chats/chats.did
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ type Message =
}

service : {
send_message: (principal, text) -> ();
get_messages: (principal, nat64) -> (vec Message) query;
create_chat: (principal) -> (opt nat64);
send_message: (nat64, text) -> (bool);
get_messages: (nat64, nat64) -> (opt vec Message) query;
}
23 changes: 17 additions & 6 deletions src/chats/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,24 @@ use ic_types::Principal;
use crate::domain::chat::Message;
use crate::queries::*;
use crate::updates::*;
use crate::domain::chat_list::ChatId;

// #[query]
// fn list_chats() -> Vec<ChatSummary> {
// }

#[query]
fn get_messages(chat_id: ChatId, from_index: usize) -> Option<Vec<Message>> {
get_messages::query(chat_id, from_index)
}

#[update]
fn send_message(recipient: Principal, text: String) {
send_message::update(recipient, text)
fn create_chat(recipient: Principal) -> Option<ChatId> {
create_chat::update(recipient)
}

#[update]
fn send_message(chat_id: ChatId, text: String) -> bool {
send_message::update(chat_id, text)
}

#[query]
fn get_messages(from_user: Principal, from_index: usize) -> Vec<Message> {
get_messages::query(from_user, from_index)
}
8 changes: 4 additions & 4 deletions src/chats/src/domain/chat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ impl Chat {
}
}

pub fn involves_user(&self, user: &Principal) -> bool {
self.user1 == *user || self.user2 == *user
}

pub fn push_message(&mut self, sender: &Principal, text: String, timestamp: u64) {
let id = match self.messages.last() {
Some(m) => m.id + 1,
Expand All @@ -44,10 +48,6 @@ impl Chat {
.map(|m| Message::new(m.id, m.timestamp, m.sent_by_user1 == (*me == self.user1), m.text.clone()))
.collect()
}

pub fn get_key(&self) -> (Principal, Principal) {
(self.user1.clone(), self.user2.clone())
}
}

#[derive(CandidType)]
Expand Down
77 changes: 56 additions & 21 deletions src/chats/src/domain/chat_list.rs
Original file line number Diff line number Diff line change
@@ -1,56 +1,91 @@
use std::collections::{HashMap, hash_map::Entry::{Occupied, Vacant}};
use ic_cdk::export::candid::CandidType;
use ic_types::Principal;
use highway::{HighwayHasher, HighwayHash};
use serde::Deserialize;
use shared::StableState;
use super::chat::Chat;

#[derive(Default)]
pub struct ChatList {
chats: HashMap<(Principal, Principal), Chat>
chats: HashMap<ChatId, Chat>
}

impl ChatList {
pub fn get(&self, sender: Principal, recipient: Principal) -> Option<&Chat> {
let (user1, user2) = ChatList::order_users(sender, recipient);

self.chats.get(&(user1, user2))
pub fn create(&mut self, sender: Principal, recipient: Principal) -> Option<ChatId> {

let chat_id = ChatId::new(&sender, &recipient);

match self.chats.entry(chat_id) {
Occupied(_) => None,
Vacant(e) => {
e.insert(Chat::new(sender, recipient));
Some(chat_id)
}
}
}

pub fn get_or_add_chat(&mut self, sender: Principal, recipient: Principal) -> &mut Chat {
let (user1, user2) = ChatList::order_users(sender, recipient);
pub fn get(&self, chat_id: ChatId, on_behalf_of: &Principal) -> Option<&Chat> {

match self.chats.entry((user1.clone(), user2.clone())) {
Occupied(e) => e.into_mut(),
Vacant(e) => e.insert(Chat::new(user1, user2))
let chat = self.chats.get(&chat_id)?;

if !chat.involves_user(on_behalf_of) {
return None;
}

Some(chat)
}

fn order_users(user1: Principal, user2: Principal) -> (Principal, Principal) {
if user1 < user2 {
(user1, user2)
} else {
(user2, user1)
pub fn get_mut(&mut self, chat_id: ChatId, on_behalf_of: &Principal) -> Option<&mut Chat> {

let chat = self.chats.get_mut(&chat_id)?;

if !chat.involves_user(on_behalf_of) {
return None;
}

Some(chat)
}
}

impl StableState for ChatList {
type State = Vec<Chat>;
type State = Vec<(ChatId, Chat)>;

fn drain(self) -> Vec<Chat> {
fn drain(self) -> Vec<(ChatId, Chat)> {
self.chats
.into_iter()
.map(|(_, v)| v)
.collect()
}

fn fill(chats: Vec<Chat>) -> ChatList {
let map: HashMap<(Principal, Principal), Chat> = chats
fn fill(chats: Vec<(ChatId, Chat)>) -> ChatList {
let map: HashMap<ChatId, Chat> = chats
.into_iter()
.map(|c| ((c.get_key(), c)))
.collect();

ChatList {
chats: map
}
}
}
}

/// TODO: We would preferably use a Uuid or u128 but these haven't yet got a CandidType implementation
#[derive(CandidType, Deserialize, PartialEq, Eq, Hash, Copy, Clone)]
pub struct ChatId(u64);

impl ChatId {
fn new(user1: &Principal, user2: &Principal) -> ChatId {
let mut hasher = HighwayHasher::default();

if user1 < user2 {
hasher.append(user1.as_slice());
hasher.append(user2.as_slice());
} else {
hasher.append(user2.as_slice());
hasher.append(user1.as_slice());
}

ChatId(hasher.finalize64())
}
}

14 changes: 6 additions & 8 deletions src/chats/src/queries/get_messages.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
use ic_cdk::storage;
use ic_types::Principal;
use crate::domain::chat_list::ChatList;
use crate::domain::chat_list::{ChatId, ChatList};
use crate::domain::chat::Message;

pub fn query(from_user: Principal, from_index: usize) -> Vec<Message> {
let me = ic_cdk::caller();
pub fn query(chat_id: ChatId, from_index: usize) -> Option<Vec<Message>> {

let chat_list: &mut ChatList = storage::get_mut();
let me = ic_cdk::caller();

let chat = chat_list.get(chat_id, &me)?;

match chat_list.get(from_user, me.clone()) {
Some(c) => c.get_messages(&me, from_index),
None => Vec::new()
}
Some(chat.get_messages(&me, from_index))
}
11 changes: 11 additions & 0 deletions src/chats/src/updates/create_chat.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use ic_types::Principal;
use crate::domain::chat_list::{ChatId, ChatList};
use ic_cdk::{storage};

pub fn update(recipient: Principal) -> Option<ChatId> {

let chat_list: &mut ChatList = storage::get_mut();
let me = ic_cdk::caller();

chat_list.create(me, recipient)
}
3 changes: 2 additions & 1 deletion src/chats/src/updates/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod send_message;
pub mod send_message;
pub mod create_chat;
17 changes: 9 additions & 8 deletions src/chats/src/updates/send_message.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
use crate::domain::chat_list::{ChatId, ChatList};
use ic_cdk::{api, storage};
use ic_types::Principal;
use crate::domain::chat_list::ChatList;

pub fn update(recipient: Principal, text: String) {
let me = ic_cdk::caller();
pub fn update(chat_id: ChatId, text: String) -> bool {

let chat_list: &mut ChatList = storage::get_mut();
let me = ic_cdk::caller();

let chat = chat_list.get_or_add_chat(me.clone(), recipient);

let timestamp = api::time() as u64;
if let Some(chat) = chat_list.get_mut(chat_id, &me) {
let timestamp = api::time() as u64;
chat.push_message(&me, text, timestamp);
return true;
}

chat.push_message(&me, text, timestamp);
false
}

0 comments on commit 2054af2

Please sign in to comment.