---
# System prepended metadata

title: Group Messaging without Encryption
tags: [Kizuna-architecture]

---

# Group Messaging without Encryption
###### tags: `Kizuna-architecture`
<style> .ui-infobar, #doc.markdown-body { max-width: 1000px; } </style>

<img src="https://i.imgur.com/Ei4AjHI.png" width="150" style="padding-right:5px; padding-bottom:5px"/>
<img src="https://i.imgur.com/KZZK6rP.png" width="150" style="padding-right:5px; padding-bottom:5px"/>
<img src="https://i.imgur.com/pRh0t9E.png" width="150" style="padding-right:5px; padding-bottom:5px"/>
<img src="https://i.imgur.com/jkRjXyA.png" width="150" style="padding-right:5px; padding-bottom:5px"/>
<img src="https://i.imgur.com/GZlmVbA.png" width="150" style="padding-right:5px; padding-bottom:5px"/>
<img src="https://i.imgur.com/sDMto7Q.png" width="150" style="padding-right:5px; padding-bottom:5px"/>


:::info
Without Encryption
:::
## Entry Structures

```rust=
#[hdk_entry(id = "group_message", visibility = "public")]
struct GroupMessage {
    // EntryHash of first ver of Group
    group_hash: EntryHash,
    payload: Payload,
    created: Timestamp,
    sender: AgentPubKey,
    reply_to: Option<EntryHash>
}

#[hdk_entry(id = "group_file_bytes", visibility = "public")]
struct GroupFileBytes(SerializedBytes)
```

## Data Structures
```rust=
/* This will be defined in a /commons crate that will be used for both p2pmessage and group zome */

#[derive(Serialize, Deserialize, SerializedBytes, Clone)]
#[serde(rename_all="camelCase")]
struct FileMetadataInput {
   file_name: String,
   file_size: usize,
   file_type: String,
}

#[derive(Serialize, Deserialize, SerializedBytes, Clone)]
#[serde(rename_all="camelCase")]
enum PayloadInput {
  Text {payload: String},
  File {
    metadata: FileMetadataInput
    file_type: FileType
  }
}

#[derive(Serialize, Deserialize, SerializedBytes, Clone)]
#[serde(rename_all="camelCase")]
pub struct FileMetadata {
   file_name: String,
   file_size: usize,
   file_type: String,
   file_hash: EntryHash
}

// https://serde.rs/enum-representations.html#internally-tagged
#[derive(Serialize, Deserialize, SerializedBytes, Clone)]
#[serde(tag = "fileType")]
pub enum FileType {
  Image {
    thumbnail: serializedBytes,
  },
  Video {
    thumbnail: serializedBytes,
  },
  Other
}

#[derive(Serialize, Deserialize, SerializedBytes, Clone)]
#[serde(tag = "type")]
enum Payload {
  Text {payload: String},
  File {
    metadata: FileMetadata,
    file_type: FileType
  }
}

#[derive(Serialize, Deserialize, SerializedBytes, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct GroupMessageWithId {
    // entry_hash of GroupMessage
    pub id: EntryHash,
    pub content: GroupMessage,
}

#[derive(Serialize, Deserialize, SerializedBytes, Clone)]
#[serde(rename_all = "camelCase")]
pub struct GroupMessageData {
    pub group_hash: EntryHash,
    pub payload: Payload,
    pub created: Timestamp,
    pub sender: AgentPubKey,
    pub reply_to: Option<GroupMessageWithId>
};

#[derive(Serialize, Deserialize, SerializedBytes, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct GroupMessageElement {
    pub entry: GroupMessageData,
    pub signed_header: SignedHeaderHashed,
}
```
## Zome Functions

### `init`
```mermaid
sequenceDiagram
participant ALC as Alice_Lobby_Cell
ALC-->>ALC: create unrestricted cap grant for recv_remote_signal
```

### `send_message`
- [x] build with no errors
- [x] send_message test 
- [x] check messages
- [x] tryorama tests for send_message
```rust=
struct GroupMessageInput{
    group_hash: EntryHash,
    payload: PayloadInput,
    // Will only have value if Payload::File
    file_bytes: Option<SerializedBytes>
    sender: AgentPubKey,
    reply_to: Option<EntryHash>
}

#[hdk_extern]
pub fn send_message(message: GroupMessageInput) -> ExternResult<GroupMessageData>
```

