Add Contact Flow
This commit is contained in:
parent
da82e25575
commit
fd53fadef9
|
@ -0,0 +1,41 @@
|
|||
package contact
|
||||
|
||||
import (
|
||||
"cwtch.im/cwtch/model"
|
||||
"cwtch.im/cwtch/peer"
|
||||
"git.openprivacy.ca/flutter/libcwtch-go/features"
|
||||
"git.openprivacy.ca/openprivacy/connectivity/tor"
|
||||
)
|
||||
|
||||
// Functionality groups some common UI triggered functions for contacts...
|
||||
type Functionality struct {
|
||||
}
|
||||
|
||||
const addContactPrefix = "addcontact"
|
||||
|
||||
const sendMessagePrefix = "sendmessage"
|
||||
|
||||
// FunctionalityGate returns contact.Functionality always
|
||||
func FunctionalityGate(experimentMap map[string]bool) (*Functionality, error) {
|
||||
return new(Functionality), nil
|
||||
}
|
||||
|
||||
// SendMessage handles sending messages to contacts
|
||||
func (pf *Functionality) SendMessage(peer peer.SendMessages, handle string, message string) features.Response {
|
||||
eventID := peer.SendMessageToPeer(handle, message)
|
||||
return features.ConstructResponse(sendMessagePrefix, eventID)
|
||||
}
|
||||
|
||||
// HandleImportString handles contact import strings
|
||||
func (pf *Functionality) HandleImportString(peer peer.ModifyContactsAndPeers, importString string) features.Response {
|
||||
if tor.IsValidHostname(importString) {
|
||||
if peer.GetContact(importString) == nil {
|
||||
peer.AddContact(importString, importString, model.AuthApproved)
|
||||
// Implicit Peer Attempt
|
||||
peer.PeerWithOnion(importString)
|
||||
return features.ConstructResponse(addContactPrefix, "success")
|
||||
}
|
||||
return features.ConstructResponse(addContactPrefix, "contact_already_exists")
|
||||
}
|
||||
return features.ConstructResponse(addContactPrefix, "invalid_import_string")
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
package contact
|
||||
|
||||
import (
|
||||
"cwtch.im/cwtch/model"
|
||||
"git.openprivacy.ca/flutter/libcwtch-go/features"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const ValidHostname = "openpravyvc6spbd4flzn4g2iqu4sxzsizbtb5aqec25t76dnoo5w7yd"
|
||||
|
||||
type MockPeer struct {
|
||||
hasContact bool
|
||||
addContact bool
|
||||
peerRequest bool
|
||||
}
|
||||
|
||||
func (m MockPeer) GetContacts() []string {
|
||||
panic("should never be called")
|
||||
}
|
||||
|
||||
func (m MockPeer) GetContact(s string) *model.PublicProfile {
|
||||
if m.hasContact {
|
||||
return &(model.GenerateNewProfile("").PublicProfile)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m MockPeer) GetContactAttribute(s string, s2 string) (string, bool) {
|
||||
panic("should never be called")
|
||||
}
|
||||
|
||||
func (m *MockPeer) AddContact(nick, onion string, authorization model.Authorization) {
|
||||
m.addContact = true
|
||||
}
|
||||
|
||||
func (m MockPeer) SetContactAuthorization(s string, authorization model.Authorization) error {
|
||||
panic("should never be called")
|
||||
}
|
||||
|
||||
func (m MockPeer) SetContactAttribute(s string, s2 string, s3 string) {
|
||||
panic("should never be called")
|
||||
}
|
||||
|
||||
func (m MockPeer) DeleteContact(s string) {
|
||||
panic("should never be called")
|
||||
}
|
||||
|
||||
func (m *MockPeer) PeerWithOnion(s string) {
|
||||
m.peerRequest = true
|
||||
}
|
||||
|
||||
func (m MockPeer) JoinServer(s string) error {
|
||||
panic("should never be called")
|
||||
}
|
||||
|
||||
func TestContactFunctionality_InValidHostname(t *testing.T) {
|
||||
cf, _ := FunctionalityGate(map[string]bool{})
|
||||
|
||||
peer := &MockPeer{
|
||||
hasContact: false,
|
||||
addContact: false,
|
||||
peerRequest: false,
|
||||
}
|
||||
|
||||
response := cf.HandleImportString(peer, "")
|
||||
|
||||
if peer.addContact || peer.peerRequest {
|
||||
t.Fatalf("HandleImportString for a malformed import string should have no resulted in addContact or a peerRequest: %v", peer)
|
||||
}
|
||||
|
||||
if response.Error() != features.ConstructResponse(addContactPrefix, "invalid_import_string").Error() {
|
||||
t.Fatalf("Response to a successful import is malformed: %v", response)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestContactFunctionality_ValidHostnameExistingContact(t *testing.T) {
|
||||
cf, _ := FunctionalityGate(map[string]bool{})
|
||||
|
||||
peer := &MockPeer{
|
||||
hasContact: true,
|
||||
addContact: false,
|
||||
peerRequest: false,
|
||||
}
|
||||
|
||||
response := cf.HandleImportString(peer, ValidHostname)
|
||||
|
||||
if peer.addContact || peer.peerRequest {
|
||||
t.Fatalf("HandleImportString for a valid string should not call addContact or a peerRequest when the contact already exists: %v", peer)
|
||||
}
|
||||
|
||||
if response.Error() != features.ConstructResponse(addContactPrefix, "contact_already_exists").Error() {
|
||||
t.Fatalf("Response to a successful import is malformed: %v", response)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestContactFunctionality_ValidHostnameUnknownContact(t *testing.T) {
|
||||
cf, _ := FunctionalityGate(map[string]bool{})
|
||||
|
||||
peer := &MockPeer{
|
||||
hasContact: false,
|
||||
addContact: false,
|
||||
peerRequest: false,
|
||||
}
|
||||
|
||||
response := cf.HandleImportString(peer, ValidHostname)
|
||||
|
||||
if peer.addContact && peer.peerRequest {
|
||||
if response.Error() != features.ConstructResponse(addContactPrefix, "success").Error() {
|
||||
t.Fatalf("Response to a successful import is malformed: %v", response)
|
||||
}
|
||||
} else {
|
||||
t.Fatalf("HandleImportString for a valid import string should have resulted in addContact or a peerRequest: %v", peer)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package features
|
||||
|
||||
import "errors"
|
||||
|
||||
// Response is a wrapper to better semantically convey the response type...
|
||||
type Response error
|
||||
|
||||
const errorSeparator = "."
|
||||
|
||||
// ConstructResponse is a helper function for creating Response structures.
|
||||
func ConstructResponse(prefix string, error string) Response {
|
||||
return errors.New(prefix + errorSeparator + error)
|
||||
}
|
4
go.mod
4
go.mod
|
@ -3,7 +3,7 @@ module git.openprivacy.ca/flutter/libcwtch-go
|
|||
go 1.15
|
||||
|
||||
require (
|
||||
cwtch.im/cwtch v0.6.0
|
||||
cwtch.im/cwtch v0.6.3
|
||||
git.openprivacy.ca/openprivacy/connectivity v1.3.3
|
||||
git.openprivacy.ca/openprivacy/log v1.0.2
|
||||
)
|
||||
)
|
6
go.sum
6
go.sum
|
@ -2,6 +2,12 @@ cwtch.im/cwtch v0.5.1 h1:84foD/HBebPbA4gUEwp+feakeHkD3Di53Q3FnSbqDMM=
|
|||
cwtch.im/cwtch v0.5.1/go.mod h1:snHZIZwRQPAZG2LRZsN5SpAIbeR597VJoDS+KHm7q9w=
|
||||
cwtch.im/cwtch v0.6.0 h1:LaIRs8dDtnSr/MVFX3giTxnYwSyAIu0w55eZWWO7VZI=
|
||||
cwtch.im/cwtch v0.6.0/go.mod h1:snHZIZwRQPAZG2LRZsN5SpAIbeR597VJoDS+KHm7q9w=
|
||||
cwtch.im/cwtch v0.6.1 h1:NqfLPS7k3rRi5A0qf/tHoq42BEc/K89LHgZqsBs7Luk=
|
||||
cwtch.im/cwtch v0.6.1/go.mod h1:snHZIZwRQPAZG2LRZsN5SpAIbeR597VJoDS+KHm7q9w=
|
||||
cwtch.im/cwtch v0.6.2 h1:UqwVnxNXvhhG7yGpcY9aXyq0dy31XzjV708BWCHHIms=
|
||||
cwtch.im/cwtch v0.6.2/go.mod h1:snHZIZwRQPAZG2LRZsN5SpAIbeR597VJoDS+KHm7q9w=
|
||||
cwtch.im/cwtch v0.6.3 h1:AifcbxK60UTeOiOt0ur8PLQeDCuljQLhLqrAOO/8guA=
|
||||
cwtch.im/cwtch v0.6.3/go.mod h1:snHZIZwRQPAZG2LRZsN5SpAIbeR597VJoDS+KHm7q9w=
|
||||
cwtch.im/tapir v0.2.1 h1:t1YJB9q5sV1A9xwiiwL6WVfw3dwQWLoecunuzT1PQtw=
|
||||
cwtch.im/tapir v0.2.1/go.mod h1:xzzZ28adyUXNkYL1YodcHsAiTt3IJ8Loc29YVn9mIEQ=
|
||||
git.openprivacy.ca/openprivacy/bine v0.0.4 h1:CO7EkGyz+jegZ4ap8g5NWRuDHA/56KKvGySR6OBPW+c=
|
||||
|
|
13
lib.go
13
lib.go
|
@ -10,6 +10,7 @@ import (
|
|||
"cwtch.im/cwtch/model"
|
||||
"cwtch.im/cwtch/model/attr"
|
||||
"cwtch.im/cwtch/peer"
|
||||
contact "git.openprivacy.ca/flutter/libcwtch-go/features/contacts"
|
||||
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
@ -73,6 +74,7 @@ func StartCwtch(appDir string, torPath string) {
|
|||
newApp.GetPrimaryBus().Subscribe(event.ACNStatus, acnQueue)
|
||||
newApp.GetPrimaryBus().Subscribe(utils.UpdateGlobalSettings, acnQueue)
|
||||
newApp.GetPrimaryBus().Subscribe(utils.SetLoggingLevel, acnQueue)
|
||||
newApp.GetPrimaryBus().Subscribe(event.AppError, acnQueue)
|
||||
|
||||
eventHandler = utils.NewEventHandler(newApp)
|
||||
|
||||
|
@ -168,6 +170,11 @@ func c_SendProfileEvent(onion_ptr *C.char, onion_len C.int, json_ptr *C.char, js
|
|||
SendProfileEvent(onion, eventJson)
|
||||
}
|
||||
|
||||
const (
|
||||
AddContact = event.Type("AddContact")
|
||||
ImportString = event.Field("ImportString")
|
||||
)
|
||||
|
||||
// SendProfileEvent is a generic method for Rebroadcasting Profile Events from a UI
|
||||
func SendProfileEvent(onion string, eventJson string) {
|
||||
// Convert the Event Json back to a typed Event Struct, this will make the
|
||||
|
@ -185,6 +192,12 @@ func SendProfileEvent(onion string, eventJson string) {
|
|||
// We need to update the local cache
|
||||
// Ideally I think this would be pushed back into Cwtch
|
||||
switch new_event.EventType {
|
||||
case AddContact:
|
||||
// Peer Functionality is Always Enabled, so we forgo the existence check...
|
||||
// TODO: Combine with GroupFunctionality to make a meta-handleimportstring that can do both!
|
||||
pf, _ := contact.FunctionalityGate(utils.ReadGlobalSettings().Experiments)
|
||||
err := pf.HandleImportString(peer, new_event.Data[ImportString])
|
||||
eventHandler.Push(event.NewEvent(event.AppError, map[event.Field]string{event.Data: err.Error()}))
|
||||
case event.SetAttribute:
|
||||
peer.SetAttribute(new_event.Data[event.Key], new_event.Data[event.Data])
|
||||
case event.SetPeerAttribute:
|
||||
|
|
|
@ -290,7 +290,11 @@ func (eh *EventHandler) forwardProfileMessages(onion string, q event.Queue) {
|
|||
// TODO: graceful shutdown, via an injected event of special QUIT type exiting loop/go routine
|
||||
for {
|
||||
e := q.Next()
|
||||
ev := EventProfileEnvelope{Event: *e, Profile: onion}
|
||||
ev := EventProfileEnvelope{Event: e, Profile: onion}
|
||||
eh.profileEvents <- ev
|
||||
}
|
||||
}
|
||||
|
||||
func (eh *EventHandler) Push(newEvent event.Event) {
|
||||
eh.appBusQueue.Publish(newEvent)
|
||||
}
|
||||
|
|
|
@ -245,6 +245,7 @@ func NewManager(profile string, gcd *GrandCentralDispatcher) Manager {
|
|||
// uiManager.AddContact(onion)
|
||||
// (handle string, displayName string, image string, badge int, status int, authorization string, loading bool, lastMsgTime int)
|
||||
func EnrichNewPeer(handle string, ph *PeerHelper, ev *EventProfileEnvelope) error {
|
||||
log.Infof("Enriching New Peer %v", handle)
|
||||
if ph.IsGroup(handle) {
|
||||
group := ph.peer.GetGroup(handle)
|
||||
if group != nil {
|
||||
|
@ -266,11 +267,19 @@ func EnrichNewPeer(handle string, ph *PeerHelper, ev *EventProfileEnvelope) erro
|
|||
ev.Event.Data["picture"] = ph.GetProfilePic(handle)
|
||||
|
||||
ev.Event.Data["nick"] = ph.GetNick(handle)
|
||||
ev.Event.Data["status"] = strconv.Itoa(int(connections.ConnectionStateToType()[contact.State]))
|
||||
|
||||
// TODO Replace this if with a better flow that separates New Contacts and Peering Updates
|
||||
if contact.State == "" {
|
||||
// Will be disconnected to start
|
||||
ev.Event.Data["status"] = connections.ConnectionStateName[connections.DISCONNECTED]
|
||||
} else {
|
||||
ev.Event.Data["status"] = contact.State
|
||||
}
|
||||
ev.Event.Data["authorization"] = string(contact.Authorization)
|
||||
ev.Event.Data["loading"] = "false"
|
||||
ev.Event.Data["lastMsgTime"] = strconv.Itoa(getLastMessageTime(&contact.Timeline))
|
||||
} else {
|
||||
log.Errorf("Failed to find contact: %v", handle)
|
||||
}
|
||||
} else {
|
||||
// could be a server?
|
||||
|
|
Loading…
Reference in New Issue