---
# System prepended metadata

title: User Preference
tags: [Kizuna-architecture]

---

# User Preference
###### tags: `Kizuna-architecture`
<style> .ui-infobar, #doc.markdown-body { max-width: 1500px; } </style>

## User Stories
- [I want to choose whether the receiver of my message knows whether I am typing or not](https://beyonder.atlassian.net/browse/KIZUNA-51)
- [I want to choose whether the sender knows if i read the message or not](https://beyonder.atlassian.net/browse/KIZUNA-52)

## Comments
- how to stop people from abusively changing the preference (rapidly pressing the toggle button) or is this a ui concern?
    - Debounce is probably a solution for this (not sure how to implement debounce in holochain tho)
- Is it okay to store preference as state since it's only a bool value? Or is it better to also do events pattern?
- Flesh out read receipt architecture (different cases of call_remote, etc)
- Would it be better to have a read_message function that accepts multiple messages to mark them as read rather than a read_message function that accepts a single message then invoking it multiple times on the UI
- I think we should probably make a function that calls all asnychronous functions when the user goes online


## Entry Structure
```rust=
// private entry
#[hdk_entry(id = "preference", visibility = "private")]
pub struct Preference {
    typing_indicator: bool,
    read_receipt: bool,
}

#[hdk_entry(id = "per_agent_preference", visibility = "private")]
pub struct PerAgentPreference {
    typing_indicator: Vec<AgentPubKey>,
    read_receipt: Vec<AgentPubKey>,
}

#[hdk_entry(id = "per_group_preference", visibility = "private")]
pub struct PerGroupPreference {
    typing_indicator: Vec<EntryHash>,
    read_receipt: Vec<EntryHash>,
}

#[derive(Serialize, Deserialize, SerializedBytes, Clone, Debug)]
pub struct PreferenceIO {
    typing_indicator: Option<bool>,
    read_receipt: Option<bool>,
}

#[derive(Serialize, Deserialize, SerializedBytes, Clone, Debug)]
pub struct PerAgentPreferenceIO {
    typing_indicator: Option<Vec<AgentPubKey>>,
    read_receipt: Option<Vec<AgentPubKey>>,
}

#[derive(Serialize, Deserialize, SerializedBytes, Clone, Debug)]
pub struct PerGroupPreferenceIO {
    typing_indicator: Option<Vec<EntryHash>>,
    read_receipt: Option<Vec<EntryHash>>,
}
#[derive(From, Into, Serialize, Deserialize, SerializedBytes)]
pub struct PreferenceWrapper(Preference);

#[derive(From, Into, Serialize, Deserialize, SerializedBytes)]
pub struct PerAgentPreferenceWrapper(PerAgentPreference);

#[derive(From, Into, Serialize, Deserialize, SerializedBytes)]
pub struct PerGroupPreferenceWrapper(PerGroupPreference);
```

## Zome Functions

```rust=
fn set_preference(preference: PreferenceIO) 
-> ExternResult<PreferenceWrapper>

fn get_preference() 
-> ExternResult<PreferenceWrapper>

fn set_per_agent_preference(preference: PerAgentPreferenceIO) 
-> ExternResult<PerAgentPreferenceWrapper>

fn get_per_agent_preference() 
-> ExternResult<PerAgentPreferenceWrapper>

fn set_per_group_preference(preference: PerGroupPreferenceIO) 
-> ExternResult<PerGroupPreferenceWrapper>

fn get_per_group_preference() 
-> ExternResult<PerGroupPreferenceWrapper>
```
## Flow for all preference function    
### `Flow for init`

```mermaid
sequenceDiagram
participant APC as Alice_Preference_Cell

APC-->APC: create Preference entry with all fields set to true
APC-->APC: create Per Agent Preference entry with all fields set to empty Vectors
APC-->APC: create Per Group Preference entry with all fields set to empty Vectors


```

### `Flow for set_preference`
```mermaid
sequenceDiagram

participant AUI as Alice_UI
participant APC as Alice_Preference_Cell


AUI-->APC: call set_preference
APC-->APC: check if all of the parameters are not null
alt All parameters are not null
    APC-->APC: create Preference entry with the parameters
else Some or all parameters are null 
     Note over APC: this is to have the flexibility to call set_preference with only one parameter
    APC-->APC: fetch preference
    APC-->APC: create Preference entry with the parameters and the fetched preference 
end
    APC-->AUI: return new Preference

```
### `Flow for get_preference` 
```mermaid
sequenceDiagram

participant AUI as Alice_UI
participant APC as Alice_Preference_Cell


AUI-->APC: call get_preference
APC-->>APC: fetch all preference and return the last entry
APC-->>AUI: return preference

```

:::info
```This is the current implementation```
This set_per_agent_preference overrides the current entry
:::
:::danger
Is it better to do event-sourcing pattern than stateful pattern here? 
:::
### `Flow for set_per_agent_preference` 
```mermaid
sequenceDiagram

participant AUI as Alice_UI
participant APC as Alice_Preference_Cell


AUI-->APC: call set_per_agent_preference
    
    APC-->>APC: check if all parameters are not null
    alt if all parameters are not null 
        APC-->APC: create PerAgentPreferenece with parameter's value
    else if some or all parameters are null
        Note over APC: To offer the flexibility to send only one parameter
        APC-->APC: fetch per_agent_preference entries
         APC-->APC: create PerGroupPreference entry with the fetched preference and parameter 
    end 
APC-->AUI: return new PerAgentPreference value
```

:::info
This set_per_agent_preference "toggles" the user pubkeys in the parameter in the fetched preference
:::
### `Flow for set_per_agent_preference v2` 
```mermaid
sequenceDiagram

participant AUI as Alice_UI
participant APC as Alice_Preference_Cell


AUI-->APC: call set_per_agent_preference
    APC-->APC: fetch per_agent_preference entries
    APC-->APC: store fetched typing_indicator preference vector as new_typing_indicator 
    APC-->APC: store fetched read_receipt preference vector as new_read_receipt 
    loop parameter's typing_indicator preference
        alt if pubkey is in the fetched typing_indicator
            APC-->APC: add pubkey to the new_typing_indicator 
        else if pubkey is not in the fetched typing_indicator preference
            APC-->APC: remove pubkey to the new_typing_indicator
        end
    end
    
    loop parameter's read_receipt preference
        alt if pubkey is in the fetched read_receipt
            APC-->APC: add pubkey to the new_read_receipt 
        else if pubkey is not in the fetched read_receipt preference
            APC-->APC: remove pubkey to the new_read_receipt
        end
    end
    APC-->APC: create PerAgentPreferenece entry with new_read_receipt and new_typing_indicator

APC-->AUI: return new PerAgentPreference value
```


### `Flow for get_per_agent_preference` 
```mermaid
sequenceDiagram

participant AUI as Alice_UI
participant APC as Alice_Preference_Cell


AUI-->APC: call get_per_agent_preference
APC-->APC: fetch all per_agent_preference entries and return last entry
APC-->AUI: return PerAgentPreference value
```

:::danger
Is it better to do event-sourcing pattern than stateful pattern here? 
:::
### `Flow for set_per_group_preference` 
```mermaid
sequenceDiagram

participant AUI as Alice_UI
participant APC as Alice_Preference_Cell


AUI-->APC: call set_per_group_preference
APC-->APC: check if all parameters are not null
alt All parameters are not null 
    APC-->APC: create PerGroupPreference entry with the parameters
else Some or all parameters are null
    APC-->APC: fetch per_group_preference entries
    APC-->APC: create PerGroupPreference entry with the fetched preference and parameter 
end
APC-->AUI: return new PerGroupPreference value
```
### `Flow for get_per_group_preference` 
```mermaid
sequenceDiagram

participant AUI as Alice_UI
participant APC as Alice_Preference_Cell


AUI-->APC: call get_per_group_preference
APC-->APC: fetch all per_group_preference entries and return last entry
APC-->AUI: return PerGroupPreference value
```

:::warning
Everything below will be transferred to a different zome archi
:::

## Flow for Group Messaging's read_receipt 

```mermaid
    sequenceDiagram
        participant AUI as Alice_UI
        participant ALD as Alice_Lobby_DNA
        participant BUI as Bob_UI
        participant BLD as Bob_Lobby_DNA
        participant CUI as Charlie_UI
        participant CLD as Charlie_Lobby_DNA
        
    AUI-->>ALD: sets preference 
    ALD-->>AUI: fetch preference 
    BLD-->>ALD: call_remote! receive_group_message
    BLD-->>CLD: call_remote receive_group_message
    AUI-->>AUI: reads group message
    alt If global read_receipt is on
        alt If group's UUID does not an entry in the PerGroupPreference
            AUI-->ALD: call send_group_read_receipt
            ALD-->BLD: call_remote receive_group_read_receipt
            BLD-->BUI: emit_signal read_receipt
            BUI-->BUI: update status of relevant group message
            ALD-->CLD: call_remote receive_group_read_receipt
            CLD-->CUI: emit_signal read_receipt
            CUI-->CUI: update status of relevant group message
            
        end 
    end
```

### Group receipts and typing (NOT UPDATED)


## Receipts 

### `Hdk entries and zome functions`

### `Flow`




### `read_group_message`
```rust=
struct GroupStatus {
    agent: AgentPubKey,
    timestamp: Timestamp
}

#[hdk_entry(id="groupmessagereceipt", visibility="public")]
struct GroupMessageReceipt {
    id: Vec<EntryHash>,
    status: Vec<GroupStatus>
}

struct GroupMessageReceiptInput {
    id: Vec<EntryHash>,
    agent: AgentPubKey
}

enum Signal {
    GroupMessageReadReceipt { 
        messages: Vec<EntryHash>
    }
}


fn read_group_message(receipt: GroupMessageReceiptInput) -> ExternResult<GroupMessageReceipt>
```


```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-->>ALD: sets receipt preference for group 
    ALD-->>AUI: fetch ReceiptPreference
    

    alt if read_receipts is on
        alt if Alice did not include this group in her read preference 
            AUI-->>ALD: call `read_group_message`
            rect rgba(200, 100, 100, .5) 
            ALD-->>ALD: invoke 'commit_group_message_receipt'
            end 
            ALD-->>ALD: remote_signal GroupMessageReadReceipt signal
            
            opt if user is online
            ALD-->>OC: 
            rect rgba(100, 100, 200, .5) 
                OC-->>OC: invoke `fetch_group_message_receipts`
            end
            OC-->>OUI: emit_signal receipts
            end
            ALD-->>AUI: return receipts 
          
        end
    end
```
### `commit_group_message_receipt`
```rust=
pub fn commit_group_message_receipt(receipt: GroupMessageReceiptInput) -> ExternResult<GroupMessageReceipt>

[hdk_entry(id="groupmessagereceipt", visibility="public")]
struct GroupMessageReceipt {
    id: Vec<EntryHash>,
    status: Vec<GroupStatus>
}

struct GroupMessageReceiptInput {
    id: Vec<EntryHash>,
    agent: AgentPubKey
}
```
```mermaid
sequenceDiagram
participant AUI as Alice_UI
participant AC as Alice_Cell
participant LDHT as Lobby_DHT

rect rgba(200,100,100,.5)
AC-->>AC: invoke `fetch_group_message_receipts`
end
AC-->>LDHT: fetch receipts
LDHT-->>AC: return receipts

loop fetched receipts 
    AC-->>AC: initialize has_read bool to false
    loop group status vec
        opt agent in parameter is found in this current group status vec
            AC-->>AC: set has_read to true
            AC-->>AC: break
        end
    end
    alt has_read = true
        AC-->>AUI: return receipt
    else has_read = false
        AC-->>AC: create group message receipt entry 
        AC-->>AUI: return receipt
    end

end
```

### `fetch_group_message_receipts`

```rust=
fn fetch_group_message_receipts(receipt: Option<GroupMessageReceiptInput>) -> ExternResult<GroupMessageReceipt>

[hdk_entry(id="groupmessagereceipt", visibility="public")]
struct GroupMessageReceipt {
    id: Vec<EntryHash>,
    status: Vec<GroupStatus>
}

struct GroupMessageReceiptInput {
    id: Vec<EntryHash>,
    agent: AgentPubKey
}
```

```mermaid
sequenceDiagram
    participant AC as Alice_Cell
    participant LDHT as Lobby_DHT
    
    AC-->AC: fetch receipts
```