```mermaid
sequenceDiagram
participant AUI as Alice_UI
participant ALC as Alice_Lobby_Cell
participant BLC as Bobby_Lobby_Cell
participant LDHT as Lobby_DHT

AUI-->>ALC: call send_message
opt Payload::File
ALC-->>ALC: create GroupFileBytes entry from file_bytes
ALC-->>LDHT: commit GroupFileBytes to DHT
end
ALC-->>ALC: create GroupMessagentry
ALC-->>LDHT: commit GroupMessage to DHT
ALC-->>ALC: generate path "group_hash.unix_timestamp".ensure()
note right of ALC: orignal EntryHash
note right of ALC: unix_timestamp should be created 1 per day in UTC timestamp format

alt payload is a text
ALC-->>LDHT: link from unix_timestamp ->  GroupMessage with tag "text"
else payload is a file
ALC-->>LDHT: link from unix_timestamp ->  GroupMessage with tag "file"
end
note right of ALC: we can get the timestamp per message from the timestamp of the Link


ALC-->>AUI: return GroupMessage (skip this until post_commit is possible)

note right of ALC: yellow box will be moved to post_commit
    rect rgba(255,255,0,0.2)
    rect rgba(250,100,0,0.2)
    note right of ALC: call get_group_latest_version() for the dark orange box
    ALC-->>LDHT: get_details with given EntryHash of Group
    note right of ALC: GetOption latest()
    LDHT-->>ALC: Group EntryDetails
    ALC-->>ALC: check if there is update or not
    alt there is an update for group Entry
      ALC-->>ALC: check which update header is the latest
      ALC-->>LDHT: get entry of the EntryHash in latest update header
      note right of ALC: GetOption contents()
      note right of ALC: may change once get_details() API returns elements in update field and not just header
LDHT-->>ALC: latest Group entry
end
end
ALC-->>ALC: get the current group members

loop for all members
ALC-->>BLC: invoke 'remote_signal' payload GroupMessageData
end

alt User is online
    rect rgba(0,230, 100, .5)
        BLC-->>BLC: invoke `recv_remote_signal`
    end
end
end
```


### `recv_remote_signal`

```rust=
#[hdk_extern]
fn pin_message(signal: SerializedBytes) -> ExternResult<()>
```
```mermaid
sequenceDiagram

participant BUI as Bobby_UI
participant BLC as Bobby_Lobby_Cell
participant ALC as Alice_Lobby_Cell

BLC-->>BLC: check Payload enum type
alt "GroupMessage"    
BLC-->>BUI: emit_signal Message to UI 
else "GroupMessageRead"
BLC-->>BUI: emit_signal GroupMessageRead
end 
```

### `indicate_group_typing`
```rust=
#[derive(Serialize, Deserialize, SerializedBytes, Clone, Debug)]
pub struct GroupTypingDetailIO {
    group_id: EntryHash,
    indicated_by: AgentPubKey,
    members: Vec<AgentPubKey>,
    is_typing: bool,
}

enum SignalPayload {
    GroupTypingDetail(GroupTypingDetailIO)
}

#[hdk_extern]
fn indicate_group_typing(group_typing_detail: GroupTypingDetailIO) -> ExternResult<()>
```

```mermaid
sequenceDiagram
    participant AUI as Alice_UI
    participant ALD as Alice_Cell
    participant OC as Others_Cell
    participant OUI as Others_UI
    
    AUI-->>ALD: sets preference
    ALD-->>AUI: fetch preference
    AUI-->>AUI: starts or stops typing
    
    alt typing preference is on
        alt the Group's EntryHash is not in Alice's typing_indicator within PerGroupPreference
            AUI-->>ALD: call group_typing
            ALD-->>OC: remote_signal GroupTypingDetailIO
            alt User is online
                OC-->>OUI: emit_signal GroupTypingDetailIO 
                alt if is_typing is true
                    OUI-->>OUI: Add Alice's profile in typing array
                else if is_typing is false
                    OUI-->>OUI: Remove Alice's profile in typing array
                
                end
            else User is offline
                Note left of OUI: Do nothing
            end
        end 
    end
```

