/* * Copyright (c) 2023 Proton AG * * This file is part of ProtonVPN. * * ProtonVPN 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. * * ProtonVPN 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 ProtonVPN. If not, see . */ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using ProtonVPN.Update.Releases; namespace ProtonVPN.Update.Updates { /// /// Represents app update state and an interface to update related operations. /// Operations are performed by . /// public class AppUpdate : IAppUpdate { private readonly InternalState _state; public AppUpdate(AppUpdates appUpdates): this( new InternalState { AppUpdates = appUpdates, Releases = new List(), NewRelease = Release.Empty }) { } private AppUpdate(InternalState state) { _state = state; } public bool Available => !_state.NewRelease.IsEmpty(); public bool Ready => _state.Ready; public string FilePath { get { if (_state.NewRelease.IsNew) { return _state.AppUpdates.FilePath(_state.NewRelease); } return string.Empty; } } public string FileArguments { get { if (_state.NewRelease.IsNew) { return _state.NewRelease.File.Args; } return string.Empty; } } public Version Version => _state.NewRelease.IsNew ? _state.NewRelease.Version : new Version(); public IReadOnlyList ReleaseHistory() { return _state.EarlyAccess ? GetAllReleases() : GetStableReleases(); } public async Task Latest(bool earlyAccess) { IReadOnlyList releases = await _state.AppUpdates.ReleaseHistory(earlyAccess); return WithReleases(releases, earlyAccess); } public IAppUpdate CachedLatest(bool earlyAccess) { return WithReleases(_state.Releases, earlyAccess); } public async Task Downloaded() { if (Available && !Ready) { await _state.AppUpdates.Download(_state.NewRelease); } return this; } public async Task Validated() { bool valid = await _state.AppUpdates.Valid(_state.NewRelease); return WithReady(valid); } public async Task Started(bool auto) { await _state.AppUpdates.StartUpdate(_state.NewRelease); return this; } private AppUpdate WithReleases(IReadOnlyList releases, bool earlyAccess) { Release newRelease = NewRelease(releases, earlyAccess); bool releaseChanged = !Equals(newRelease, _state.NewRelease); InternalState state = _state.Clone(); state.EarlyAccess = earlyAccess; state.Releases = releases; state.NewRelease = newRelease; if (releaseChanged) { state.Ready = false; } return new AppUpdate(state); } private AppUpdate WithReady(bool ready) { InternalState state = _state.Clone(); state.Ready = ready; return new AppUpdate(state); } private IReadOnlyList GetAllReleases() { return _state.Releases.ToList(); } private IReadOnlyList GetStableReleases() { return _state.Releases .SkipWhile(r => r.IsEarlyAccess && r.IsNew) .ToList(); } private static Release NewRelease(IEnumerable releases, bool earlyAccess) { return releases .FirstOrDefault(r => r.IsNew && (!r.IsEarlyAccess || earlyAccess)) ?? Release.Empty; } private static bool Equals(Release one, Release other) { return one.Version.Equals(other.Version) && (one.File == null && other.File == null || one.File != null && other.File != null && one.File.Equals(other.File)); } } }