Example #1
0
        public HelpCommands(CommandIconService commandIconService, AvaloniaGlobalComponent global)
        {
            Global       = global.Global;
            AboutCommand = new CommandDefinition(
                "About",
                commandIconService.GetCompletionKindImage("About"),
                ReactiveCommand.Create(() =>
            {
                IoC.Get <IShell>().AddOrSelectDocument(() => new AboutViewModel(Global));
            }));

            CustomerSupportCommand = new CommandDefinition(
                "Customer Support",
                commandIconService.GetCompletionKindImage("CustomerSupport"),
                ReactiveCommand.Create(() =>
            {
                try
                {
                    IoHelpers.OpenBrowser("https://www.reddit.com/r/WasabiWallet/");
                }
                catch (Exception ex)
                {
                    Logging.Logger.LogWarning <HelpCommands>(ex);
                    IoC.Get <IShell>().AddOrSelectDocument(() => new AboutViewModel(Global));
                }
            }));

            ReportBugCommand = new CommandDefinition(
                "Report Bug",
                commandIconService.GetCompletionKindImage("ReportBug"),
                ReactiveCommand.Create(() =>
            {
                try
                {
                    IoHelpers.OpenBrowser("https://github.com/zkSNACKs/WalletWasabi/issues");
                }
                catch (Exception ex)
                {
                    Logging.Logger.LogWarning <HelpCommands>(ex);
                    IoC.Get <IShell>().AddOrSelectDocument(() => new AboutViewModel(Global));
                }
            }));

            DocsCommand = new CommandDefinition(
                "Documentation",
                commandIconService.GetCompletionKindImage("Documentation"),
                ReactiveCommand.Create(() =>
            {
                try
                {
                    IoHelpers.OpenBrowser("https://docs.wasabiwallet.io/");
                }
                catch (Exception ex)
                {
                    Logging.Logger.LogWarning <HelpCommands>(ex);
                    IoC.Get <IShell>().AddOrSelectDocument(() => new AboutViewModel(Global));
                }
            }));

            PrivacyPolicyCommand = new CommandDefinition(
                "Privacy Policy",
                commandIconService.GetCompletionKindImage("PrivacyPolicy"),
                ReactiveCommand.Create(() =>
            {
                IoC.Get <IShell>().AddOrSelectDocument(() => new PrivacyPolicyViewModel(Global));
            }));

            TermsAndConditionsCommand = new CommandDefinition(
                "Terms and Conditions",
                commandIconService.GetCompletionKindImage("TermsAndConditions"),
                ReactiveCommand.Create(() =>
            {
                IoC.Get <IShell>().AddOrSelectDocument(() => new TermsAndConditionsViewModel(Global));
            }));

            LegalIssuesCommand = new CommandDefinition(
                "Legal Issues",
                commandIconService.GetCompletionKindImage("LegalIssues"),
                ReactiveCommand.Create(() =>
            {
                IoC.Get <IShell>().AddOrSelectDocument(() => new LegalIssuesViewModel(Global));
            }));

#if DEBUG
            DevToolsCommand = new CommandDefinition(
                "Dev Tools",
                commandIconService.GetCompletionKindImage("DevTools"),
                ReactiveCommand.Create(() =>
            {
                var devTools = new DevTools(Application.Current.Windows.FirstOrDefault());

                var devToolsWindow = new Window
                {
                    Width         = 1024,
                    Height        = 512,
                    Content       = devTools,
                    DataTemplates =
                    {
                        new ViewLocator <Avalonia.Diagnostics.ViewModels.ViewModelBase>()
                    }
                };

                devToolsWindow.Show();
            }));
#endif
        }
Example #2
0
        public void Initialize(NodesCollection nodes, WasabiSynchronizer synchronizer, UpdateChecker updateChecker)
        {
            Nodes        = nodes;
            Synchronizer = synchronizer;
            HashChain    = synchronizer.BitcoinStore.HashChain;
            UseTor       = Global.Config.UseTor;       // Do not make it dynamic, because if you change this config settings only next time will it activate.

            _status = ActiveStatuses.WhenAnyValue(x => x.CurrentStatus)
                      .ObserveOn(RxApp.MainThreadScheduler)
                      .ToProperty(this, x => x.Status)
                      .DisposeWith(Disposables);

            Observable.FromEventPattern <NodeEventArgs>(nodes, nameof(nodes.Added))
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(_ =>
            {
                SetPeers(Nodes.Count);
            }).DisposeWith(Disposables);

            Observable.FromEventPattern <NodeEventArgs>(nodes, nameof(nodes.Removed))
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(_ =>
            {
                SetPeers(Nodes.Count);
            }).DisposeWith(Disposables);

            SetPeers(Nodes.Count);

            Observable.FromEventPattern <bool>(typeof(WalletService), nameof(WalletService.DownloadingBlockChanged))
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(x => DownloadingBlock = x.EventArgs)
            .DisposeWith(Disposables);

            Synchronizer.WhenAnyValue(x => x.TorStatus)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(status =>
            {
                SetTor(status);
                SetPeers(Nodes.Count);
            }).DisposeWith(Disposables);

            Synchronizer.WhenAnyValue(x => x.BackendStatus)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(_ =>
            {
                Backend = Synchronizer.BackendStatus;
            }).DisposeWith(Disposables);

            _filtersLeft = HashChain.WhenAnyValue(x => x.HashesLeft)
                           .Throttle(TimeSpan.FromMilliseconds(100))
                           .ObserveOn(RxApp.MainThreadScheduler)
                           .ToProperty(this, x => x.FiltersLeft)
                           .DisposeWith(Disposables);

            Synchronizer.WhenAnyValue(x => x.UsdExchangeRate)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(usd =>
            {
                BtcPrice = $"${(long)usd}";
            }).DisposeWith(Disposables);

            Observable.FromEventPattern <bool>(Synchronizer, nameof(Synchronizer.ResponseArrivedIsGenSocksServFail))
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(e =>
            {
                OnResponseArrivedIsGenSocksServFail(e.EventArgs);
            }).DisposeWith(Disposables);

            this.WhenAnyValue(x => x.FiltersLeft, x => x.DownloadingBlock)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(tup =>
            {
                (int filtersLeft, bool downloadingBlock) = tup.ToValueTuple();
                if (filtersLeft == 0 && !downloadingBlock)
                {
                    TryRemoveStatus(StatusBarStatus.Synchronizing);
                }
                else
                {
                    TryAddStatus(StatusBarStatus.Synchronizing);
                }
            });

            this.WhenAnyValue(x => x.Tor, x => x.Backend, x => x.Peers)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(tup =>
            {
                (TorStatus tor, BackendStatus backend, int peers) = tup.ToValueTuple();
                if (tor == TorStatus.NotRunning || backend != BackendStatus.Connected || peers < 1)
                {
                    TryAddStatus(StatusBarStatus.Connecting);
                }
                else
                {
                    TryRemoveStatus(StatusBarStatus.Connecting);
                }
            });

            this.WhenAnyValue(x => x.UpdateStatus)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(x =>
            {
                if (x == UpdateStatus.Critical)
                {
                    TryAddStatus(StatusBarStatus.CriticalUpdate);
                }
                else
                {
                    TryRemoveStatus(StatusBarStatus.CriticalUpdate);
                }

                if (x == UpdateStatus.Optional)
                {
                    TryAddStatus(StatusBarStatus.OptionalUpdate);
                }
                else
                {
                    TryRemoveStatus(StatusBarStatus.OptionalUpdate);
                }
            });

            UpdateCommand = ReactiveCommand.Create(() =>
            {
                try
                {
                    IoHelpers.OpenBrowser("https://wasabiwallet.io/#download");
                }
                catch (Exception ex)
                {
                    Logging.Logger.LogWarning <StatusBarViewModel>(ex);
                    IoC.Get <IShell>().AddOrSelectDocument(() => new AboutViewModel(Global));
                }
            }, this.WhenAnyValue(x => x.UpdateStatus)
                                                   .ObserveOn(RxApp.MainThreadScheduler)
                                                   .Select(x => x != UpdateStatus.Latest));
            this.RaisePropertyChanged(nameof(UpdateCommand));             // The binding happens after the constructor. So, if the command is not in constructor, then we need this line.

            updateChecker.Start(TimeSpan.FromMinutes(7),
                                () =>
            {
                UpdateStatus = UpdateStatus.Critical;
                return(Task.CompletedTask);
            },
                                () =>
            {
                if (UpdateStatus != UpdateStatus.Critical)
                {
                    UpdateStatus = UpdateStatus.Optional;
                }
                return(Task.CompletedTask);
            });
        }