### read_group_message (tats)
```rust=
struct GroupMessageReadData {
    group_id: EntryHash
    message_ids: Vec<EntryHash>;
    reader: AgentPubKey; 
    timestamp: Timestamp;
    message_created: Timestamp;
    members: Vec<AgentPubKey>
}

enum SignalPayload {
    GroupMessageRead(GroupMessageReadData),
}

pub fn read_group_message(group_message_read_data: GroupMessageReadData) -> ExternResult<GroupMessageReadData>
```

```mermaid
sequenceDiagram

participant AUI as Alice_UI
participant AC as Alice_Cell
participant OC as Others_Cell
participant OUI as Others_UI


AUI-->AC: sets preference
AC-->>AUI: returns preference
AUI-->>AUI: reads group message
note over AC,AUI: premise til here

AUI-->>AC: read_group_message
AC-->>AC: create_link AgentPubKey -> group EntryHash with Tag (group_id,unix_timestamp,message_hash)
note over AC: unix_timestamp is the timestamp of message creation
AC-->>OC: invoke 'remote_signal' with payload to all members

alt Others are online
OC-->>OC: invoke 'recv_remote_signal' 
OC-->>OUI: emit_signal GroupMessageRead
end

AC-->>AUI: GroupMessageReadData
```
## Pin Message Related Fns
### `pin_message`
```rust=
struct PinDetail {
  group_hash: EntryHash,
  group_message_hash: EntryHash, 
}

#[hdk_extern]
fn pin_message(pin_detail: PinDetail) -> ExternResult<()>
```
```mermaid
sequenceDiagram

participant AUI as Alice_UI
participant ALC as Alice_Lobby_Cell
participant BLC as Bobby_Lobby_Cell
participant LDHT as Lobby_DHT

AUI-->>ALC: call pin_message
ALC-->>LDHT: create_link (base: group, target: group_message, tag: "pinned")
LDHT-->>ALC: HeaderHash
ALC-->>AUI: Ok()
```

### `unpin_message`

```rust=
struct pinDetail {
  group_hash: EntryHash,
  group_message_hash: EntryHash
}

#[hdk_extern]
fn unpin_message(pin_detail: pinDetail) -> ExternResult<()>
```
```mermaid
sequenceDiagram

participant AUI as Alice_UI
participant ALC as Alice_Lobby_Cell
participant BLC as Bobby_Lobby_Cell
participant LDHT as Lobby_DHT

AUI-->>ALC: call unpin_message
ALC-->>LDHT: get_links (base: group_hash, link_tag: "pinned")
LDHT-->>ALC: Links
ALC-->>ALC: get the index of the desired link to be removed with `position` method
alt the Link to be deleted exists 
ALC-->>LDHT: delete_link() the link which contains the pinned message hash
LDHT-->>ALC: HeaderHash
else the Link does not exist
ALC-->>AUI: Ok()
end

ALC-->>AUI: Ok()
```

### `get_pinned_messages`
```rust=
struct GroupHash(EntryHash)
struct PinContents(HashMap<GroupMessageHash, GroupMessageElement>)

#[hdk_extern]
fn get_pinned_messages(group_hash: GroupHash) -> ExternResult<PinContents>
```
```mermaid
sequenceDiagram

participant AUI as Alice_UI
participant ALC as Alice_Lobby_Cell
participant BLC as Bobby_Lobby_Cell
participant LDHT as Lobby_DHT

AUI-->>ALC: call get_pinned_messages
ALC-->>ALC: initialize PinContents HashMap
ALC-->>LDHT: get_links (base: group_hash, link_tag: "pinned")
LDHT-->>ALC: Links
loop for link in Links
ALC-->>LDHT: get on target (GroupMessage EntryHash)
note over ALC: contents() GetOptions
LDHT-->>ALC: GroupMessage Element
alt Message is a replyTo another message
ALC-->>LDHT: get() on the EntryHash of the replied message
LDHT-->>ALC: GroupMessage Element
end
ALC-->>ALC: insert GroupMessage Hash and GroupMessage Element (k,v)
end
ALC-->>AUI: Ok(pinContents)
```

### Getters
* UI will be doing the consolidation of values
* pass as much data from the backend
* standardize return values/structures across zome functions

