public async Task LoadFoldersAsync(Config config, string tilde, CancellationToken cancellationToken)
        {
            var folders = await this.FetchFoldersAsync(config, tilde, cancellationToken);
            this.folders = new ConcurrentDictionary<string, Folder>(folders.Select(x => new KeyValuePair<string, Folder>(x.FolderId, x)));

            this.OnFoldersChanged();
        }
        private async Task<IEnumerable<Folder>> FetchFoldersAsync(Config config, string tilde, CancellationToken cancellationToken)
        {
            // If the folder is invalid for any reason, we'll ignore it.
            // Again, there's the potential for duplicate folder IDs (if the user's been fiddling their config). 
            // In this case, there's nothing really sensible we can do. Just pick one of them :)
            var folderConstructionTasks = config.Folders
                .Where(x => String.IsNullOrWhiteSpace(x.Invalid))
                .DistinctBy(x => x.ID)
                .Select(async folder =>
                {
                    var ignores = await this.FetchFolderIgnoresAsync(folder.ID, cancellationToken);
                    var syncState = await this.FetchFolderSyncStateAsync(folder.ID, cancellationToken);
                    var path = folder.Path;
                    if (path.StartsWith("~"))
                        path = Path.Combine(tilde, path.Substring(1).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar));

                    return new Folder(folder.ID, path, syncState, new FolderIgnores(ignores.IgnorePatterns, ignores.RegexPatterns));
                });

            cancellationToken.ThrowIfCancellationRequested();

            var folders = await Task.WhenAll(folderConstructionTasks);
            return folders;
        }
 public ConfigSavedEventArgs(Config config)
 {
     this.Config = config;
 }
        public async Task ReloadFoldersAsync(Config config, string tilde, CancellationToken cancellationToken)
        {
            var folders = await this.FetchFoldersAsync(config, tilde, cancellationToken);
            var newFolders = new ConcurrentDictionary<string, Folder>();
            var existingFolders = this.folders;

            // Maybe nothing changed?
            if (existingFolders.Values.SequenceEqual(folders))
                return;

            var changeNotifications = new List<Action>();

            // Re-use the existing folder object if possible
            foreach (var folder in folders)
            {
                Folder existingFolder;
                if (existingFolders.TryGetValue(folder.FolderId, out existingFolder))
                {
                    if (existingFolder.SyncState != folder.SyncState)
                    {
                        changeNotifications.Add(() => this.OnSyncStateChanged(folder, existingFolder.SyncState, folder.SyncState));
                        existingFolder.SyncState = folder.SyncState;
                    }
                    newFolders[folder.FolderId] = existingFolder;
                }
                else
                {
                    newFolders[folder.FolderId] = folder;
                }
            }

            this.folders = newFolders;
            foreach (var changeNotification in changeNotifications)
            {
                changeNotification();
            }

            this.OnFoldersChanged();
        }
 private void OnConfigSaved(Config config)
 {
     this.ConfigSaved?.Invoke(this, new ConfigSavedEventArgs(config));
 }