X-Git-Url: https://code.grnet.gr/git/pithos-ms-client/blobdiff_plain/aa7ac00e76724d9f37e85051b96137d2110183da..ea0184f3ab941d992d2c45f34c95ab6064c5f961:/trunk/Pithos.Client.WPF/Shell/ShellViewModel.cs diff --git a/trunk/Pithos.Client.WPF/Shell/ShellViewModel.cs b/trunk/Pithos.Client.WPF/Shell/ShellViewModel.cs index 98e4627..f86f46e 100644 --- a/trunk/Pithos.Client.WPF/Shell/ShellViewModel.cs +++ b/trunk/Pithos.Client.WPF/Shell/ShellViewModel.cs @@ -50,6 +50,7 @@ using System.ServiceModel; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls.Primitives; +using AppLimit.NetSparkle; using Caliburn.Micro; using Hardcodet.Wpf.TaskbarNotification; using Pithos.Client.WPF.Configuration; @@ -83,10 +84,11 @@ namespace Pithos.Client.WPF { /// * ShowFilePropertiesEvent: Raised when a shell command requests the display of the file/container properties dialog /// //TODO: CODE SMELL Why does the shell handle the SelectiveSynchChanges? - [Export(typeof(IShell))] + [Export(typeof(IShell)), Export(typeof(ShellViewModel))] public class ShellViewModel : Screen, IStatusNotification, IShell, IHandle, IHandle, IHandle { + //The Status Checker provides the current synch state //TODO: Could we remove the status checker and use events in its place? private readonly IStatusChecker _statusChecker; @@ -95,7 +97,7 @@ namespace Pithos.Client.WPF { public PithosSettings Settings { get; private set; } - private readonly ConcurrentDictionary _monitors = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _monitors = new ConcurrentDictionary(); /// /// Dictionary of account monitors, keyed by account /// @@ -105,7 +107,7 @@ namespace Pithos.Client.WPF { /// // TODO: Does the Shell REALLY need access to the monitors? Could we achieve the same results with a better design? // TODO: The monitors should be internal to Pithos.Core, even though exposing them makes coding of the Object and Container windows easier - public ConcurrentDictionary Monitors + public ConcurrentDictionary Monitors { get { return _monitors; } } @@ -118,19 +120,40 @@ namespace Pithos.Client.WPF { private ServiceHost _statusService; //Logging in the Pithos client is provided by log4net - private static readonly log4net.ILog Log = log4net.LogManager.GetLogger("Pithos"); - - //Lazily initialized File Version info. This is done once and lazily to avoid blocking the UI - private Lazy _fileVersion; - - /// + private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly PollAgent _pollAgent; + private readonly NetworkAgent _networkAgent; + + [Import] + public Selectives Selectives { get; set; } + + private MiniStatusViewModel _miniStatus; + + [Import] + public MiniStatusViewModel MiniStatus + { + get { return _miniStatus; } + set + { + _miniStatus = value; + _miniStatus.Shell = this; + _miniStatus.Deactivated += (sender, arg) => + { + _statusVisible = false; + NotifyOfPropertyChange(()=>MiniStatusCaption); + }; + } + } + + /// /// The Shell depends on MEF to provide implementations for windowManager, events, the status checker service and the settings /// /// /// The PithosSettings class encapsulates the app's settings to abstract their storage mechanism (App settings, a database or registry) /// [ImportingConstructor] - public ShellViewModel(IWindowManager windowManager, IEventAggregator events, IStatusChecker statusChecker, PithosSettings settings) + public ShellViewModel(IWindowManager windowManager, IEventAggregator events, IStatusChecker statusChecker, PithosSettings settings,PollAgent pollAgent,NetworkAgent networkAgent) { try { @@ -143,59 +166,129 @@ namespace Pithos.Client.WPF { _events = events; _events.Subscribe(this); + _pollAgent = pollAgent; + _networkAgent = networkAgent; Settings = settings; Proxy.SetFromSettings(settings); - StatusMessage = "In Synch"; + StatusMessage = Settings.Accounts.Count==0 + ? "No Accounts added\r\nPlease add an account" + : "Starting"; - _fileVersion= new Lazy(() => - { - Assembly assembly = Assembly.GetExecutingAssembly(); - var fileVersion = FileVersionInfo.GetVersionInfo(assembly.Location); - return fileVersion; - }); _accounts.CollectionChanged += (sender, e) => { NotifyOfPropertyChange(() => OpenFolderCaption); NotifyOfPropertyChange(() => HasAccounts); }; + SetVersionMessage(); } catch (Exception exc) { Log.Error("Error while starting the ShellViewModel",exc); throw; } - } + } - protected override void OnActivate() + private void SetVersionMessage() + { + Assembly assembly = Assembly.GetExecutingAssembly(); + var fileVersion = FileVersionInfo.GetVersionInfo(assembly.Location); + VersionMessage = String.Format("Pithos+ {0}", fileVersion.FileVersion); + } + + public void CurrentSyncStatus() + { + if (Accounts.Count == 0) + { + ShowPreferences("AccountTab"); + } + else + { + if (!_statusVisible) + _windowManager.ShowWindow(MiniStatus); + else + { + if (MiniStatus.IsActive) + MiniStatus.TryClose(); + } + _statusVisible = !_statusVisible; + + NotifyOfPropertyChange(() => MiniStatusCaption); + } + } + + protected override void OnActivate() { base.OnActivate(); - + InitializeSparkle(); + + //Must delay opening the upgrade window + //to avoid Windows Messages sent by the TaskbarIcon + TaskEx.Delay(5000).ContinueWith(_=> + Execute.OnUIThread(()=> _sparkle.StartLoop(true,Settings.UpdateForceCheck,Settings.UpdateCheckInterval))); + StartMonitoring(); } - - private async void StartMonitoring() + private void OnCheckFinished(object sender, bool updaterequired) + { + + Log.InfoFormat("Upgrade check finished. Need Upgrade: {0}", updaterequired); + if (_manualUpgradeCheck) + { + _manualUpgradeCheck = false; + if (!updaterequired) + //Sparkle raises events on a background thread + Execute.OnUIThread(()=> + ShowBalloonFor(new Notification{Title="Pithos+ is up to date",Message="You have the latest Pithos+ version. No update is required"})); + } + } + + private void OnUpgradeDetected(object sender, UpdateDetectedEventArgs e) + { + Log.InfoFormat("Update detected {0}",e.LatestVersion); + } + + public void CheckForUpgrade() + { + ShowBalloonFor(new Notification{Title="Checking for upgrades",Message="Contacting the server to retrieve the latest Pithos+ version."}); + _sparkle.StopLoop(); + _sparkle.updateDetected -= OnUpgradeDetected; + _sparkle.checkLoopFinished -= OnCheckFinished; + _sparkle.Dispose(); + + _manualUpgradeCheck = true; + InitializeSparkle(); + _sparkle.StartLoop(true,true,Settings.UpdateCheckInterval); + } + + private void InitializeSparkle() + { + _sparkle = new Sparkle(Settings.UpdateUrl); + _sparkle.updateDetected += OnUpgradeDetected; + _sparkle.checkLoopFinished += OnCheckFinished; + _sparkle.ShowDiagnosticWindow = Settings.UpdateDiagnostics; + } + + private async void StartMonitoring() { try { + if (Settings.IgnoreCertificateErrors) + { + ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true; + } + var accounts = Settings.Accounts.Select(MonitorAccount); await TaskEx.WhenAll(accounts); _statusService = StatusService.Start(); -/* - foreach (var account in Settings.Accounts) - { - await MonitorAccount(account); - } -*/ - } catch (AggregateException exc) { @@ -225,19 +318,29 @@ namespace Pithos.Client.WPF { PithosMonitor monitor; var accountName = account.AccountName; - if (_monitors.TryGetValue(accountName, out monitor)) + MigrateFolders(account); + + Selectives.SetIsSelectiveEnabled(account.AccountKey, account.SelectiveSyncEnabled); + + if (Monitors.TryGetValue(account.AccountKey, out monitor)) { //If the account is active - if (account.IsActive) - //Start the monitor. It's OK to start an already started monitor, - //it will just ignore the call - StartMonitor(monitor).Wait(); - else - { - //If the account is inactive - //Stop and remove the monitor - RemoveMonitor(accountName); - } + if (account.IsActive) + { + //The Api Key may have changed throuth the Preferences dialog + monitor.ApiKey = account.ApiKey; + Debug.Assert(monitor.StatusNotification == this,"An existing monitor should already have a StatusNotification service object"); + monitor.RootPath = account.RootPath; + //Start the monitor. It's OK to start an already started monitor, + //it will just ignore the call + StartMonitor(monitor).Wait(); + } + else + { + //If the account is inactive + //Stop and remove the monitor + RemoveMonitor(account.ServerUrl,accountName); + } return; } @@ -255,7 +358,7 @@ namespace Pithos.Client.WPF { monitor.AuthenticationUrl = account.ServerUrl; - _monitors[accountName] = monitor; + Monitors[account.AccountKey] = monitor; if (account.IsActive) { @@ -268,8 +371,21 @@ namespace Pithos.Client.WPF { }); } + private void MigrateFolders(AccountSettings account) + { + var oldOthersFolder=Path.Combine(account.RootPath, FolderConstants.OldOthersFolder); + var newOthersFolder = Path.Combine(account.RootPath, FolderConstants.OthersFolder); + var oldFolder = new DirectoryInfo(oldOthersFolder); + var newFolder = new DirectoryInfo(newOthersFolder); + + if (oldFolder.Exists && !newFolder.Exists) + { + oldFolder.MoveTo(newOthersFolder); + } + } - protected override void OnViewLoaded(object view) + + protected override void OnViewLoaded(object view) { UpdateStatus(); var window = (Window)view; @@ -288,10 +404,37 @@ namespace Pithos.Client.WPF { { _statusMessage = value; NotifyOfPropertyChange(() => StatusMessage); + NotifyOfPropertyChange(() => TooltipMessage); } } - private readonly ObservableConcurrentCollection _accounts = new ObservableConcurrentCollection(); + public string VersionMessage { get; set; } + + public string TooltipMessage + { + get + { + return String.Format("{0}\r\n{1}",VersionMessage,StatusMessage); + } + } + + public string TooltipMiniStatus + { + get + { + return String.Format("{0}\r\n{1}", "Status Window", "Enable / Disable the status window"); + } + } + + public string ToggleStatusWindowMessage + { + get + { + return String.Format("{0}" + Environment.NewLine + "{1} Toggle Mini Status"); + } + } + + private readonly ObservableConcurrentCollection _accounts = new ObservableConcurrentCollection(); public ObservableConcurrentCollection Accounts { get { return _accounts; } @@ -347,17 +490,29 @@ namespace Pithos.Client.WPF { #region Commands - public void ShowPreferences() + public void CancelCurrentOperation() + { + _networkAgent.CancelCurrentOperation(); + } + + public void ShowPreferences() + { + ShowPreferences(null); + } + + public void ShowPreferences(string currentTab) { - Settings.Reload(); - var preferences = new PreferencesViewModel(_windowManager,_events, this,Settings); - _windowManager.ShowDialog(preferences); + //Settings.Reload(); + + var preferences = new PreferencesViewModel(_windowManager, _events, this, Settings,currentTab); + _windowManager.ShowDialog(preferences); } public void AboutPithos() { - var about = new AboutViewModel(); + var about = IoC.Get(); + about.LatestVersion=_sparkle.LatestVersion; _windowManager.ShowWindow(about); } @@ -383,23 +538,60 @@ namespace Pithos.Client.WPF { } -/* + public void GoToSite() { - var site = Properties.Settings.Default.PithosSite; + var site = Properties.Settings.Default.ProductionServer; Process.Start(site); } -*/ + public void GoToSite(AccountInfo account) { - /*var site = String.Format("{0}/ui/?token={1}&user={2}", - account.SiteUri,account.Token, - account.UserName);*/ - Process.Start(account.SiteUri); + var uri = account.SiteUri.Replace("http://","https://"); + Process.Start(uri); } - public void ShowFileProperties() + private bool _statusVisible; + + public string MiniStatusCaption + { + get + { + return _statusVisible ? "Hide Status Window" : "Show Status Window"; + } + } + + public bool HasConflicts + { + get { return true; } + } + public void ShowConflicts() + { + _windowManager.ShowWindow(IoC.Get()); + } + + /// + /// Open an explorer window to the target path's directory + /// and select the file + /// + /// + public void GoToFile(FileEntry entry) + { + var fullPath = entry.FullPath; + if (!File.Exists(fullPath) && !Directory.Exists(fullPath)) + return; + Process.Start("explorer.exe","/select, " + fullPath); + } + + public void OpenLogPath() + { + var pithosDataPath = PithosSettings.PithosDataPath; + + Process.Start(pithosDataPath); + } + + public void ShowFileProperties() { var account = Settings.Accounts.First(acc => acc.IsActive); var dir = new DirectoryInfo(account.RootPath + @"\pithos"); @@ -465,17 +657,15 @@ namespace Pithos.Client.WPF { public void SynchNow() { - var agent = IoC.Get(); - agent.SynchNow(); + _pollAgent.SynchNow(); } public ObjectInfo RefreshObjectInfo(ObjectInfo currentInfo) { if (currentInfo==null) throw new ArgumentNullException("currentInfo"); - Contract.EndContractBlock(); - - var monitor = Monitors[currentInfo.Account]; + Contract.EndContractBlock(); + var monitor = Monitors[currentInfo.AccountKey]; var newInfo=monitor.CloudClient.GetObjectInfo(currentInfo.Account, currentInfo.Container, currentInfo.Name); return newInfo; } @@ -486,68 +676,127 @@ namespace Pithos.Client.WPF { throw new ArgumentNullException("container"); Contract.EndContractBlock(); - var monitor = Monitors[container.Account]; + var monitor = Monitors[container.AccountKey]; var newInfo = monitor.CloudClient.GetContainerInfo(container.Account, container.Name); return newInfo; } - - public void ToggleSynching() + private bool _isPaused; + public bool IsPaused + { + get { return _isPaused; } + set + { + _isPaused = value; + PauseSyncCaption = IsPaused ? "Resume syncing" : "Pause syncing"; + var iconKey = IsPaused ? "TraySyncPaused" : "TrayInSynch"; + StatusIcon = String.Format(@"../Images/{0}.ico", iconKey); + + NotifyOfPropertyChange(() => IsPaused); + } + } + + public void ToggleSynching() { - bool isPaused=false; - foreach (var pair in Monitors) + IsPaused=!IsPaused; + foreach (var monitor in Monitors.Values) { - var monitor = pair.Value; - monitor.Pause = !monitor.Pause; - isPaused = monitor.Pause; + monitor.Pause = IsPaused ; } + _pollAgent.Pause = IsPaused; + _networkAgent.Pause = IsPaused; - PauseSyncCaption = isPaused ? "Resume syncing" : "Pause syncing"; - var iconKey = isPaused? "TraySyncPaused" : "TrayInSynch"; - StatusIcon = String.Format(@"../Images/{0}.ico", iconKey); - } - - public void ExitPithos() - { - foreach (var pair in Monitors) - { - var monitor = pair.Value; - monitor.Stop(); - } - ((Window)GetView()).Close(); } - #endregion + + public void ExitPithos() + { + try + { + + foreach (var monitor in Monitors.Select(pair => pair.Value)) + { + monitor.Stop(); + } + + var view = GetView() as Window; + if (view != null) + view.Close(); + } + catch (Exception exc) + { + Log.Info("Exception while exiting", exc); + } + finally + { + Application.Current.Shutdown(); + } + } + + #endregion private readonly Dictionary _iconNames = new List { new StatusInfo(PithosStatus.InSynch, "All files up to date", "TrayInSynch"), - new StatusInfo(PithosStatus.Syncing, "Syncing Files", "TraySynching"), + new StatusInfo(PithosStatus.PollSyncing, "Polling Files", "TraySynching"), + new StatusInfo(PithosStatus.LocalSyncing, "Syncing Files", "TraySynching"), new StatusInfo(PithosStatus.SyncPaused, "Sync Paused", "TraySyncPaused") }.ToDictionary(s => s.Status); readonly IWindowManager _windowManager; - - /// + //private int _syncCount=0; + + + private PithosStatus _pithosStatus = PithosStatus.Disconnected; + + public void SetPithosStatus(PithosStatus status) + { + if (_pithosStatus == PithosStatus.LocalSyncing && status == PithosStatus.PollComplete) + return; + if (_pithosStatus == PithosStatus.PollSyncing && status == PithosStatus.LocalComplete) + return; + if (status == PithosStatus.LocalComplete || status == PithosStatus.PollComplete) + _pithosStatus = PithosStatus.InSynch; + else + _pithosStatus = status; + UpdateStatus(); + } + + public void SetPithosStatus(PithosStatus status,string message) + { + StatusMessage = message; + SetPithosStatus(status); + } + + /* public Notifier GetNotifier(Notification startNotification, Notification endNotification) + { + return new Notifier(this, startNotification, endNotification); + }*/ + + public Notifier GetNotifier(string startMessage, string endMessage, params object[] args) + { + return new Notifier(this, + new StatusNotification(String.Format(startMessage,args)), + new StatusNotification(String.Format(endMessage,args))); + } + + + /// /// Updates the visual status indicators of the application depending on status changes, e.g. icon, stat /// public void UpdateStatus() { - var pithosStatus = _statusChecker.GetPithosStatus(); - if (_iconNames.ContainsKey(pithosStatus)) + if (_iconNames.ContainsKey(_pithosStatus)) { - var info = _iconNames[pithosStatus]; + var info = _iconNames[_pithosStatus]; StatusIcon = String.Format(@"../Images/{0}.ico", info.IconName); - - - - StatusMessage = String.Format("Pithos {0}\r\n{1}", _fileVersion.Value.FileVersion,info.StatusText); } - - //_events.Publish(new Notification { Title = "Start", Message = "Start Monitoring", Level = TraceLevel.Info}); + + if (_pithosStatus == PithosStatus.InSynch) + StatusMessage = "All files up to date"; } @@ -580,7 +829,10 @@ namespace Pithos.Client.WPF { var message = String.Format("API Key Expired for {0}. Starting Renewal", monitor.UserName); Log.Error(message, exc); - TryAuthorize(monitor, retries).Wait(); + var account = Settings.Accounts.Find(acc => acc.AccountKey == new Uri(new Uri(monitor.AuthenticationUrl), monitor.UserName)); + account.IsExpired = true; + Notify(new ExpirationNotification(account)); + //TryAuthorize(monitor.UserName, retries).Wait(); break; case HttpStatusCode.ProxyAuthenticationRequired: TryAuthenticateProxy(monitor,retries); @@ -606,7 +858,7 @@ namespace Pithos.Client.WPF { Execute.OnUIThread(() => { var proxyAccount = IoC.Get(); - proxyAccount.Settings = this.Settings; + proxyAccount.Settings = Settings; if (true != _windowManager.ShowDialog(proxyAccount)) return; StartMonitor(monitor, retries); @@ -628,51 +880,7 @@ namespace Pithos.Client.WPF { } - private async Task TryAuthorize(PithosMonitor monitor,int retries) - { - _events.Publish(new Notification { Title = "Authorization failed", Message = "Your API Key has probably expired. You will be directed to a page where you can renew it", Level = TraceLevel.Error }); - - try - { - - var credentials = await PithosAccount.RetrieveCredentials(Settings.PithosLoginUrl); - - var account = Settings.Accounts.First(act => act.AccountName == credentials.UserName); - account.ApiKey = credentials.Password; - monitor.ApiKey = credentials.Password; - Settings.Save(); - await TaskEx.Delay(10000); - StartMonitor(monitor, retries + 1); - NotifyOfPropertyChange(()=>Accounts); - } - catch (AggregateException exc) - { - string message = String.Format("API Key retrieval for {0} failed", monitor.UserName); - Log.Error(message, exc.InnerException); - _events.Publish(new Notification { Title = "Authorization failed", Message = message, Level = TraceLevel.Error }); - } - catch (Exception exc) - { - string message = String.Format("API Key retrieval for {0} failed", monitor.UserName); - Log.Error(message, exc); - _events.Publish(new Notification { Title = "Authorization failed", Message = message, Level = TraceLevel.Error }); - } - - } - - private static bool IsUnauthorized(WebException exc) - { - if (exc==null) - throw new ArgumentNullException("exc"); - Contract.EndContractBlock(); - - var response = exc.Response as HttpWebResponse; - if (response == null) - return false; - return (response.StatusCode == HttpStatusCode.Unauthorized); - } - - private void TryLater(PithosMonitor monitor, Exception exc,int retries) + private void TryLater(PithosMonitor monitor, Exception exc,int retries) { var message = String.Format("An exception occured. Can't start monitoring\nWill retry in 10 seconds"); Task.Factory.StartNewDelayed(10000, () => StartMonitor(monitor,retries+1)); @@ -686,16 +894,19 @@ namespace Pithos.Client.WPF { { StatusMessage = status; - _events.Publish(new Notification { Title = "Pithos", Message = status, Level = level }); + _events.Publish(new Notification { Title = "Pithos+", Message = status, Level = level }); } public void NotifyChangedFile(string filePath) { - var entry = new FileEntry {FullPath=filePath}; + if (RecentFiles.Any(e => e.FullPath == filePath)) + return; + IProducerConsumerCollection files=RecentFiles; FileEntry popped; while (files.Count > 5) files.TryTake(out popped); + var entry = new FileEntry { FullPath = filePath }; files.TryAdd(entry); } @@ -708,7 +919,7 @@ namespace Pithos.Client.WPF { account.SiteUri, Uri.EscapeDataString(account.Token), Uri.EscapeDataString(account.UserName)); - if (Accounts.All(item => item.UserName != account.UserName)) + if (!Accounts.Any(item => item.UserName == account.UserName && item.SiteUri == account.SiteUri)) Accounts.TryAdd(account); } @@ -717,12 +928,14 @@ namespace Pithos.Client.WPF { { if (conflictFiles == null) return; - if (!conflictFiles.Any()) + //Convert to list to avoid multiple iterations + var files = conflictFiles.ToList(); + if (files.Count==0) return; UpdateStatus(); //TODO: Create a more specific message. For now, just show a warning - NotifyForFiles(conflictFiles,message,TraceLevel.Warning); + NotifyForFiles(files,message,TraceLevel.Warning); } @@ -735,7 +948,7 @@ namespace Pithos.Client.WPF { StatusMessage = message; - _events.Publish(new Notification { Title = "Pithos", Message = message, Level = level}); + _events.Publish(new Notification { Title = "Pithos+", Message = message, Level = level}); } public void Notify(Notification notification) @@ -744,18 +957,25 @@ namespace Pithos.Client.WPF { } - public void RemoveMonitor(string accountName) + public void RemoveMonitor(string serverUrl,string accountName) { if (String.IsNullOrWhiteSpace(accountName)) return; - var accountInfo=_accounts.FirstOrDefault(account => account.UserName == accountName); - _accounts.TryRemove(accountInfo); + var accountInfo=_accounts.FirstOrDefault(account => account.UserName == accountName && account.StorageUri.ToString().StartsWith(serverUrl)); + if (accountInfo != null) + { + _accounts.TryRemove(accountInfo); + _pollAgent.RemoveAccount(accountInfo); + } - PithosMonitor monitor; - if (Monitors.TryRemove(accountName, out monitor)) + var accountKey = new Uri(new Uri(serverUrl),accountName); + PithosMonitor monitor; + if (Monitors.TryRemove(accountKey, out monitor)) { monitor.Stop(); + //TODO: Also remove any pending actions for this account + //from the network queue } } @@ -792,21 +1012,30 @@ namespace Pithos.Client.WPF { public void Handle(SelectiveSynchChanges message) { - var accountName = message.Account.AccountName; - PithosMonitor monitor; - if (_monitors.TryGetValue(accountName, out monitor)) - { - monitor.AddSelectivePaths(message.Added); - monitor.RemoveSelectivePaths(message.Removed); + TaskEx.Run(() => + { + PithosMonitor monitor; + if (Monitors.TryGetValue(message.Account.AccountKey, out monitor)) + { + Selectives.SetIsSelectiveEnabled(message.Account.AccountKey, message.Enabled); + monitor.SetSelectivePaths(message.Uris, message.Added, message.Removed); + } + + var account = Accounts.FirstOrDefault(acc => acc.AccountKey == message.Account.AccountKey); + if (account != null) + { + this._pollAgent.SetSelectivePaths(account, message.Added, message.Removed); + } + }); - } - } - private bool _pollStarted = false; + private bool _pollStarted; + private Sparkle _sparkle; + private bool _manualUpgradeCheck; - //SMELL: Doing so much work for notifications in the shell is wrong + //SMELL: Doing so much work for notifications in the shell is wrong //The notifications should be moved to their own view/viewmodel pair //and different templates should be used for different message types //This will also allow the addition of extra functionality, eg. actions @@ -828,40 +1057,85 @@ namespace Pithos.Client.WPF { if (!_pollStarted) return; _pollStarted= false; - notification.Title = "Pithos"; + notification.Title = "Pithos+"; notification.Message = "Start Synchronisation"; } + var deleteNotification = notification as CloudDeleteNotification; + if (deleteNotification != null) + { + StatusMessage = String.Format("Deleted {0}", deleteNotification.Data.Name); + return; + } + + var progress = notification as ProgressNotification; + + + if (progress != null) + { + StatusMessage = String.Format("{0} {1:p2} of {2} - {3}", + progress.Action, + progress.Block/(double)progress.TotalBlocks, + progress.FileSize.ToByteSize(), + progress.FileName); + return; + } + + var info = notification as StatusNotification; + if (info != null) + { + StatusMessage = info.Title; + return; + } if (String.IsNullOrWhiteSpace(notification.Message) && String.IsNullOrWhiteSpace(notification.Title)) return; - BalloonIcon icon; - switch (notification.Level) - { - case TraceLevel.Error: - icon = BalloonIcon.Error; - break; - case TraceLevel.Info: - case TraceLevel.Verbose: - icon = BalloonIcon.Info; - break; - case TraceLevel.Warning: - icon = BalloonIcon.Warning; - break; - default: - icon = BalloonIcon.None; - break; - } - - if (Settings.ShowDesktopNotifications) - { - var tv = (ShellView) GetView(); - var balloon=new PithosBalloon{Title=notification.Title,Message=notification.Message,Icon=icon}; - tv.TaskbarView.ShowCustomBalloon(balloon,PopupAnimation.Fade,4000); -// tv.TaskbarView.ShowBalloonTip(notification.Title, notification.Message, icon); - } + if (notification.Level <= TraceLevel.Warning) + ShowBalloonFor(notification); } - #endregion + + private void ShowBalloonFor(Notification notification) + { + Contract.Requires(notification!=null); + + if (!Settings.ShowDesktopNotifications) + return; + + BalloonIcon icon; + switch (notification.Level) + { + case TraceLevel.Verbose: + return; + case TraceLevel.Info: + icon = BalloonIcon.Info; + break; + case TraceLevel.Error: + icon = BalloonIcon.Error; + break; + case TraceLevel.Warning: + icon = BalloonIcon.Warning; + break; + default: + return; + } + + var tv = (ShellView) GetView(); + System.Action clickAction = null; + if (notification is ExpirationNotification) + { + clickAction = () => ShowPreferences("AccountTab"); + } + var balloon = new PithosBalloon + { + Title = notification.Title, + Message = notification.Message, + Icon = icon, + ClickAction = clickAction + }; + tv.TaskbarView.ShowCustomBalloon(balloon, PopupAnimation.Fade, 4000); + } + + #endregion public void Handle(ShowFilePropertiesEvent message) { @@ -906,5 +1180,11 @@ namespace Pithos.Client.WPF { var status=statusKeeper.GetFileStatus(localFileName); return status; } + + public void RemoveAccountFromDatabase(AccountSettings account) + { + var statusKeeper = IoC.Get(); + statusKeeper.ClearFolderStatus(account.RootPath); + } } }