Group Functionality Experiments with Server Lists
continuous-integration/drone/pr Build is passing
Details
continuous-integration/drone/pr Build is passing
Details
This commit is contained in:
parent
154ba4610d
commit
f9b4e1179e
|
@ -1,6 +1,8 @@
|
|||
package groups
|
||||
|
||||
import (
|
||||
"cwtch.im/cwtch/event"
|
||||
"cwtch.im/cwtch/model"
|
||||
"cwtch.im/cwtch/peer"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
|
@ -9,22 +11,40 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
const ServerPrefix = "server:"
|
||||
const TofuBundlePrefix = "tofubundle:"
|
||||
const GroupPrefix = "torv3"
|
||||
const GroupExperiment = "tapir-groups-experiment"
|
||||
const serverPrefix = "server:"
|
||||
const tofuBundlePrefix = "tofubundle:"
|
||||
const groupPrefix = "torv3"
|
||||
const groupExperiment = "tapir-groups-experiment"
|
||||
|
||||
const (
|
||||
// ServerList is a json encoded list of servers
|
||||
ServerList = event.Field("ServerList")
|
||||
)
|
||||
|
||||
const (
|
||||
// UpdateServerInfo is an event containing a ProfileOnion and a ServerList
|
||||
UpdateServerInfo = event.Type("UpdateServerInfo")
|
||||
)
|
||||
|
||||
// ReadServerInfo is a meta-interface for reading information about servers..
|
||||
type ReadServerInfo interface {
|
||||
peer.ReadContacts
|
||||
peer.ReadServers
|
||||
}
|
||||
|
||||
// GroupFunctionality provides experiment gated server functionality
|
||||
type GroupFunctionality struct {
|
||||
}
|
||||
|
||||
// ExperimentGate returns GroupFunctionality if the experiment is enabled, and an error otherwise.
|
||||
func ExperimentGate(experimentMap map[string]bool) (*GroupFunctionality, error) {
|
||||
if experimentMap[GroupExperiment] {
|
||||
if experimentMap[groupExperiment] {
|
||||
return new(GroupFunctionality), nil
|
||||
}
|
||||
return nil, fmt.Errorf("gated by %v", GroupExperiment)
|
||||
return nil, fmt.Errorf("gated by %v", groupExperiment)
|
||||
}
|
||||
|
||||
// SendMessage is a deprecated api
|
||||
func (gf *GroupFunctionality) SendMessage(peer peer.CwtchPeer, handle string, message string) error {
|
||||
// TODO this auto accepting behaviour needs some thinking through
|
||||
if !peer.GetGroup(handle).Accepted {
|
||||
|
@ -43,25 +63,49 @@ func (gf *GroupFunctionality) SendMessage(peer peer.CwtchPeer, handle string, me
|
|||
// ValidPrefix returns true if an import string contains a prefix that indicates it contains information about a
|
||||
// server or a group
|
||||
func (gf *GroupFunctionality) ValidPrefix(importString string) bool {
|
||||
return strings.HasPrefix(importString, TofuBundlePrefix) || strings.HasPrefix(importString, ServerPrefix) || strings.HasPrefix(importString, GroupPrefix)
|
||||
return strings.HasPrefix(importString, tofuBundlePrefix) || strings.HasPrefix(importString, serverPrefix) || strings.HasPrefix(importString, groupPrefix)
|
||||
}
|
||||
|
||||
// GetServerInfoList compiles all the information the UI might need regarding all servers..
|
||||
func (gf *GroupFunctionality) GetServerInfoList(profile ReadServerInfo) []Server {
|
||||
var servers []Server
|
||||
for _, server := range profile.GetServers() {
|
||||
servers = append(servers, gf.GetServerInfo(server, profile))
|
||||
}
|
||||
return servers
|
||||
}
|
||||
|
||||
// GetServerInfo compiles all the information the UI might need regarding a particular server including any verified
|
||||
// cryptographic keys
|
||||
func (gf *GroupFunctionality) GetServerInfo(serverOnion string, profile peer.ReadContacts) Server {
|
||||
serverInfo := profile.GetContact(serverOnion)
|
||||
keyTypes := []model.KeyType{model.KeyTypeServerOnion, model.KeyTypeTokenOnion, model.KeyTypePrivacyPass}
|
||||
var serverKeys []ServerKey
|
||||
|
||||
for _, keyType := range keyTypes {
|
||||
if key, has := serverInfo.GetAttribute(string(keyType)); has {
|
||||
serverKeys = append(serverKeys, ServerKey{Type: string(keyType), Key: key})
|
||||
}
|
||||
}
|
||||
return Server{Onion: serverOnion, Status: serverInfo.State, Keys: serverKeys}
|
||||
}
|
||||
|
||||
// HandleImportString handles import strings for groups and servers
|
||||
func (gf *GroupFunctionality) HandleImportString(peer peer.CwtchPeer, importString string) error {
|
||||
|
||||
if strings.HasPrefix(importString, TofuBundlePrefix) {
|
||||
if strings.HasPrefix(importString, tofuBundlePrefix) {
|
||||
bundle := strings.Split(importString, "||")
|
||||
gf.HandleImportString(peer, bundle[0][11:])
|
||||
gf.HandleImportString(peer, bundle[1])
|
||||
return nil
|
||||
} else if strings.HasPrefix(importString, ServerPrefix) {
|
||||
} else if strings.HasPrefix(importString, serverPrefix) {
|
||||
// Server Key Bundles are prefixed with
|
||||
bundle, err := base64.StdEncoding.DecodeString(importString[7:])
|
||||
if err == nil {
|
||||
return peer.AddServer(string(bundle))
|
||||
}
|
||||
return err
|
||||
} else if strings.HasPrefix(importString, GroupPrefix) {
|
||||
} else if strings.HasPrefix(importString, groupPrefix) {
|
||||
//eg: torv3JFDWkXExBsZLkjvfkkuAxHsiLGZBk0bvoeJID9ItYnU=EsEBCiBhOWJhZDU1OTQ0NWI3YmM2N2YxYTM5YjkzMTNmNTczNRIgpHeNaG+6jy750eDhwLO39UX4f2xs0irK/M3P6mDSYQIaOTJjM2ttb29ibnlnaGoyenc2cHd2N2Q1N3l6bGQ3NTNhdW8zdWdhdWV6enB2ZmFrM2FoYzRiZHlkCiJAdVSSVgsksceIfHe41OJu9ZFHO8Kwv3G6F5OK3Hw4qZ6hn6SiZjtmJlJezoBH0voZlCahOU7jCOg+dsENndZxAA==
|
||||
return peer.ImportGroup(importString)
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ package groups
|
|||
import "testing"
|
||||
|
||||
func TestGroupFunctionality_ValidPrefix(t *testing.T) {
|
||||
gf, _ := ExperimentGate(map[string]bool{GroupExperiment: true})
|
||||
gf, _ := ExperimentGate(map[string]bool{groupExperiment: true})
|
||||
if gf.ValidPrefix("torv3blahblahblah") == false {
|
||||
t.Fatalf("torv3 should be a valid prefix")
|
||||
}
|
||||
|
@ -26,13 +26,13 @@ func TestGroupFunctionality_IsEnabled(t *testing.T) {
|
|||
t.Fatalf("group functionality should be disabled")
|
||||
}
|
||||
|
||||
_, err = ExperimentGate(map[string]bool{GroupExperiment: true})
|
||||
_, err = ExperimentGate(map[string]bool{groupExperiment: true})
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("group functionality should be enabled")
|
||||
}
|
||||
|
||||
_, err = ExperimentGate(map[string]bool{GroupExperiment: false})
|
||||
_, err = ExperimentGate(map[string]bool{groupExperiment: false})
|
||||
if err == nil {
|
||||
t.Fatalf("group functionality should be disabled")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package groups
|
||||
|
||||
type ServerKey struct {
|
||||
Type string `json:"type"`
|
||||
Key string `json:"key"`
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
Onion string `json:"onion"`
|
||||
Status string `json:"status"`
|
||||
Keys []ServerKey `json:"keys"`
|
||||
}
|
3
go.mod
3
go.mod
|
@ -6,4 +6,5 @@ require (
|
|||
cwtch.im/cwtch v0.6.6
|
||||
git.openprivacy.ca/openprivacy/connectivity v1.4.2
|
||||
git.openprivacy.ca/openprivacy/log v1.0.2
|
||||
)
|
||||
golang.org/x/mobile v0.0.0-20210220033013-bdb1ca9a1e08 // indirect
|
||||
)
|
||||
|
|
13
go.sum
13
go.sum
|
@ -37,6 +37,7 @@ git.openprivacy.ca/openprivacy/log v1.0.0/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQN
|
|||
git.openprivacy.ca/openprivacy/log v1.0.1/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
|
||||
git.openprivacy.ca/openprivacy/log v1.0.2 h1:HLP4wsw4ljczFAelYnbObIs821z+jgMPCe8uODPnGQM=
|
||||
git.openprivacy.ca/openprivacy/log v1.0.2/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cretz/bine v0.1.1-0.20200124154328-f9f678b84cca/go.mod h1:6PF6fWAvYtwjRGkAuDEJeWNOv3a2hUouSP/yRYXmvHw=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
@ -80,14 +81,23 @@ go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg=
|
|||
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200206161412-a0c6ece9d31a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee h1:4yd7jl+vXjalO5ztz6Vc1VADv+S/80LGJmyl1ROJ2AI=
|
||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20210220033013-bdb1ca9a1e08 h1:h+GZ3ubjuWaQjGe8owMGcmMVCqs0xYJtRG5y2bpHaqU=
|
||||
golang.org/x/mobile v0.0.0-20210220033013-bdb1ca9a1e08/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
|
@ -119,8 +129,11 @@ golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
|||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e h1:FDhOuMEY4JVRztM/gsbk+IKUQ8kj74bxZrgw87eMMVc=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69 h1:yBHHx+XZqXJBm6Exke3N7V9gnlsyXxoCPEb1yVenjfk=
|
||||
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200625195345-7480c7b4547d h1:V1BGE5ZHrUIYZYNEm0i7jrPwSo3ks0HSn1TrartSqME=
|
||||
golang.org/x/tools v0.0.0-20200625195345-7480c7b4547d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
|
|
48
lib.go
48
lib.go
|
@ -11,6 +11,7 @@ import (
|
|||
"cwtch.im/cwtch/model/attr"
|
||||
"cwtch.im/cwtch/peer"
|
||||
contact "git.openprivacy.ca/flutter/libcwtch-go/features/contacts"
|
||||
"git.openprivacy.ca/flutter/libcwtch-go/features/groups"
|
||||
"git.openprivacy.ca/openprivacy/connectivity"
|
||||
|
||||
"encoding/json"
|
||||
|
@ -28,6 +29,10 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
profileOnion = event.Field("profileOnion")
|
||||
)
|
||||
|
||||
var application app.Application
|
||||
var eventHandler *utils.EventHandler
|
||||
var acnQueue event.Queue
|
||||
|
@ -97,12 +102,9 @@ func StartCwtch(appDir string, torPath string) {
|
|||
settings := utils.ReadGlobalSettings()
|
||||
settingsJson, _ := json.Marshal(settings)
|
||||
|
||||
|
||||
newApp.LoadProfiles("be gay do crime")
|
||||
application = newApp
|
||||
|
||||
|
||||
|
||||
// Send global settings to the UI...
|
||||
application.GetPrimaryBus().Publish(event.NewEvent(utils.UpdateGlobalSettings, map[event.Field]string{event.Data: string(settingsJson)}))
|
||||
log.Infof("libcwtch-go application launched")
|
||||
|
@ -149,6 +151,16 @@ func SendAppEvent(eventJson string) {
|
|||
log.Debugf("New Settings %v", globalSettings)
|
||||
utils.WriteGlobalSettings(globalSettings)
|
||||
|
||||
// Group Experiment Refresh
|
||||
groupHandler, err := groups.ExperimentGate(utils.ReadGlobalSettings().Experiments)
|
||||
if err == nil {
|
||||
for profileOnion := range application.ListPeers() {
|
||||
serverListForOnion := groupHandler.GetServerInfoList(application.GetPeer(profileOnion))
|
||||
serversListBytes, _ := json.Marshal(serverListForOnion)
|
||||
eventHandler.Push(event.NewEvent(groups.UpdateServerInfo, map[event.Field]string{"ProfileOnion": profileOnion, groups.ServerList: string(serversListBytes)}))
|
||||
}
|
||||
}
|
||||
|
||||
// Explicitly toggle blocking/unblocking of unknown connections for profiles
|
||||
// that have been loaded.
|
||||
if utils.ReadGlobalSettings().BlockUnknownConnections {
|
||||
|
@ -349,7 +361,7 @@ func AcceptContact(profile, handle string) {
|
|||
err := application.GetPeer(profile).SetContactAuthorization(handle, model.AuthApproved)
|
||||
if err == nil {
|
||||
eventHandler.Push(event.NewEvent(event.PeerStateChange, map[event.Field]string{
|
||||
"ProfileOnion": profile,
|
||||
profileOnion: profile,
|
||||
event.RemotePeer: handle,
|
||||
"authorization": string(model.AuthApproved),
|
||||
}))
|
||||
|
@ -367,7 +379,7 @@ func BlockContact(profile, handle string) {
|
|||
err := application.GetPeer(profile).SetContactAuthorization(handle, model.AuthBlocked)
|
||||
if err == nil {
|
||||
eventHandler.Push(event.NewEvent(event.PeerStateChange, map[event.Field]string{
|
||||
"ProfileOnion": profile,
|
||||
profileOnion: profile,
|
||||
event.RemotePeer: handle,
|
||||
"authorization": string(model.AuthBlocked),
|
||||
}))
|
||||
|
@ -385,9 +397,9 @@ func DebugResetContact(profile, handle string) {
|
|||
err := application.GetPeer(profile).SetContactAuthorization(handle, model.AuthUnknown)
|
||||
if err == nil {
|
||||
application.GetPrimaryBus().Publish(event.NewEvent(event.PeerStateChange, map[event.Field]string{
|
||||
"ProfileOnion": profile,
|
||||
event.RemotePeer: handle,
|
||||
"authorization": string(model.AuthUnknown),
|
||||
profileOnion: profile,
|
||||
event.RemotePeer: handle,
|
||||
"authorization": string(model.AuthUnknown),
|
||||
}))
|
||||
} else {
|
||||
log.Errorf("error resetting contact: %s", err.Error())
|
||||
|
@ -454,5 +466,25 @@ func ResetTor() {
|
|||
globalACN.Restart()
|
||||
}
|
||||
|
||||
//export c_CreateGroup
|
||||
func c_CreateGroup(profile_ptr *C.char, profile_len C.int, server_ptr *C.char, server_len C.int) {
|
||||
profile := C.GoStringN(profile_ptr, profile_len)
|
||||
server := C.GoStringN(server_ptr, server_len)
|
||||
CreateGroup(profile, server)
|
||||
}
|
||||
|
||||
func CreateGroup(profile string, server string) {
|
||||
peer := application.GetPeer(profile)
|
||||
_, err := groups.ExperimentGate(utils.ReadGlobalSettings().Experiments)
|
||||
if err == nil {
|
||||
gid, _, err := peer.StartGroup(server)
|
||||
if err == nil {
|
||||
log.Debugf("created group %v on %v: $v", profile, server, gid)
|
||||
} else {
|
||||
log.Errorf("error creating group or %v on server %v: %v", profile, server, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Leave as is, needed by ffi
|
||||
func main() {}
|
||||
|
|
|
@ -118,9 +118,18 @@ func (eh *EventHandler) handleAppBusEvent(e *event.Event) string {
|
|||
e.Data["Online"] = online
|
||||
|
||||
var contacts []Contact
|
||||
var servers []groups.Server
|
||||
for _, contact := range profile.GetContacts() {
|
||||
|
||||
// Only compile the server info if we have enabled the experiment...
|
||||
// Note that this means that this info can become stale if when first loaded the experiment
|
||||
// has been disabled and then is later re-enabled. As such we need to ensure that this list is
|
||||
// re-fetched when the group experiment is enabled via a dedicated ListServerInfo event...
|
||||
if profile.GetContact(contact).IsServer() {
|
||||
continue
|
||||
groupHandler, err := groups.ExperimentGate(ReadGlobalSettings().Experiments)
|
||||
if err == nil {
|
||||
servers = append(servers, groupHandler.GetServerInfo(contact, profile))
|
||||
}
|
||||
}
|
||||
|
||||
contactInfo := profile.GetContact(contact)
|
||||
|
@ -132,20 +141,25 @@ func (eh *EventHandler) handleAppBusEvent(e *event.Event) string {
|
|||
saveHistory = event.DeleteHistoryDefault
|
||||
}
|
||||
contacts = append(contacts, Contact{
|
||||
Name: name,
|
||||
Onion: contactInfo.Onion,
|
||||
Status: contactInfo.State,
|
||||
Picture: cpicPath,
|
||||
Name: name,
|
||||
Onion: contactInfo.Onion,
|
||||
Status: contactInfo.State,
|
||||
Picture: cpicPath,
|
||||
Authorization: string(contactInfo.Authorization),
|
||||
SaveHistory: saveHistory,
|
||||
Messages: contactInfo.Timeline.Len(),
|
||||
Unread: 0,
|
||||
LastMessage: strconv.Itoa(getLastMessageTime(&contactInfo.Timeline)),
|
||||
SaveHistory: saveHistory,
|
||||
Messages: contactInfo.Timeline.Len(),
|
||||
Unread: 0,
|
||||
LastMessage: strconv.Itoa(getLastMessageTime(&contactInfo.Timeline)),
|
||||
})
|
||||
}
|
||||
|
||||
bytes, _ := json.Marshal(contacts)
|
||||
e.Data["ContactsJson"] = string(bytes)
|
||||
|
||||
// Marshal the server list into the new peer event...
|
||||
serversListBytes, _ := json.Marshal(servers)
|
||||
e.Data[groups.ServerList] = string(serversListBytes)
|
||||
|
||||
log.Infof("contactsJson %v", e.Data["ContactsJson"])
|
||||
}
|
||||
|
||||
|
|
|
@ -21,24 +21,24 @@ const GlobalSettingsFilename = "ui.globals"
|
|||
const saltFile = "SALT"
|
||||
|
||||
type GlobalSettings struct {
|
||||
Locale string
|
||||
Theme string
|
||||
PreviousPid int64
|
||||
ExperimentsEnabled bool
|
||||
Experiments map[string]bool
|
||||
Locale string
|
||||
Theme string
|
||||
PreviousPid int64
|
||||
ExperimentsEnabled bool
|
||||
Experiments map[string]bool
|
||||
BlockUnknownConnections bool
|
||||
StateRootPane int
|
||||
FirstTime bool
|
||||
StateRootPane int
|
||||
FirstTime bool
|
||||
}
|
||||
|
||||
var DefaultGlobalSettings = GlobalSettings{
|
||||
Locale: "en",
|
||||
Theme: "light",
|
||||
PreviousPid: -1,
|
||||
ExperimentsEnabled: false,
|
||||
Experiments: make(map[string]bool),
|
||||
StateRootPane: 0,
|
||||
FirstTime: true,
|
||||
Locale: "en",
|
||||
Theme: "light",
|
||||
PreviousPid: -1,
|
||||
ExperimentsEnabled: false,
|
||||
Experiments: make(map[string]bool),
|
||||
StateRootPane: 0,
|
||||
FirstTime: true,
|
||||
BlockUnknownConnections: false,
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue