forked from Silverfish/proton-bridge
298 lines
7.2 KiB
Go
298 lines
7.2 KiB
Go
// Copyright (c) 2022 Proton AG
|
|
//
|
|
// This file is part of Proton Mail Bridge.
|
|
//
|
|
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
//go:build build_qt
|
|
// +build build_qt
|
|
|
|
package qt
|
|
|
|
import (
|
|
"sync"
|
|
|
|
"github.com/ProtonMail/proton-bridge/v2/internal/events"
|
|
"github.com/ProtonMail/proton-bridge/v2/internal/frontend/types"
|
|
"github.com/therecipe/qt/core"
|
|
)
|
|
|
|
func init() {
|
|
QMLUser_QRegisterMetaType()
|
|
QMLUserModel_QRegisterMetaType()
|
|
}
|
|
|
|
// QMLUserModel stores list of of users
|
|
type QMLUserModel struct {
|
|
core.QAbstractListModel
|
|
|
|
_ map[int]*core.QByteArray `property:"roles"`
|
|
_ int `property:"count"`
|
|
_ func() `constructor:"init"`
|
|
_ func(row int) *core.QVariant `slot:"get"`
|
|
|
|
userIDs []string
|
|
userByID map[string]*QMLUser
|
|
access sync.RWMutex
|
|
f *FrontendQt
|
|
}
|
|
|
|
func (um *QMLUserModel) init() {
|
|
um.access.Lock()
|
|
defer um.access.Unlock()
|
|
um.SetCount(0)
|
|
um.ConnectRowCount(um.rowCount)
|
|
um.ConnectRoleNames(um.roleNames)
|
|
um.ConnectData(um.data)
|
|
um.ConnectGet(um.get)
|
|
um.ConnectCount(func() int {
|
|
um.access.RLock()
|
|
defer um.access.RUnlock()
|
|
return len(um.userIDs)
|
|
})
|
|
um.userIDs = []string{}
|
|
um.userByID = map[string]*QMLUser{}
|
|
}
|
|
|
|
func (um *QMLUserModel) roleNames() map[int]*core.QByteArray {
|
|
return map[int]*core.QByteArray{
|
|
int(core.Qt__DisplayRole): core.NewQByteArray2("user", -1),
|
|
}
|
|
}
|
|
|
|
func (um *QMLUserModel) data(index *core.QModelIndex, property int) *core.QVariant {
|
|
if !index.IsValid() {
|
|
um.f.log.WithField("size", len(um.userIDs)).Info("Trying to get user by invalid index")
|
|
return core.NewQVariant()
|
|
}
|
|
return um.get(index.Row())
|
|
}
|
|
|
|
func (um *QMLUserModel) get(index int) *core.QVariant {
|
|
um.access.Lock()
|
|
defer um.access.Unlock()
|
|
if index < 0 || index >= len(um.userIDs) {
|
|
um.f.log.WithField("index", index).WithField("size", len(um.userIDs)).Info("Trying to get user by wrong index")
|
|
return core.NewQVariant()
|
|
}
|
|
|
|
u, err := um.getUserByID(um.userIDs[index])
|
|
if err != nil {
|
|
um.f.log.WithError(err).Error("Cannot get user from backend")
|
|
return core.NewQVariant()
|
|
}
|
|
return u.ToVariant()
|
|
}
|
|
|
|
func (um *QMLUserModel) getUserByID(userID string) (*QMLUser, error) {
|
|
u, ok := um.userByID[userID]
|
|
if ok {
|
|
return u, nil
|
|
}
|
|
|
|
user, err := um.f.bridge.GetUser(userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
u = newQMLUserFromBacked(um, user)
|
|
um.userByID[userID] = u
|
|
return u, nil
|
|
}
|
|
|
|
func (um *QMLUserModel) rowCount(*core.QModelIndex) int {
|
|
um.access.RLock()
|
|
defer um.access.RUnlock()
|
|
return len(um.userIDs)
|
|
}
|
|
|
|
func (um *QMLUserModel) setCount() {
|
|
um.SetCount(len(um.userIDs))
|
|
}
|
|
|
|
func (um *QMLUserModel) addUser(userID string) {
|
|
um.BeginInsertRows(core.NewQModelIndex(), len(um.userIDs), len(um.userIDs))
|
|
um.access.Lock()
|
|
if um.indexByIDNotSafe(userID) < 0 {
|
|
um.userIDs = append(um.userIDs, userID)
|
|
}
|
|
um.access.Unlock()
|
|
um.EndInsertRows()
|
|
um.setCount()
|
|
}
|
|
|
|
func (um *QMLUserModel) removeUser(row int) {
|
|
um.BeginRemoveRows(core.NewQModelIndex(), row, row)
|
|
um.access.Lock()
|
|
id := um.userIDs[row]
|
|
um.userIDs = append(um.userIDs[:row], um.userIDs[row+1:]...)
|
|
delete(um.userByID, id)
|
|
um.access.Unlock()
|
|
um.EndRemoveRows()
|
|
um.setCount()
|
|
}
|
|
|
|
func (um *QMLUserModel) clear() {
|
|
um.BeginResetModel()
|
|
um.access.Lock()
|
|
um.userIDs = []string{}
|
|
um.userByID = map[string]*QMLUser{}
|
|
um.SetCount(0)
|
|
um.access.Unlock()
|
|
um.EndResetModel()
|
|
}
|
|
|
|
func (um *QMLUserModel) load() {
|
|
um.clear()
|
|
|
|
for _, user := range um.f.bridge.GetUsers() {
|
|
um.addUser(user.ID())
|
|
|
|
// We need mark that all existing users already saw setup
|
|
// guide. This it is OK to construct QML here because it is in main thread.
|
|
u, err := um.getUserByID(user.ID())
|
|
if err != nil {
|
|
um.f.log.WithError(err).Error("Cannot get QMLUser while loading users")
|
|
}
|
|
u.SetSetupGuideSeen(true)
|
|
}
|
|
|
|
// If there are no active accounts.
|
|
if um.Count() == 0 {
|
|
um.f.log.Info("No active accounts")
|
|
}
|
|
}
|
|
|
|
func (um *QMLUserModel) userChanged(userID string) {
|
|
defer um.f.eventListener.Emit(events.UserChangeDone, userID)
|
|
|
|
index := um.indexByIDNotSafe(userID)
|
|
user, err := um.f.bridge.GetUser(userID)
|
|
|
|
if user == nil || err != nil {
|
|
if index >= 0 { // delete existing user
|
|
um.removeUser(index)
|
|
}
|
|
// if not exiting do nothing
|
|
return
|
|
}
|
|
|
|
if index < 0 { // add non-existing user
|
|
um.addUser(userID)
|
|
return
|
|
}
|
|
|
|
// update exiting user
|
|
um.userByID[userID].update(user)
|
|
}
|
|
|
|
func (um *QMLUserModel) indexByIDNotSafe(wantID string) int {
|
|
for i, id := range um.userIDs {
|
|
if id == wantID {
|
|
return i
|
|
}
|
|
}
|
|
return -1
|
|
}
|
|
|
|
func (um *QMLUserModel) indexByID(id string) int {
|
|
um.access.RLock()
|
|
defer um.access.RUnlock()
|
|
|
|
return um.indexByIDNotSafe(id)
|
|
}
|
|
|
|
// QMLUser holds data, slots and signals and for user.
|
|
type QMLUser struct {
|
|
core.QObject
|
|
|
|
_ string `property:"username"`
|
|
_ string `property:"avatarText"`
|
|
_ bool `property:"loggedIn"`
|
|
_ bool `property:"splitMode"`
|
|
_ bool `property:"setupGuideSeen"`
|
|
_ float32 `property:"usedBytes"`
|
|
_ float32 `property:"totalBytes"`
|
|
_ string `property:"password"`
|
|
_ []string `property:"addresses"`
|
|
|
|
_ func(makeItActive bool) `slot:"toggleSplitMode"`
|
|
_ func() `signal:"toggleSplitModeFinished"`
|
|
_ func() `slot:"logout"`
|
|
_ func() `slot:"remove"`
|
|
_ func(address string) `slot:"configureAppleMail"`
|
|
|
|
ID string
|
|
}
|
|
|
|
func newQMLUserFromBacked(um *QMLUserModel, user types.User) *QMLUser {
|
|
qu := NewQMLUser(um)
|
|
qu.ID = user.ID()
|
|
|
|
qu.update(user)
|
|
|
|
qu.ConnectToggleSplitMode(func(activateSplitMode bool) {
|
|
go func() {
|
|
defer um.f.panicHandler.HandlePanic()
|
|
defer qu.ToggleSplitModeFinished()
|
|
if activateSplitMode == user.IsCombinedAddressMode() {
|
|
user.SwitchAddressMode()
|
|
}
|
|
qu.SetSplitMode(!user.IsCombinedAddressMode())
|
|
}()
|
|
})
|
|
|
|
qu.ConnectLogout(func() {
|
|
qu.SetLoggedIn(false)
|
|
go func() {
|
|
defer um.f.panicHandler.HandlePanic()
|
|
user.Logout()
|
|
}()
|
|
})
|
|
|
|
qu.ConnectRemove(func() {
|
|
go func() {
|
|
defer um.f.panicHandler.HandlePanic()
|
|
|
|
// TODO: remove preferences
|
|
if err := um.f.bridge.DeleteUser(qu.ID, false); err != nil {
|
|
um.f.log.WithError(err).Error("Failed to remove user")
|
|
// TODO: notification
|
|
}
|
|
}()
|
|
})
|
|
|
|
qu.ConnectConfigureAppleMail(func(address string) {
|
|
go func() {
|
|
defer um.f.panicHandler.HandlePanic()
|
|
um.f.configureAppleMail(qu.ID, address)
|
|
}()
|
|
})
|
|
|
|
return qu
|
|
}
|
|
|
|
func (qu *QMLUser) update(user types.User) {
|
|
username := user.Username()
|
|
qu.SetAvatarText(getInitials(username))
|
|
qu.SetUsername(username)
|
|
qu.SetLoggedIn(user.IsConnected())
|
|
qu.SetSplitMode(!user.IsCombinedAddressMode())
|
|
qu.SetSetupGuideSeen(false)
|
|
qu.SetUsedBytes(float32(user.UsedBytes()))
|
|
qu.SetTotalBytes(float32(user.TotalBytes()))
|
|
qu.SetPassword(user.GetBridgePassword())
|
|
qu.SetAddresses(user.GetAddresses())
|
|
}
|