2021-11-09 23:47:33 +00:00
|
|
|
package model
|
|
|
|
|
2021-11-17 22:34:13 +00:00
|
|
|
import (
|
|
|
|
"cwtch.im/cwtch/model/attr"
|
|
|
|
"cwtch.im/cwtch/model/constants"
|
|
|
|
"encoding/json"
|
2022-03-03 23:58:41 +00:00
|
|
|
"time"
|
2021-11-17 22:34:13 +00:00
|
|
|
)
|
2021-11-09 23:47:33 +00:00
|
|
|
|
|
|
|
// AccessControl is a type determining client assigned authorization to a peer
|
|
|
|
type AccessControl struct {
|
|
|
|
Blocked bool // Any attempts from this handle to connect are blocked
|
|
|
|
Read bool // Allows a handle to access the conversation
|
|
|
|
Append bool // Allows a handle to append new messages to the conversation
|
|
|
|
}
|
|
|
|
|
|
|
|
// DefaultP2PAccessControl - because in the year 2021, go does not support constant structs...
|
|
|
|
func DefaultP2PAccessControl() AccessControl {
|
|
|
|
return AccessControl{Read: true, Append: true, Blocked: false}
|
|
|
|
}
|
|
|
|
|
|
|
|
// AccessControlList represents an access control list for a conversation. Mapping handles to conversation
|
|
|
|
// functions
|
|
|
|
type AccessControlList map[string]AccessControl
|
|
|
|
|
2021-11-11 00:41:43 +00:00
|
|
|
// Serialize transforms the ACL into json.
|
2021-11-09 23:47:33 +00:00
|
|
|
func (acl *AccessControlList) Serialize() []byte {
|
|
|
|
data, _ := json.Marshal(acl)
|
|
|
|
return data
|
|
|
|
}
|
|
|
|
|
2021-11-11 00:41:43 +00:00
|
|
|
// DeserializeAccessControlList takes in JSON and returns an AccessControlList
|
2021-11-09 23:47:33 +00:00
|
|
|
func DeserializeAccessControlList(data []byte) AccessControlList {
|
|
|
|
var acl AccessControlList
|
|
|
|
json.Unmarshal(data, &acl)
|
|
|
|
return acl
|
|
|
|
}
|
|
|
|
|
2021-11-11 00:41:43 +00:00
|
|
|
// Attributes a type-driven encapsulation of an Attribute map.
|
2021-11-09 23:47:33 +00:00
|
|
|
type Attributes map[string]string
|
|
|
|
|
2021-11-11 00:41:43 +00:00
|
|
|
// Serialize transforms an Attributes map into a JSON struct
|
2021-11-09 23:47:33 +00:00
|
|
|
func (a *Attributes) Serialize() []byte {
|
|
|
|
data, _ := json.Marshal(a)
|
|
|
|
return data
|
|
|
|
}
|
|
|
|
|
2021-11-17 23:59:52 +00:00
|
|
|
// DeserializeAttributes converts a JSON struct into an Attributes map
|
2021-11-09 23:47:33 +00:00
|
|
|
func DeserializeAttributes(data []byte) Attributes {
|
|
|
|
var attributes Attributes
|
|
|
|
json.Unmarshal(data, &attributes)
|
|
|
|
return attributes
|
|
|
|
}
|
|
|
|
|
|
|
|
// Conversation encapsulates high-level information about a conversation, including the
|
|
|
|
// handle, any set attributes, the access control list associated with the message tree and the
|
|
|
|
// accepted status of the conversation (whether the user has consented into the conversation).
|
|
|
|
type Conversation struct {
|
|
|
|
ID int
|
|
|
|
Handle string
|
|
|
|
Attributes Attributes
|
|
|
|
ACL AccessControlList
|
|
|
|
Accepted bool
|
|
|
|
}
|
2021-11-17 22:34:13 +00:00
|
|
|
|
2021-11-17 23:59:52 +00:00
|
|
|
// GetAttribute is a helper function that fetches a conversation attribute by scope, zone and key
|
2021-11-17 22:34:13 +00:00
|
|
|
func (ci *Conversation) GetAttribute(scope attr.Scope, zone attr.Zone, key string) (string, bool) {
|
|
|
|
if value, exists := ci.Attributes[scope.ConstructScopedZonedPath(zone.ConstructZonedPath(key)).ToString()]; exists {
|
|
|
|
return value, true
|
|
|
|
}
|
|
|
|
return "", false
|
|
|
|
}
|
|
|
|
|
2021-11-17 23:59:52 +00:00
|
|
|
// IsGroup is a helper attribute that identifies whether a conversation is a legacy group
|
2021-11-17 22:34:13 +00:00
|
|
|
func (ci *Conversation) IsGroup() bool {
|
|
|
|
if _, exists := ci.Attributes[attr.LocalScope.ConstructScopedZonedPath(attr.LegacyGroupZone.ConstructZonedPath(constants.GroupID)).ToString()]; exists {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2021-11-17 23:59:52 +00:00
|
|
|
// IsServer is a helper attribute that identifies whether a conversation is with a server
|
2021-11-17 22:34:13 +00:00
|
|
|
func (ci *Conversation) IsServer() bool {
|
|
|
|
if _, exists := ci.Attributes[attr.PublicScope.ConstructScopedZonedPath(attr.ServerKeyZone.ConstructZonedPath(string(BundleType))).ToString()]; exists {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2022-03-03 23:58:41 +00:00
|
|
|
// ServerSyncProgress is only valid during a server being in the AUTHENTICATED state and therefor in the syncing process
|
|
|
|
// it returns a double (0-1) representing the estimated progress of the syncing
|
|
|
|
func (ci *Conversation) ServerSyncProgress() float64 {
|
|
|
|
startTimeStr, startExists := ci.Attributes[attr.LocalScope.ConstructScopedZonedPath(attr.LegacyGroupZone.ConstructZonedPath(constants.SyncPreLastMessageTime)).ToString()]
|
|
|
|
recentTimeStr, recentExists := ci.Attributes[attr.LocalScope.ConstructScopedZonedPath(attr.LegacyGroupZone.ConstructZonedPath(constants.SyncMostRecentMessageTime)).ToString()]
|
|
|
|
|
|
|
|
if !startExists || !recentExists {
|
|
|
|
return 0.0
|
|
|
|
}
|
|
|
|
|
|
|
|
startTime, err := time.Parse(startTimeStr, time.RFC3339Nano)
|
|
|
|
if err != nil {
|
|
|
|
return 0.0
|
|
|
|
}
|
|
|
|
recentTime, err := time.Parse(recentTimeStr, time.RFC3339Nano)
|
|
|
|
if err != nil {
|
|
|
|
return 0.0
|
|
|
|
}
|
|
|
|
|
|
|
|
syncRange := time.Since(startTime)
|
|
|
|
pointFromStart := startTime.Sub(recentTime)
|
|
|
|
return pointFromStart.Seconds() / syncRange.Seconds()
|
|
|
|
}
|
|
|
|
|
2021-11-17 23:59:52 +00:00
|
|
|
// ConversationMessage bundles an instance of a conversation message row
|
2021-11-17 22:34:13 +00:00
|
|
|
type ConversationMessage struct {
|
|
|
|
ID int
|
|
|
|
Body string
|
|
|
|
Attr Attributes
|
|
|
|
Signature string
|
|
|
|
ContentHash string
|
|
|
|
}
|