Example #3
0
        public HelpCommands(CommandIconService commandIconService, AvaloniaGlobalComponent global)
        {
            Global       = global.Global;
            AboutCommand = new CommandDefinition(
                "About",
                commandIconService.GetCompletionKindImage("About"),
                ReactiveCommand.Create(() =>
            {
                IoC.Get <IShell>().AddOrSelectDocument(() => new AboutViewModel(Global));
            }));

            CustomerSupportCommand = new CommandDefinition(
                "Customer Support",
                commandIconService.GetCompletionKindImage("CustomerSupport"),
                ReactiveCommand.Create(() =>
            {
                try
                {
                    IoHelpers.OpenBrowser("https://www.reddit.com/r/WasabiWallet/");
                }
                catch (Exception ex)
                {
                    Logging.Logger.LogWarning <HelpCommands>(ex);
                    IoC.Get <IShell>().AddOrSelectDocument(() => new AboutViewModel(Global));
                }
            }));

            ReportBugCommand = new CommandDefinition(
                "Report Bug",
                commandIconService.GetCompletionKindImage("ReportBug"),
                ReactiveCommand.Create(() =>
            {
                try
                {
                    IoHelpers.OpenBrowser("https://github.com/zkSNACKs/WalletWasabi/issues");
                }
                catch (Exception ex)
                {
                    Logging.Logger.LogWarning <HelpCommands>(ex);
                    IoC.Get <IShell>().AddOrSelectDocument(() => new AboutViewModel(Global));
                }
            }));

            DocsCommand = new CommandDefinition(
                "Documentation",
                commandIconService.GetCompletionKindImage("Documentation"),
                ReactiveCommand.Create(() =>
            {
                try
                {
                    IoHelpers.OpenBrowser("https://docs.wasabiwallet.io/");
                }
                catch (Exception ex)
                {
                    Logging.Logger.LogWarning <HelpCommands>(ex);
                    IoC.Get <IShell>().AddOrSelectDocument(() => new AboutViewModel(Global));
                }
            }));

            PrivacyPolicyCommand = new CommandDefinition(
                "Privacy Policy",
                commandIconService.GetCompletionKindImage("PrivacyPolicy"),
                ReactiveCommand.Create(() =>
            {
                IoC.Get <IShell>().AddOrSelectDocument(() => new PrivacyPolicyViewModel(Global));
            }));

            TermsAndConditionsCommand = new CommandDefinition(
                "Terms and Conditions",
                commandIconService.GetCompletionKindImage("TermsAndConditions"),
                ReactiveCommand.Create(() =>
            {
                IoC.Get <IShell>().AddOrSelectDocument(() => new TermsAndConditionsViewModel(Global));
            }));

            LegalIssuesCommand = new CommandDefinition(
                "Legal Issues",
                commandIconService.GetCompletionKindImage("LegalIssues"),
                ReactiveCommand.Create(() =>
            {
                IoC.Get <IShell>().AddOrSelectDocument(() => new LegalIssuesViewModel(Global));
            }));
        }
        public LoadWalletViewModel(WalletManagerViewModel owner, LoadWalletType loadWalletType)
            : base(loadWalletType == LoadWalletType.Password ? "Test Password" : (loadWalletType == LoadWalletType.Desktop ? "Load Wallet" : "Hardware Wallet"))
        {
            Owner          = owner;
            Password       = "";
            LoadWalletType = loadWalletType;
            Wallets        = new ObservableCollection <LoadWalletEntry>();
            IsHwWalletSearchTextVisible = false;

            this.WhenAnyValue(x => x.SelectedWallet)
            .Subscribe(_ => TrySetWalletStates());

            this.WhenAnyValue(x => x.IsWalletOpened)
            .Subscribe(_ => TrySetWalletStates());

            this.WhenAnyValue(x => x.IsBusy)
            .Subscribe(_ => TrySetWalletStates());

            LoadCommand           = ReactiveCommand.CreateFromTask(async() => await LoadWalletAsync(), this.WhenAnyValue(x => x.CanLoadWallet));
            TestPasswordCommand   = ReactiveCommand.CreateFromTask(async() => await LoadKeyManagerAsync(requirePassword: true, isHardwareWallet: false), this.WhenAnyValue(x => x.CanTestPassword));
            OpenFolderCommand     = ReactiveCommand.Create(OpenWalletsFolder);
            ImportColdcardCommand = ReactiveCommand.CreateFromTask(async() =>
            {
                var ofd = new OpenFileDialog
                {
                    AllowMultiple = false,
                    Title         = "Import Coldcard"
                };

                if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                {
                    ofd.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
                }

                var selected = await ofd.ShowAsync(Application.Current.MainWindow, fallBack: true);
                if (selected != null && selected.Any())
                {
                    var path       = selected.First();
                    var jsonString = await File.ReadAllTextAsync(path);
                    var json       = JObject.Parse(jsonString);
                    var xpubString = json["ExtPubKey"].ToString();
                    var mfpString  = json["MasterFingerprint"].ToString();

                    // https://github.com/zkSNACKs/WalletWasabi/pull/1663#issuecomment-508073066
                    // Coldcard 2.1.0 improperly implemented Wasabi skeleton fingerprint at first, so we must reverse byte order.
                    // The solution was to add a ColdCardFirmwareVersion json field from 2.1.1 and correct the one generated by 2.1.0.
                    var coldCardVersionString = json["ColdCardFirmwareVersion"]?.ToString();
                    var reverseByteOrder      = false;
                    if (coldCardVersionString is null)
                    {
                        reverseByteOrder = true;
                    }
                    else
                    {
                        Version coldCardVersion = new Version(coldCardVersionString);

                        if (coldCardVersion == new Version("2.1.0"))                         // Should never happen though.
                        {
                            reverseByteOrder = true;
                        }
                    }
                    HDFingerprint mfp   = NBitcoinHelpers.BetterParseHDFingerprint(mfpString, reverseByteOrder: reverseByteOrder);
                    ExtPubKey extPubKey = NBitcoinHelpers.BetterParseExtPubKey(xpubString);
                    Logger.LogInfo("Creating a new wallet file.");
                    var walletName     = Global.GetNextHardwareWalletName(customPrefix: "Coldcard");
                    var walletFullPath = Global.GetWalletFullPath(walletName);
                    KeyManager.CreateNewHardwareWalletWatchOnly(mfp, extPubKey, walletFullPath);
                    owner.SelectLoadWallet();
                }
            });

            EnumerateHardwareWalletsCommand = ReactiveCommand.CreateFromTask(async() => await EnumerateHardwareWalletsAsync());

            OpenBrowserCommand = ReactiveCommand.Create <string>(x => IoHelpers.OpenBrowser(x));

            Observable.Merge(OpenBrowserCommand.ThrownExceptions)
            .Merge(LoadCommand.ThrownExceptions)
            .Merge(TestPasswordCommand.ThrownExceptions)
            .Merge(OpenFolderCommand.ThrownExceptions)
            .Merge(ImportColdcardCommand.ThrownExceptions)
            .Merge(EnumerateHardwareWalletsCommand.ThrownExceptions)
            .Subscribe(ex =>
            {
                SetWarningMessage(ex.ToTypeMessageString());
                Logger.LogError(ex);
            });

            SetLoadButtonText();
        }
        public StatusBarViewModel(NodesCollection nodes, WasabiSynchronizer synchronizer, UpdateChecker updateChecker)
        {
            UpdateStatus = UpdateStatus.Latest;
            Nodes        = nodes;
            Synchronizer = synchronizer;
            BlocksLeft   = 0;
            FiltersLeft  = synchronizer.GetFiltersLeft();
            UseTor       = Global.Config.UseTor.Value;       // Don't make it dynamic, because if you change this config settings only next time will it activate.

            Observable.FromEventPattern <NodeEventArgs>(nodes, nameof(nodes.Added))
            .Subscribe(x =>
            {
                SetPeers(Nodes.Count);
            }).DisposeWith(Disposables);

            Observable.FromEventPattern <NodeEventArgs>(nodes, nameof(nodes.Removed))
            .Subscribe(x =>
            {
                SetPeers(Nodes.Count);
            }).DisposeWith(Disposables);

            SetPeers(Nodes.Count);

            Observable.FromEventPattern <int>(typeof(WalletService), nameof(WalletService.ConcurrentBlockDownloadNumberChanged))
            .Subscribe(x =>
            {
                BlocksLeft = x.EventArgs;
            }).DisposeWith(Disposables);

            Observable.FromEventPattern(synchronizer, nameof(synchronizer.NewFilter)).Subscribe(x =>
            {
                FiltersLeft = Synchronizer.GetFiltersLeft();
            }).DisposeWith(Disposables);

            synchronizer.WhenAnyValue(x => x.TorStatus).Subscribe(status =>
            {
                SetTor(status);
                SetPeers(Nodes.Count);
            }).DisposeWith(Disposables);

            synchronizer.WhenAnyValue(x => x.BackendStatus).Subscribe(_ =>
            {
                Backend = Synchronizer.BackendStatus;
            }).DisposeWith(Disposables);

            synchronizer.WhenAnyValue(x => x.BestBlockchainHeight).Subscribe(_ =>
            {
                FiltersLeft = Synchronizer.GetFiltersLeft();
            }).DisposeWith(Disposables);

            synchronizer.WhenAnyValue(x => x.UsdExchangeRate).Subscribe(usd =>
            {
                BtcPrice = $"${(long)usd}";
            }).DisposeWith(Disposables);

            Observable.FromEventPattern <bool>(synchronizer, nameof(synchronizer.ResponseArrivedIsGenSocksServFail))
            .Subscribe(e =>
            {
                OnResponseArrivedIsGenSocksServFail(e.EventArgs);
            }).DisposeWith(Disposables);

            this.WhenAnyValue(x => x.BlocksLeft).Subscribe(blocks =>
            {
                RefreshStatus();
            });

            this.WhenAnyValue(x => x.FiltersLeft).Subscribe(filters =>
            {
                RefreshStatus();
            });

            this.WhenAnyValue(x => x.Tor).Subscribe(tor =>
            {
                RefreshStatus();
            });

            this.WhenAnyValue(x => x.Backend).Subscribe(backend =>
            {
                RefreshStatus();
            });

            this.WhenAnyValue(x => x.Peers).Subscribe(peers =>
            {
                RefreshStatus();
            });

            this.WhenAnyValue(x => x.UpdateStatus).Subscribe(_ =>
            {
                RefreshStatus();
            });

            this.WhenAnyValue(x => x.Status).Subscribe(async status =>
            {
                if (status.EndsWith("."))                 // Then do animation.
                {
                    string nextAnimation = null;
                    if (status.EndsWith("..."))
                    {
                        nextAnimation = status.TrimEnd("..", StringComparison.Ordinal);
                    }
                    else if (status.EndsWith("..") || status.EndsWith("."))
                    {
                        nextAnimation = $"{status}.";
                    }

                    if (nextAnimation != null)
                    {
                        await Task.Delay(1000);
                        if (Status == status)                         // If still the same.
                        {
                            Status = nextAnimation;
                        }
                    }
                }
            });

            UpdateCommand = ReactiveCommand.Create(() =>
            {
                try
                {
                    IoHelpers.OpenBrowser("https://wasabiwallet.io/#download");
                }
                catch (Exception ex)
                {
                    Logging.Logger.LogWarning <StatusBarViewModel>(ex);
                    IoC.Get <IShell>().AddOrSelectDocument(() => new AboutViewModel());
                }
            }, this.WhenAnyValue(x => x.UpdateStatus).Select(x => x != UpdateStatus.Latest));

            updateChecker.Start(TimeSpan.FromMinutes(7),
                                () =>
            {
                UpdateStatus = UpdateStatus.Critical;
                return(Task.CompletedTask);
            },
                                () =>
            {
                if (UpdateStatus != UpdateStatus.Critical)
                {
                    UpdateStatus = UpdateStatus.Optional;
                }
                return(Task.CompletedTask);
            });
        }
        public void Initialize(NodesCollection nodes, WasabiSynchronizer synchronizer, UpdateChecker updateChecker)
        {
            Nodes        = nodes;
            Synchronizer = synchronizer;
            HashChain    = synchronizer.BitcoinStore.HashChain;
            UseTor       = Global.Config.UseTor.Value;       // Don't make it dynamic, because if you change this config settings only next time will it activate.

            Observable.FromEventPattern <NodeEventArgs>(nodes, nameof(nodes.Added))
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(x =>
            {
                SetPeers(Nodes.Count);
            }).DisposeWith(Disposables);

            Observable.FromEventPattern <NodeEventArgs>(nodes, nameof(nodes.Removed))
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(x =>
            {
                SetPeers(Nodes.Count);
            }).DisposeWith(Disposables);

            SetPeers(Nodes.Count);

            Observable.FromEventPattern <int>(typeof(WalletService), nameof(WalletService.ConcurrentBlockDownloadNumberChanged))
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(x =>
            {
                BlocksLeft = x.EventArgs;
            }).DisposeWith(Disposables);

            Synchronizer.WhenAnyValue(x => x.TorStatus)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(status =>
            {
                SetTor(status);
                SetPeers(Nodes.Count);
            }).DisposeWith(Disposables);

            Synchronizer.WhenAnyValue(x => x.BackendStatus)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(_ =>
            {
                Backend = Synchronizer.BackendStatus;
            }).DisposeWith(Disposables);

            HashChain.WhenAnyValue(x => x.HashesLeft)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(x =>
            {
                FiltersLeft = x;
            }).DisposeWith(Disposables);

            Synchronizer.WhenAnyValue(x => x.UsdExchangeRate)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(usd =>
            {
                BtcPrice = $"${(long)usd}";
            }).DisposeWith(Disposables);

            Observable.FromEventPattern <bool>(Synchronizer, nameof(Synchronizer.ResponseArrivedIsGenSocksServFail))
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(e =>
            {
                OnResponseArrivedIsGenSocksServFail(e.EventArgs);
            }).DisposeWith(Disposables);

            this.WhenAnyValue(x => x.BlocksLeft)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(blocks =>
            {
                RefreshStatus();
            });

            this.WhenAnyValue(x => x.FiltersLeft)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(filters =>
            {
                RefreshStatus();
            });

            this.WhenAnyValue(x => x.Tor)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(tor =>
            {
                RefreshStatus();
            });

            this.WhenAnyValue(x => x.Backend)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(backend =>
            {
                RefreshStatus();
            });

            this.WhenAnyValue(x => x.Peers)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(peers =>
            {
                RefreshStatus();
            });

            this.WhenAnyValue(x => x.UpdateStatus)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(_ =>
            {
                RefreshStatus();
            });

            UpdateCommand = ReactiveCommand.Create(() =>
            {
                try
                {
                    IoHelpers.OpenBrowser("https://wasabiwallet.io/#download");
                }
                catch (Exception ex)
                {
                    Logging.Logger.LogWarning <StatusBarViewModel>(ex);
                    IoC.Get <IShell>().AddOrSelectDocument(() => new AboutViewModel());
                }
            }, this.WhenAnyValue(x => x.UpdateStatus)
                                                   .ObserveOn(RxApp.MainThreadScheduler)
                                                   .Select(x => x != UpdateStatus.Latest));

            updateChecker.Start(TimeSpan.FromMinutes(7),
                                () =>
            {
                UpdateStatus = UpdateStatus.Critical;
                return(Task.CompletedTask);
            },
                                () =>
            {
                if (UpdateStatus != UpdateStatus.Critical)
                {
                    UpdateStatus = UpdateStatus.Optional;
                }
                return(Task.CompletedTask);
            });
        }