#### Structures
```rust=
pub enum PayloadType {
  Text,
  File,
  All
}

pub struct GroupMessageHash(EntryHash)
pub struct ReadList(HashMap<AgentPubKey, Timestamp>)
pub struct GroupMessageContent(GroupMessageElement, ReadList)

pub struct MessagesByGroup(HashMap<GroupEntryHash, Vec<GroupMessageHash>>)
pub struct GroupMessagesContents(HashMap<GroupMessageHash, GroupMessageContent>)

pub struct GroupMessagesOutput(
    MessagesByGroup,
    GroupMessagesContents
)
```

### `get_messages_by_group_by_timestamp`
**USE CASE IN UI**
  - to fetch texts for a particular date
  - to fetch files for a particular date
  - to fetch all messages for a particular date
```rust=

pub struct GroupChatFilter {
  // has to be the original EntryHash
  group_id: EntryHash,
  // has to be divideable with no remainder in YYYY/MM/DD/00:00
  date: Timestamp,
  // This signifies which payload type is desired
  // to be fetched.
  payload_type: PayloadType
}

pub fn get_messages_by_group_by_timestamp(group_chat_filter: GroupChatFilter) 
-> ExternResult<GroupMessagesOutput>
```
```mermaid
sequenceDiagram

participant BUI as Bobby_UI
participant BLC as Bobby_Lobby_Cell
participant LDHT as Lobby_DHT


BUI-->>BLC: call `fetch_messages_by_group_by_timestamp`
BLC-->>BLC: constrcut path group_hash.timestamp
alt payload_type is File
  BLC-->>LDHT: get_links on given group_hash.timestamp with tag "file"
else payload_type is Text
  BLC-->>LDHT: get_links on given group_hash.timestamp with tag "text"
else payload_type is All
  BLC-->>LDHT: get_links on given group_hash.timestamp with NO tag
end
LDHT-->>BLC: Links (the target is groupmessage entryhash)

BLC-->>BLC: initialize MessagesByGroup (group_id as single key)
BLC-->>BLC: construct GroupMessagesContents HashMap

loop for all Targets
BLC-->>LDHT: get() on target (GroupMessage EntryHash)
note right of BLC: contents() GetOptions
LDHT-->>BLC: GroupMessage Element

alt Message is a replyTo another message
BLC-->>LDHT: get() on the EntryHash of the replied message
end

BLC-->>BLC: insert GroupEntryHash:GroupMessageHash to MessagesByGroup

loop for all members

BLC-->>LDHT: get_links with member AgentPubKey|group_id:|
LDHT-->>BLC: Links (target AgentPubKey)

end

BLC-->>BLC: construct empty ReadList HashMap
loop for all links
    BLC-->>BLC: insert AgentPubKey:link.timestamp
end

BLC-->>BLC: construct GroupMessageData 
BLC-->>BLC: construct GroupMessageElement
BLC-->>BLC: construct GroupMessageContent with (GroupMessageElement, ReadList)
BLC-->>BLC: insert GroupMessageHash:GroupMessageBundle to MessagesByGroup
end
BLC-->>BLC: construct GroupMessagesOutput
BLC-->>BUI: return GroupMessagesOutput
```

### `get_previous_group_messages`
**USE CASE IN UI**
- get the previous (timestamp wise) batch of messages (both text and files) when the user scrolls up in the group chatroom page
- get the pervious batch of files when the user scrolls in the file tab of the group chat (see the ui picture above for reference)
- get the latest batch of messages for a particular group. Useful when the user refreshes the page when s/he is in the group chatroom page.
- get the latest batch of files for a particular group when the user opens up the file page for a particular group 
```rust=
pub struct GroupMsgBatchFetchFilter {
  group_id: EntryHash,
  // the last message of the last batch
  last_fetched: Option<EntryHash>,
  last_message_timestamp: Option<Timestamp>,
  batch_size: u8,
  payload_type: PayloadType
  
}

pub fn get_previous_group_messages(filter: GroupMsgBatchFetchFilter)
-> ExternResult<GroupMessagesOutput>
```

