diff options
-rw-r--r-- | gui/backend.go | 11 | ||||
-rw-r--r-- | gui/handlers.cpp | 16 | ||||
-rw-r--r-- | gui/handlers.h | 4 | ||||
-rw-r--r-- | gui/qml/DonateDialog.qml | 5 | ||||
-rw-r--r-- | gui/qml/main.qml | 7 | ||||
-rw-r--r-- | pkg/backend/api.go | 29 | ||||
-rw-r--r-- | pkg/backend/bitmask.go | 19 | ||||
-rw-r--r-- | pkg/backend/donate.go | 35 | ||||
-rw-r--r-- | pkg/backend/status.go | 103 | ||||
-rw-r--r-- | pkg/config/gui.go | 14 |
10 files changed, 158 insertions, 85 deletions
diff --git a/gui/backend.go b/gui/backend.go index 9c65025..ab96edb 100644 --- a/gui/backend.go +++ b/gui/backend.go @@ -31,9 +31,14 @@ func Quit() { } -//export ToggleDonate -func ToggleDonate() { - backend.ToggleDonate() +//export DonateAccepted +func DonateAccepted() { + backend.DonateAccepted() +} + +//export DonateRejected +func DonateRejected() { + backend.DonateRejected() } //export SubscribeToEvent diff --git a/gui/handlers.cpp b/gui/handlers.cpp index e37de2d..39a5746 100644 --- a/gui/handlers.cpp +++ b/gui/handlers.cpp @@ -1,5 +1,7 @@ #include <QTimer> #include <QDebug> +#include <QDesktopServices> +#include <QUrl> #include "handlers.h" #include "lib/libgoshim.h" @@ -23,9 +25,19 @@ void Backend::unblock() Unblock(); } -void Backend::toggleDonate() +void Backend::donateAccepted() { - ToggleDonate(); + DonateAccepted(); +} + +void Backend::donateRejected() +{ + DonateRejected(); +} + +void Backend::openURL(QString link) +{ + QDesktopServices::openUrl(QUrl(link)); } void Backend::quit() diff --git a/gui/handlers.h b/gui/handlers.h index 3fe9eed..e65fd5e 100644 --- a/gui/handlers.h +++ b/gui/handlers.h @@ -33,7 +33,9 @@ public slots: void switchOn(); void switchOff(); void unblock(); - void toggleDonate(); + void donateAccepted(); + void donateRejected(); + void openURL(QString link); void quit(); }; diff --git a/gui/qml/DonateDialog.qml b/gui/qml/DonateDialog.qml index b7431ab..eb761a4 100644 --- a/gui/qml/DonateDialog.qml +++ b/gui/qml/DonateDialog.qml @@ -16,12 +16,13 @@ MessageDialog { onAccepted: { if (backend) { - backend.donateAccepted(true) + backend.openURL(ctx.donateURL) + backend.donateAccepted() } } onRejected: { if (backend) { - backend.donateAccepted(false) + backend.donateRejected() } } } diff --git a/gui/qml/main.qml b/gui/qml/main.qml index 98eac80..65c09cb 100644 --- a/gui/qml/main.qml +++ b/gui/qml/main.qml @@ -16,10 +16,9 @@ ApplicationWindow { target: jsonModel onDataChanged: { ctx = JSON.parse(jsonModel.getJson()); - if (ctx.donate == 'true') { + if (ctx.donateDialog == 'true') { console.debug(jsonModel.getJson()) donate.visible = true - backend.toggleDonate() } } } @@ -171,7 +170,9 @@ ApplicationWindow { MenuItem { text: qsTr("Donate...") visible: true - //onTriggered: donate.open() + onTriggered: { + donate.visible = true + } } MenuItem { diff --git a/pkg/backend/api.go b/pkg/backend/api.go index f924cbd..5cb0304 100644 --- a/pkg/backend/api.go +++ b/pkg/backend/api.go @@ -6,6 +6,7 @@ import ( "C" "fmt" "log" + "time" "unsafe" "0xacab.org/leap/bitmask-vpn/pkg/bitmask" @@ -23,18 +24,24 @@ func SwitchOff() { } func Unblock() { + //TODO fmt.Println("unblock... [not implemented]") } func Quit() { if ctx.Status != off { go setStatus(stopping) + ctx.cfg.SetUserStoppedVPN(true) stopVPN() } } -func ToggleDonate() { - toggleDonate() +func DonateAccepted() { + donateAccepted() +} + +func DonateRejected() { + donateRejected() } func SubscribeToEvent(event string, f unsafe.Pointer) { @@ -42,21 +49,23 @@ func SubscribeToEvent(event string, f unsafe.Pointer) { } func InitializeBitmaskContext() { - pi := bitmask.GetConfiguredProvider() + p := bitmask.GetConfiguredProvider() initOnce.Do(func() { - initializeContext(pi.Provider, pi.AppName) + initializeContext( + p.Provider, p.AppName) }) go ctx.updateStatus() - /* DEBUG - timer := time.NewTimer(time.Second * 3) go func() { - <-timer.C - fmt.Println("donate timer fired") - toggleDonate() + if needsDonationReminder() { + // wait a bit before launching reminder + timer := time.NewTimer(time.Minute * 5) + <-timer.C + showDonate() + } + }() - */ } func RefreshContext() *C.char { diff --git a/pkg/backend/bitmask.go b/pkg/backend/bitmask.go index 07d27ea..8fd2367 100644 --- a/pkg/backend/bitmask.go +++ b/pkg/backend/bitmask.go @@ -5,6 +5,7 @@ import ( "os" "0xacab.org/leap/bitmask-vpn/pkg/bitmask" + "0xacab.org/leap/bitmask-vpn/pkg/config" ) func initializeBitmask() { @@ -19,6 +20,7 @@ func initializeBitmask() { log.Println("error: cannot initialize bitmask") } ctx.bm = b + ctx.cfg = config.ParseConfig() } func startVPN() { @@ -36,16 +38,25 @@ func stopVPN() { } } +func wantDonations() bool { + if config.AskForDonations == "true" { + return true + } + return false +} + // initializeContext initializes an empty connStatus and assigns it to the // global ctx holder. This is expected to be called only once, so the public // api uses the sync.Once primitive to call this. func initializeContext(provider, appName string) { var st status = off ctx = &connectionCtx{ - AppName: appName, - Provider: provider, - Donate: false, - Status: st, + AppName: appName, + Provider: provider, + DonateURL: config.DonateURL, + AskForDonations: wantDonations(), + DonateDialog: false, + Status: st, } go trigger(OnStatusChanged) initializeBitmask() diff --git a/pkg/backend/donate.go b/pkg/backend/donate.go new file mode 100644 index 0000000..d216687 --- /dev/null +++ b/pkg/backend/donate.go @@ -0,0 +1,35 @@ +package backend + +import ( + "log" + "time" +) + +func needsDonationReminder() bool { + return ctx.cfg.NeedsDonationReminder() +} + +func donateAccepted() { + stmut.Lock() + defer stmut.Unlock() + ctx.DonateDialog = false + log.Println("marking as donated") + ctx.cfg.SetDonated() + go trigger(OnStatusChanged) +} + +func donateRejected() { + timer := time.NewTimer(time.Hour) + go func() { + <-timer.C + showDonate() + }() +} + +func showDonate() { + stmut.Lock() + defer stmut.Unlock() + ctx.DonateDialog = true + ctx.cfg.SetLastReminded() + go trigger(OnStatusChanged) +} diff --git a/pkg/backend/status.go b/pkg/backend/status.go index e2d31db..7e9f211 100644 --- a/pkg/backend/status.go +++ b/pkg/backend/status.go @@ -6,6 +6,7 @@ import ( "log" "0xacab.org/leap/bitmask-vpn/pkg/bitmask" + "0xacab.org/leap/bitmask-vpn/pkg/config" ) const ( @@ -20,60 +21,20 @@ const ( // if we ever switch again to a provider-agnostic app, we should keep a map here. var ctx *connectionCtx -// the status type reflects the current VPN status. Go code is responsible for updating -// it; the C gui just watches its changes and pulls its updates via the serialized -// context object. - -type status int - -const ( - off status = iota - starting - on - stopping - failed - unknown -) - -func (s status) String() string { - return [...]string{offStr, startingStr, onStr, stoppingStr, failedStr}[s] -} - -func (s status) MarshalJSON() ([]byte, error) { - b := bytes.NewBufferString(`"`) - b.WriteString(s.String()) - b.WriteString(`"`) - return b.Bytes(), nil -} - -func (s status) fromString(st string) status { - switch st { - case offStr: - return off - case startingStr: - return starting - case onStr: - return on - case stoppingStr: - return stopping - case failedStr: - return failed - default: - return unknown - } -} - // The connectionCtx keeps the global state that is passed around to C-land. It // also serves as the primary way of passing requests from the frontend to the // Go-core, by letting the UI write some of these variables and processing // them. type connectionCtx struct { - AppName string `json:"appName"` - Provider string `json:"provider"` - Donate bool `json:"donate"` - Status status `json:"status"` - bm bitmask.Bitmask + AppName string `json:"appName"` + Provider string `json:"provider"` + AskForDonations bool `json:"askForDonations"` + DonateDialog bool `json:"donateDialog"` + DonateURL string `json:"donateURL"` + Status status `json:"status"` + bm bitmask.Bitmask + cfg *config.Config } func (c connectionCtx) toJson() ([]byte, error) { @@ -110,11 +71,47 @@ func setStatus(st status) { go trigger(OnStatusChanged) } -func toggleDonate() { - stmut.Lock() - defer stmut.Unlock() - ctx.Donate = !ctx.Donate - go trigger(OnStatusChanged) +// the status type reflects the current VPN status. Go code is responsible for updating +// it; the C gui just watches its changes and pulls its updates via the serialized +// context object. + +type status int + +const ( + off status = iota + starting + on + stopping + failed + unknown +) + +func (s status) String() string { + return [...]string{offStr, startingStr, onStr, stoppingStr, failedStr}[s] +} + +func (s status) MarshalJSON() ([]byte, error) { + b := bytes.NewBufferString(`"`) + b.WriteString(s.String()) + b.WriteString(`"`) + return b.Bytes(), nil +} + +func (s status) fromString(st string) status { + switch st { + case offStr: + return off + case startingStr: + return starting + case onStr: + return on + case stoppingStr: + return stopping + case failedStr: + return failed + default: + return unknown + } } func setStatusFromStr(stStr string) { diff --git a/pkg/config/gui.go b/pkg/config/gui.go index ce3f14d..5fa4886 100644 --- a/pkg/config/gui.go +++ b/pkg/config/gui.go @@ -37,7 +37,7 @@ var ( // Config holds the configuration of the systray type Config struct { file struct { - LastNotification time.Time + LastReminded time.Time Donated time.Time SelectGateway bool Obfs4 bool @@ -77,16 +77,16 @@ func (c *Config) SetUserStoppedVPN(vpnStopped bool) error { return c.save() } -func (c *Config) HasDonated() bool { - return c.file.Donated.Add(oneMonth).After(time.Now()) +func (c *Config) NeedsDonationReminder() bool { + return !c.hasDonated() && c.file.LastReminded.Add(oneDay).Before(time.Now()) } -func (c *Config) NeedsNotification() bool { - return !c.HasDonated() && c.file.LastNotification.Add(oneDay).Before(time.Now()) +func (c *Config) hasDonated() bool { + return c.file.Donated.Add(oneMonth).After(time.Now()) } -func (c *Config) SetNotification() error { - c.file.LastNotification = time.Now() +func (c *Config) SetLastReminded() error { + c.file.LastReminded = time.Now() return c.save() } |