Example #7
0
        public HelpCommands(CommandIconService commandIconService, AvaloniaGlobalComponent global)
        {
            Global       = global.Global;
            AboutCommand = new CommandDefinition(
                "About",
                commandIconService.GetCompletionKindImage("About"),
                ReactiveCommand.Create(() => IoC.Get <IShell>().AddOrSelectDocument(() => new AboutViewModel(Global))));

            CustomerSupportCommand = new CommandDefinition(
                "Customer Support",
                commandIconService.GetCompletionKindImage("CustomerSupport"),
                ReactiveCommand.Create(() =>
            {
                try
                {
                    IoHelpers.OpenBrowser("https://www.reddit.com/r/WasabiWallet/");
                }
                catch (Exception ex)
                {
                    Logger.LogWarning(ex);
                    IoC.Get <IShell>().AddOrSelectDocument(() => new AboutViewModel(Global));
                }
            }));

            ReportBugCommand = new CommandDefinition(
                "Report Bug",
                commandIconService.GetCompletionKindImage("ReportBug"),
                ReactiveCommand.Create(() =>
            {
                try
                {
                    IoHelpers.OpenBrowser("https://github.com/zkSNACKs/WalletWasabi/issues");
                }
                catch (Exception ex)
                {
                    Logger.LogWarning(ex);
                    IoC.Get <IShell>().AddOrSelectDocument(() => new AboutViewModel(Global));
                }
            }));

            DocsCommand = new CommandDefinition(
                "Documentation",
                commandIconService.GetCompletionKindImage("Documentation"),
                ReactiveCommand.Create(() =>
            {
                try
                {
                    IoHelpers.OpenBrowser("https://docs.wasabiwallet.io/");
                }
                catch (Exception ex)
                {
                    Logger.LogWarning(ex);
                    IoC.Get <IShell>().AddOrSelectDocument(() => new AboutViewModel(Global));
                }
            }));

            LegalDocumentsCommand = new CommandDefinition(
                "Legal Documents",
                commandIconService.GetCompletionKindImage("LegalDocuments"),
                ReactiveCommand.Create(() => IoC.Get <IShell>().AddOrSelectDocument(() => new LegalDocumentsViewModel(Global))));

            Observable
            .Merge(AboutCommand.GetReactiveCommand().ThrownExceptions)
            .Merge(CustomerSupportCommand.GetReactiveCommand().ThrownExceptions)
            .Merge(ReportBugCommand.GetReactiveCommand().ThrownExceptions)
            .Merge(DocsCommand.GetReactiveCommand().ThrownExceptions)
            .Merge(LegalDocumentsCommand.GetReactiveCommand().ThrownExceptions)
            .ObserveOn(RxApp.TaskpoolScheduler)
            .Subscribe(ex => Logger.LogError(ex));
        }