```mermaid
sequenceDiagram

participant BUI as Bobby_UI
participant BLC as Bobby_Lobby_Cell
participant LDHT as Lobby_DHT

BUI-->>BLC: call `get_previous_group_messages`
BLC-->>BLC: initialize MessagesByGroup (group_id as single key)
BLC-->>BLC: initialize GroupMessagesContents HashMap

alt if last_fetched && last_message_timestamp
  BLC-->>BLC: last_message_timestamp.0 /(SECONDS * MINUTES * HOURS)
  BLC-->>BLC: construct the Path group_hash.timestamp(the one divided to days) with the group_id and timestamp given in arg
  BLC-->>BLC: construct Path entry hash
  alt payload_type is File
    BLC-->>LDHT: get_links with Path entry hash with tag "file"
  else payload_type is Text
    BLC-->>LDHT: get_links with Path entry hash with tag  "text"
  else payload_type is All
    BLC-->>LDHT: get_links with Path entry hash with NO tag
  end
  LDHT-->>BLC: links (the target is groupmessage entryhash)
  BLC-->>BLC: find the last_fetched EntryHash given in arg from all the Target
  loop for link.timestamp < last_fetched_message_link.timestamp 
    loop while vec.length (in MessagesByGroup) != batch_size
      BLC-->>BLC: append GroupMessageEntryhash found in Target in MessagesByGroup[group_id] (newest to oldest)
    end
  end
end

opt while vec.length (in MessagesByGroup) != batch_size
  BLC-->>BLC: construct Path from group_id 
  BLC-->>LDHT: get_links on the path (group_hash to string)
  LDHT-->>BLC: return links (timestamp)
  BLC-->>BLC: sort Links(timestamp): newest to oldest based on timestamp in Link
  loop for every Link (timestamp) starting with the Link that has the previous date (when compared with the Link that had the last fetched message) as timestamp
    alt payload_type is File
      BLC-->>LDHT: get_links the Target with tag "file"
    else payload_type is Text
      BLC-->>LDHT: get_links the Target with tag "text"
    else payload_type is All
      BLC-->>LDHT: get_links the Target with NO tag
    end
    LDHT-->>BLC: Links (group_message EntryHash)
    loop for every group_message Link
      BLC-->>BLC: append GroupMessageEntryhash found in Target in MessagesByGroup[group_id] (newest to oldest)
      opt batch_size is met
        BLC-->>BLC: break the loop
      end
    end
  end
end

loop for GroupMessageEntryHash found in MessagesByGroup
  BLC-->>LDHT: get() with EntryHash 
  note right of BLC: getOption content()
  LDHT-->>BLC: GroupMessage Element
  alt Message is a replyTo another message
  BLC-->>LDHT: get() on the EntryHash of the replied message
  LDHT-->>BLC: GroupMessage Element
  end
  BLC-->>LDHT: get_links with GroupMessage EntryHash|read|
  LDHT-->>BLC: Links (target AgentPubKey)
  BLC-->>BLC: construct empty ReadList HashMap
  loop for all links
      BLC-->>BLC: insert AgentPubKey:link.timestamp
  end
  BLC-->>BLC: construct GroupMessageData
  BLC-->>BLC: construct GroupMessageElement
  BLC-->>BLC: construct GroupMessageContent with (GroupMessageElement, ReadList)
  BLC-->>BLC: insert GroupMessageHash:GroupMessageContent to GroupMessagesContents
end

BLC-->>BLC: construct GroupMessagesOutput
BLC-->>BUI: return GroupMessagesOutput
```

### `get_subsequent_group_messages`
**USE CASE IN UI**
- get the next (later timestamp) batch of group messages. Useful when the user jumps into a pinned or replied message and tries to scroll back in to the latest message.
```rust=
pub struct GroupMsgBatchFetchFilter {
  group_id: EntryHash,
  // the latest message of the last batch fetched
  last_fetched: EntryHash,
  last_message_timestamp: Timestamp,
  batch_size: u8,
  payload_type: PayloadType
}

pub fn get_subsequent_group_messages(filter: GroupMsgBatchFetchFilter)
-> ExternResult<GroupMessagesOutput>
```

```mermaid
sequenceDiagram

participant BUI as Bobby_UI
participant BLC as Bobby_Lobby_Cell
participant LDHT as Lobby_DHT

BUI-->>BLC: call `get_subsequent_group_messages`
BLC-->>BLC: initialize MessagesByGroup (group_id as single key)
BLC-->>BLC: initialize GroupMessagesContents HashMap

alt if last_fetched && last_message_timestamp
  BLC-->>BLC: last_message_timestamp.0 /(SECONDS * MINUTES * HOURS)
  BLC-->>BLC: construct the Path group_hash.timestamp(the one divided to days) with the group_id and timestamp given in arg
  BLC-->>BLC: construct Path entry hash
  alt payload_type is File
    BLC-->>LDHT: get_links with Path entry hash with tag "file"
  else payload_type is Text
    BLC-->>LDHT: get_links with Path entry hash with tag  "text"
  else payload_type is All
    BLC-->>LDHT: get_links with Path entry hash with NO tag
  end
  LDHT-->>BLC: links (the target is groupmessage entryhash)
  BLC-->>BLC: find the last_fetched EntryHash given in arg from all the Target
  loop for link.timestamp > last_fetched_message_link.timestamp 
    loop while vec.length (in MessagesByGroup) != batch_size
      BLC-->>BLC: append GroupMessageEntryhash found in Target in MessagesByGroup[group_id]
    end
  end
end

opt while vec.length (in MessagesByGroup) != batch_size
  BLC-->>BLC: construct Path from group_id 
  BLC-->>LDHT: get_links on the path (group_hash to string)
  LDHT-->>BLC: return links (timestamp)
  BLC-->>BLC: sort Links(timestamp): newest to oldest based on timestamp in Link
  loop for every Link (timestamp) starting with the Link that has the next date (when compared with the Link that had the last fetched message) as timestamp
    alt payload_type is File
      BLC-->>LDHT: get_links the Target with tag "file"
    else payload_type is Text
      BLC-->>LDHT: get_links the Target with tag "text"
    else payload_type is All
      BLC-->>LDHT: get_links the Target with NO tag
    end
    LDHT-->>BLC: Links (group_message EntryHash)
    loop for every group_message Link
      BLC-->>BLC: append GroupMessageEntryhash found in Target in MessagesByGroup[group_id]
      opt batch_size is met
        BLC-->>BLC: break the loop
      end
    end
  end
end

loop for GroupMessageEntryHash found in MessagesByGroup
  BLC-->>LDHT: get() with EntryHash 
  note right of BLC: getOption content()
  LDHT-->>BLC: GroupMessage Element
  alt Message is a replyTo another message
  BLC-->>LDHT: get() on the EntryHash of the replied message
  LDHT-->>BLC: GroupMessage Element
  end
  BLC-->>LDHT: get_links with GroupMessage EntryHash|read|
  LDHT-->>BLC: Links (target AgentPubKey)
  BLC-->>BLC: construct empty ReadList HashMap
  loop for all links
      BLC-->>BLC: insert AgentPubKey:link.timestamp
  end
  BLC-->>BLC: construct GroupMessageData
  BLC-->>BLC: construct GroupMessageElement
  BLC-->>BLC: construct GroupMessageContent with (GroupMessageElement, ReadList)
  BLC-->>BLC: insert GroupMessageHash:GroupMessageContent to GroupMessagesContents
end

BLC-->>BLC: construct GroupMessagesOutput
BLC-->>BUI: return GroupMessagesOutput
```

### `get_adjacent_group_messages`
**USE CASE IN UI**
- get  certain number of messages previous and later to a certain message. Useful when we want to be able to jump to a pinned or replied message.
```rust=
pub struct GroupMsgAdjacentFetchFilter {
  group_id: EntryHash,
  // the message EntryHash that has previous and later adjacent messages
  adjacent_message: EntryHash,
  message_timestamp: Timestamp,
  // This batch size goes for both forward and backward
  batch_size: u8,
}

pub fn get_adjacent_group_messages(filter: GroupMsgAdjacentFetchFilter) -> ExternResult<GroupMessagesOutput>
```