Example #8
0
        public void Initialize(NodesCollection nodes, WasabiSynchronizer synchronizer, UpdateChecker updateChecker)
        {
            Nodes          = nodes;
            Synchronizer   = synchronizer;
            HashChain      = synchronizer.BitcoinStore.HashChain;
            UseTor         = Global.Config.UseTor;     // Do not make it dynamic, because if you change this config settings only next time will it activate.
            UseBitcoinCore = Global.Config.StartLocalBitcoinCoreOnStartup;

            _status = ActiveStatuses.WhenAnyValue(x => x.CurrentStatus)
                      .ObserveOn(RxApp.MainThreadScheduler)
                      .ToProperty(this, x => x.Status)
                      .DisposeWith(Disposables);

            Observable
            .Merge(Observable.FromEventPattern <NodeEventArgs>(nodes, nameof(nodes.Added)).Select(x => true)
                   .Merge(Observable.FromEventPattern <NodeEventArgs>(nodes, nameof(nodes.Removed)).Select(x => true)
                          .Merge(Synchronizer.WhenAnyValue(x => x.TorStatus).Select(x => true))))
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(_ => Peers = Synchronizer.TorStatus == TorStatus.NotRunning ? 0 : Nodes.Count)                     // Set peers to 0 if Tor is not running, because we get Tor status from backend answer so it seems to the user that peers are connected over clearnet, while they are not.
            .DisposeWith(Disposables);

            Peers = Tor == TorStatus.NotRunning ? 0 : Nodes.Count;

            Observable.FromEventPattern <bool>(typeof(WalletService), nameof(WalletService.DownloadingBlockChanged))
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(x => DownloadingBlock = x.EventArgs)
            .DisposeWith(Disposables);

            Synchronizer.WhenAnyValue(x => x.TorStatus)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(status => Tor = UseTor ? status : TorStatus.TurnedOff)
            .DisposeWith(Disposables);

            Synchronizer.WhenAnyValue(x => x.BackendStatus)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(_ => Backend = Synchronizer.BackendStatus)
            .DisposeWith(Disposables);

            _filtersLeft = HashChain.WhenAnyValue(x => x.HashesLeft)
                           .Throttle(TimeSpan.FromMilliseconds(100))
                           .ObserveOn(RxApp.MainThreadScheduler)
                           .ToProperty(this, x => x.FiltersLeft)
                           .DisposeWith(Disposables);

            Synchronizer.WhenAnyValue(x => x.UsdExchangeRate)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(usd => BtcPrice = $"${(long)usd}")
            .DisposeWith(Disposables);

            _bitcoinCoreStatus = Global.RpcMonitor
                                 .WhenAnyValue(x => x.Status)
                                 .Throttle(TimeSpan.FromMilliseconds(100))
                                 .ObserveOn(RxApp.MainThreadScheduler)
                                 .ToProperty(this, x => x.BitcoinCoreStatus)
                                 .DisposeWith(Disposables);

            _updateStatus = Global.UpdateChecker
                            .WhenAnyValue(x => x.Status)
                            .Throttle(TimeSpan.FromMilliseconds(100))
                            .ObserveOn(RxApp.MainThreadScheduler)
                            .ToProperty(this, x => x.UpdateStatus)
                            .DisposeWith(Disposables);

            Observable.FromEventPattern <bool>(Synchronizer, nameof(Synchronizer.ResponseArrivedIsGenSocksServFail))
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(e => OnResponseArrivedIsGenSocksServFail(e.EventArgs))
            .DisposeWith(Disposables);

            this.WhenAnyValue(x => x.FiltersLeft, x => x.DownloadingBlock)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(tup =>
            {
                (int filtersLeft, bool downloadingBlock) = tup;
                if (filtersLeft == 0 && !downloadingBlock)
                {
                    TryRemoveStatus(StatusBarStatus.Synchronizing);
                }
                else
                {
                    TryAddStatus(StatusBarStatus.Synchronizing);
                }
            });

            this.WhenAnyValue(x => x.Tor, x => x.Backend, x => x.Peers)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(tup =>
            {
                (TorStatus tor, BackendStatus backend, int peers) = tup;
                if (tor == TorStatus.NotRunning || backend != BackendStatus.Connected || peers < 1)
                {
                    TryAddStatus(StatusBarStatus.Connecting);
                }
                else
                {
                    TryRemoveStatus(StatusBarStatus.Connecting);
                }
            });

            this.WhenAnyValue(x => x.UpdateStatus)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(x =>
            {
                if (x.BackendCompatible)
                {
                    TryRemoveStatus(StatusBarStatus.CriticalUpdate);
                }
                else
                {
                    TryAddStatus(StatusBarStatus.CriticalUpdate);
                }

                if (x.ClientUpToDate)
                {
                    TryRemoveStatus(StatusBarStatus.OptionalUpdate);
                }
                else
                {
                    TryAddStatus(StatusBarStatus.OptionalUpdate);
                }

                UpdateAvailable         = !x.ClientUpToDate;
                CriticalUpdateAvailable = !x.BackendCompatible;
            });

            UpdateCommand = ReactiveCommand.Create(() =>
            {
                try
                {
                    IoHelpers.OpenBrowser("https://wasabiwallet.io/#download");
                }
                catch (Exception ex)
                {
                    Logger.LogWarning(ex);
                    IoC.Get <IShell>().AddOrSelectDocument(() => new AboutViewModel(Global));
                }
            }, this.WhenAnyValue(x => x.UpdateAvailable, x => x.CriticalUpdateAvailable, (x, y) => x || y));

            this.RaisePropertyChanged(nameof(UpdateCommand));             // The binding happens after the constructor. So, if the command is not in constructor, then we need this line.

            updateChecker.Start();
        }