```mermaid
sequenceDiagram

participant BUI as Bobby_UI
participant BLC as Bobby_Lobby_Cell
participant LDHT as Lobby_DHT

BUI-->>BLC: call `get_adjacent_group_messages`
BLC-->>BLC: initialize MessagesByGroup (group_id as single key)
BLC-->>BLC: initialize GroupMessagesContents HashMap

BLC-->>BLC: message_timestamp.0 /(SECONDS * MINUTES * HOURS)
  BLC-->>BLC: construct the Path group_hash.timestamp(the one divided to days) with the group_id and timestamp given in arg
  BLC-->>BLC: construct Path entry hash
  BLC-->>LDHT: get_links with Path entry hash with NO Tag
  
  LDHT-->>BLC: links (the target is groupmessage entryhash)
  BLC-->>BLC: find the last_fetched EntryHash given in arg from all the Target
  
  loop for link.timestamp > message_timestamp
  note over BLC: for messages with timestamp later than the adjacent message timestamp
    loop while no of message EntryHash in MessagesByGroup (where messageId.timestamp > message_timestamp)  != batch_size
      BLC-->>BLC: append GroupMessageEntryhash found in Target in MessagesByGroup[group_id]
    end
    note over BLC,LDHT: repeat the loop where link.timestamp < message_timestamp
  end
  

  
  opt while no of message EntryHash in MessagesByGroup (where messageId.timestamp > message_timestamp)  != batch_size
  note over BLC: for messages with timestamp later than the adjacent message timestamp
  BLC-->>BLC: construct Path from group_id 
  BLC-->>LDHT: get_links on the path (group_hash to string)
  LDHT-->>BLC: return links (timestamp)
  BLC-->>BLC: sort Links (timestamp): newest to oldest based on timestamp in Link
  loop for every Link (timestamp) starting with the Link that has the next date (when compared with the Link that
    BLC-->>LDHT: get_links the Target with NO tag
    LDHT-->>BLC: Links (group_message EntryHash)
    loop for every group_message Link
      BLC-->>BLC: append GroupMessageEntryhash found in Target in MessagesByGroup[group_id] (newest to oldest)
      opt batch_size is met
        BLC-->>BLC: break the loop
      end
    end
  end
  note over BLC,LDHT: repeat the loop above where messageId.timestamp < message_timestamp
end



loop for GroupMessageEntryHash found in MessagesByGroup
  BLC-->>LDHT: get() with EntryHash 
  note right of BLC: getOption content()
  LDHT-->>BLC: GroupMessage Element
  alt Message is a replyTo another message
  BLC-->>LDHT: get() on the EntryHash of the replied message
  LDHT-->>BLC: GroupMessage Element
  end
  BLC-->>LDHT: get_links with GroupMessage EntryHash|read|
  LDHT-->>BLC: Links (target AgentPubKey)
  BLC-->>BLC: construct empty ReadList HashMap
  loop for all links
      BLC-->>BLC: insert AgentPubKey:link.timestamp
  end
  BLC-->>BLC: construct GroupMessageData
  BLC-->>BLC: construct GroupMessageElement
  BLC-->>BLC: construct GroupMessageContent with (GroupMessageElement, ReadList)
  BLC-->>BLC: insert GroupMessageHash:GroupMessageContent to GroupMessagesContents
end

BLC-->>BLC: construct GroupMessagesOutput
BLC-->>BUI: return GroupMessagesOutput
```

### `get_latest_messages_for_all_groups`
**USE CASE IN UI**
- get the latest messages in batch for all groups when the app loads up. (this function will be called by the aggregator zome together with other necessary functions to render the ui)
```rust=
pub struct BatchSize(u8)

pub fn get_latest_messages_for_all_groups(batch_size: BatchSize)
-> ExternResult<GroupMessagesOutput>
```

```mermaid
sequenceDiagram

participant AG as Aggregator_Zome
participant BLC as Bobby_Lobby_Cell
participant LDHT as Lobby_DHT


AG-->>BLC: call `fetch_latest_messages_for_all_groups`
BLC-->>BLC: initialize MessagesByGroup
BLC-->>BLC: initialize GroupMessagesContents HashMap
BLC-->>LDHT: get_links agent_pubkey->group|member| (group_hash)
LDHT-->>BLC: Links (Group EntryHash)

loop for each Link (group_hash)
  BLC-->>BLC: call fetch_next_batch_group_messages() with payload_type All
  note over BLC: last_fetched/last_message_timestamp as None
  BLC-->>BLC: return GroupMessagesOutput
  BLC-->>BLC: insert GroupMessagesContents and MessagesByGroup values from returned GroupMessagesOutput into the initialized MessagesByGroup and GroupMessagesContents
end
BLC-->>BLC: construct GroupMessagesOutput
BLC-->>AG: return GroupMessagesOutput
```
### `get_file_bytes`
- Useful when the user would like to download a single file
```rust=
pub struct EntryHashWrapper(EntryHash)
fn get_file_bytes(file_entry_hash: EntryHash) -> ExternResult<GroupFileBytes>
```
```mermaid
sequenceDiagram
participant BUI as Bobby_UI
participant BLC as Bobby_Lobby_Cell
participant LDHT as Lobby_DHT

BUI-->>BLC: call get_file_bytes
BLC-->>LDHT: get() with file_entry_hash
note right of BLC: getOption content()
note right of BLC: GroupFileBytes can have multiple headers but we are only concern with the content not the metadata so content() option is fine
LDHT-->>BLC: GroupFileBytes
BLC-->>BUI: return GroupFileBytes
```

## Redux
```rust=
pub struct GroupMessageHash(EntryHash)
pub struct ReadList(HashMap<AgentPubKey, Timestamp>)
pub struct GroupMessageContent(GroupMessageElement, ReadList)

pub struct MessagesByGroup(HashMap<GroupEntryHash, Vec<GroupMessageHash>>)
pub struct GroupMessagesContents(HashMap<GroupMessageHash, GroupMessageContent>)

pub struct GroupMessagesOutput(
    MessagesByGroup,
    GroupMessagesContents
)
```
```typescript=
type GroupMessageIDB64 = string;
type GroupIDB64 = string; // Group's EntryHash
type GroupFileBytesID = string; // GroupFileBytes' EntryHash
type GroupRevisionID = string; // Group's HeaderHash
type Payload =  TextPayload | FilePayload

interface TextPayload {
  type: "TEXT";
  payload: { payload: string };
}

interface FilePayload {
  type: "FILE";
  fileName: string;
  fileSize: number;
  fileType: "IMAGE" | "VIDEO" | "OTHER";
  fileHash: FileBytesID;
  thumbnail?: Uint8Array;
}

interface GroupMessage {
  groupMessageId: GroupMessageIDB64;
  groupId: GroupIDB64;
  author: string; // AgentPubKey in b64
  payload: Payload; // subject to change
  timestamp: Date;
  replyTo?: GroupMessageIDB64;
  readList: {
    // key is AgentPubKey
    [key: string]: Date;
  };
}

// TODO: make sure this is fetched from holochain at some point
interface GroupVersion {
    groupEntryHash: GroupID
    name: string;
    conversants: ProfileID[];
    timestamp: Date;
}

interface GroupConversation {
  originalGroupId: GroupIDB64; // EntryHash
  originalGroupRevisionId: GroupRevisionIDB64; // HeaderHash
  // versions: GroupVersion[];
  name: string;
  members: string[];
  createdAt: Date;
  creator: string;
  // TODO: enable setting of avatar for a GroupConversation
  avatar?: string;
  messages: GroupMessageIDB64[];
  pinned: GroupMessageIDB64[];
}

interface MessagesByGroup {
  // key here is the base64 string of Group EntryHash
  [key: string]: GroupMessageIDB64[];
}

interface GroupMessagesContents {
  // key here is the base 64 string of GroupMessage EntryHash
  [key: string]: GroupMessage;
}

interface GroupMessagesOutput {
  messagesByGroup: MessagesByGroup;
  groupMessagesContents: GroupMessagesContents;
}

interface UpdateGroupMembersData {
  // base64 string
  members: string[];
  groupId: GroupIDB64;
  groupRevisionId: GroupRevisionIDB64;
}

interface UpdateGroupNameData {
  name: string;
  groupId: GroupIDB64;
  groupRevisionId: GroupRevisionIDB64;
}

/*
 * Group Conversation interface 
 */

export interface GroupConversationsState {
  conversations: {
    // key should be the originalGroupEntryHash
    [key: string]: GroupConversation;
  };
  messages: {
    [key: string]: GroupMessage;
  };
  groupFiles: {
    [key: string]: Uint8Array;
  };
  // This makes it easier to manage when member of a group updates their username (username has no update fn yet)
  // and saves network calls to fetch username in case a member is part of multiple groups
  // key is agentPubKey
  members: {
    [key: string]: Profile;
  };
  typing: {
    // key is GroupID
    [key: string]: Profile[];
  };
  pinned: {
    [key: string]: GroupMessage
  }
}



// group conversations redux
// reducer
const initialState: GroupConversationsState = {
  conversations: {},
  messages: {},
  groupFiles: {},
  members: {},
  typing: {},
  pinned: {},
};

```

