Exemplo n.º 1
0
        // Cleanup orphaned/dangling entries in FutureAccessList
        private void DeleteOrphanedTokensInFutureAccessList(NotepadsSessionDataV1 sessionData)
        {
            HashSet <string> tokens = sessionData.TextEditors
                                      .SelectMany(editor => new[] { editor.EditingFileFutureAccessToken })
                                      .Where(token => token != null)
                                      .ToHashSet(StringComparer.OrdinalIgnoreCase);

            var tokensToBeDeleted = new List <string>();

            foreach (var entry in StorageApplicationPermissions.FutureAccessList.Entries)
            {
                if (!tokens.Contains(entry.Token))
                {
                    tokensToBeDeleted.Add(entry.Token);
                }
            }

            foreach (var tokenToBeDel in tokensToBeDeleted)
            {
                try
                {
                    StorageApplicationPermissions.FutureAccessList.Remove(tokenToBeDel);
                }
                catch (Exception ex)
                {
                    LoggingService.LogError($"[SessionManager] Failed to delete orphaned token in FutureAccessList: {ex.Message}");
                }
            }
        }
Exemplo n.º 2
0
        private async Task DeleteOrphanedBackupFilesAsync(NotepadsSessionDataV1 sessionData)
        {
            HashSet <string> backupPaths = sessionData.TextEditors
                                           .SelectMany(te => new[] { te.LastSaved?.BackupFilePath, te.Pending?.BackupFilePath })
                                           .ToHashSet(StringComparer.OrdinalIgnoreCase);

            foreach (StorageFile backupFile in await SessionUtility.GetAllBackupFilesAsync())
            {
                if (!backupPaths.Contains(backupFile.Path))
                {
                    try
                    {
                        await backupFile.DeleteAsync();
                    }
                    catch (Exception ex)
                    {
                        LoggingService.LogError($"Failed to delete backup file: {ex.Message}");
                    }
                }
            }
        }
Exemplo n.º 3
0
        private async Task DeleteOrphanedBackupFilesAsync(NotepadsSessionDataV1 sessionData)
        {
            HashSet <string> backupPaths = sessionData.TextEditors
                                           .SelectMany(te => new[] { te.LastSaved?.BackupFilePath, te.Pending?.BackupFilePath })
                                           .ToHashSet(StringComparer.OrdinalIgnoreCase);

            foreach (StorageFile backupFile in await SessionUtility.GetAllBackupFilesAsync())
            {
                if (!backupPaths.Contains(backupFile.Path))
                {
                    try
                    {
                        await backupFile.DeleteAsync();
                    }
                    catch
                    {
                        // Best effort
                    }
                }
            }
        }
Exemplo n.º 4
0
        // Cleanup orphaned/dangling backup files
        private async Task DeleteOrphanedBackupFilesAsync(NotepadsSessionDataV1 sessionData)
        {
            HashSet <string> backupPaths = sessionData.TextEditors
                                           .SelectMany(editor => new[] { editor.LastSavedBackupFilePath, editor.PendingBackupFilePath })
                                           .Where(path => path != null)
                                           .ToHashSet(StringComparer.OrdinalIgnoreCase);

            foreach (StorageFile backupFile in await SessionUtility.GetAllBackupFilesAsync(_backupFolderName))
            {
                if (!backupPaths.Contains(backupFile.Path))
                {
                    try
                    {
                        await backupFile.DeleteAsync();
                    }
                    catch (Exception ex)
                    {
                        LoggingService.LogError($"[{nameof(SessionManager)}] Failed to delete orphaned backup file: {ex.Message}");
                    }
                }
            }
        }
Exemplo n.º 5
0
        public async Task SaveSessionAsync(Action actionAfterSaving = null)
        {
            if (!IsBackupEnabled)
            {
                LoggingService.LogInfo("[SessionManager] Session backup is disabled.");
                return;
            }

            // Serialize saves
            await _semaphoreSlim.WaitAsync();

            if (!IsBackupEnabled)
            {
                return;                   // Check again after SemaphoreSlim released
            }
            Stopwatch stopwatch = Stopwatch.StartNew();

            ITextEditor[] textEditors = _notepadsCore.GetAllTextEditors();

            if (textEditors == null || textEditors.Length == 0)
            {
                await ClearSessionDataAsync();

                actionAfterSaving?.Invoke();
                _semaphoreSlim.Release();
                return;
            }

            ITextEditor selectedTextEditor = _notepadsCore.GetSelectedTextEditor();

            NotepadsSessionDataV1 sessionData = new NotepadsSessionDataV1();

            foreach (ITextEditor textEditor in textEditors)
            {
                if (_sessionData.TryGetValue(textEditor.Id, out TextEditorSessionDataV1 textEditorData))
                {
                    // Get latest state meta data
                    textEditorData.StateMetaData = textEditor.GetTextEditorStateMetaData();
                }
                else // Text content has been changed or editor has not backed up yet
                {
                    textEditorData = new TextEditorSessionDataV1
                    {
                        Id = textEditor.Id,
                    };

                    if (textEditor.EditingFile != null)
                    {
                        // Add the opened file to FutureAccessList so we can access it next launch
                        var futureAccessToken = ToToken(textEditor.Id);
                        await FileSystemUtility.TryAddOrReplaceTokenInFutureAccessList(futureAccessToken, textEditor.EditingFile);

                        textEditorData.EditingFileFutureAccessToken = futureAccessToken;
                        textEditorData.EditingFileName = textEditor.EditingFileName;
                        textEditorData.EditingFilePath = textEditor.EditingFilePath;
                    }

                    if (textEditor.IsModified)
                    {
                        if (textEditor.EditingFile != null)
                        {
                            // Persist the last save known to the app, which might not be up-to-date (if the file was modified outside the app)
                            var lastSavedBackupFile = await SessionUtility.CreateNewFileInBackupFolderAsync(ToToken(textEditor.Id) + "-LastSaved",
                                                                                                            CreationCollisionOption.ReplaceExisting);

                            if (!await BackupTextAsync(textEditor.LastSavedSnapshot.Content,
                                                       textEditor.LastSavedSnapshot.Encoding,
                                                       textEditor.LastSavedSnapshot.LineEnding,
                                                       lastSavedBackupFile))
                            {
                                continue;
                            }

                            textEditorData.LastSavedBackupFilePath = lastSavedBackupFile.Path;
                        }

                        if (textEditor.EditingFile == null || !string.Equals(textEditor.LastSavedSnapshot.Content, textEditor.GetText()))
                        {
                            // Persist pending changes relative to the last save
                            var pendingBackupFile = await SessionUtility.CreateNewFileInBackupFolderAsync(ToToken(textEditor.Id) + "-Pending",
                                                                                                          CreationCollisionOption.ReplaceExisting);

                            if (!await BackupTextAsync(textEditor.GetText(),
                                                       textEditor.LastSavedSnapshot.Encoding,
                                                       textEditor.LastSavedSnapshot.LineEnding,
                                                       pendingBackupFile))
                            {
                                continue;
                            }

                            textEditorData.PendingBackupFilePath = pendingBackupFile.Path;
                        }
                    }

                    textEditorData.StateMetaData = textEditor.GetTextEditorStateMetaData();

                    // We will not create new backup files for this text editor unless it has changes
                    _sessionData.TryAdd(textEditor.Id, textEditorData);
                }

                sessionData.TextEditors.Add(textEditorData);

                if (textEditor == selectedTextEditor)
                {
                    sessionData.SelectedTextEditor = textEditor.Id;
                }
            }

            sessionData.TabScrollViewerHorizontalOffset =
                _notepadsCore.GetTabScrollViewerHorizontalOffset();

            bool sessionDataSaved = false;

            try
            {
                string sessionJsonStr = JsonConvert.SerializeObject(sessionData, Formatting.Indented);

                if (_lastSessionJsonStr == null || !string.Equals(_lastSessionJsonStr, sessionJsonStr, StringComparison.OrdinalIgnoreCase))
                {
                    // write
                    await SessionUtility.SaveSerializedSessionMetaDataAsync(sessionJsonStr);

                    _lastSessionJsonStr = sessionJsonStr;
                    sessionDataSaved    = true;
                }
            }
            catch (Exception ex)
            {
                LoggingService.LogError($"[SessionManager] Failed to save session metadata: {ex.Message}");
                actionAfterSaving?.Invoke();
                _semaphoreSlim.Release();
                return; // Failed to save the session - do not proceed to delete backup files
            }

            if (sessionDataSaved)
            {
                await DeleteOrphanedBackupFilesAsync(sessionData);

                DeleteOrphanedTokensInFutureAccessList(sessionData);
            }

            stopwatch.Stop();

            if (sessionDataSaved)
            {
                LoggingService.LogInfo($"[SessionManager] Successfully saved the current session. Total time: {stopwatch.Elapsed.TotalMilliseconds} milliseconds.", consoleOnly: true);
            }

            actionAfterSaving?.Invoke();
            _semaphoreSlim.Release();
        }
Exemplo n.º 6
0
        public async Task SaveSessionAsync(Action actionAfterSaving = null)
        {
            if (!IsBackupEnabled)
            {
                LoggingService.LogInfo($"[{nameof(SessionManager)}] Session backup is disabled.");
                return;
            }

            // Serialize saves
            await _semaphoreSlim.WaitAsync();

            if (!IsBackupEnabled)
            {
                return;                   // Check again after SemaphoreSlim released
            }
            Stopwatch stopwatch = Stopwatch.StartNew();

            ITextEditor[] textEditors = _notepadsCore.GetAllTextEditors();

            if (textEditors == null || textEditors.Length == 0)
            {
                await ClearSessionDataAsync();

                actionAfterSaving?.Invoke();
                _semaphoreSlim.Release();
                return;
            }

            ITextEditor selectedTextEditor = _notepadsCore.GetSelectedTextEditor();

            NotepadsSessionDataV1 sessionData = new NotepadsSessionDataV1();

            foreach (ITextEditor textEditor in textEditors)
            {
                try
                {
                    var textEditorSessionData = await GetTextEditorSessionData(textEditor);

                    if (textEditorSessionData == null)
                    {
                        continue;
                    }

                    sessionData.TextEditors.Add(textEditorSessionData);

                    if (textEditor == selectedTextEditor)
                    {
                        sessionData.SelectedTextEditor = textEditor.Id;
                    }
                }
                catch (Exception ex)
                {
                    LoggingService.LogError($"[{nameof(SessionManager)}] Failed to build TextEditor session data: {ex}");
                    Analytics.TrackEvent("SessionManager_FailedToBuildTextEditorSessionData", new Dictionary <string, string>()
                    {
                        { "Exception", ex.Message }
                    });
                }
            }

            sessionData.TabScrollViewerHorizontalOffset =
                _notepadsCore.GetTabScrollViewerHorizontalOffset();

            bool sessionDataSaved = false;

            try
            {
                string sessionJsonStr = JsonConvert.SerializeObject(sessionData, Formatting.Indented);

                if (_lastSessionJsonStr == null || !string.Equals(_lastSessionJsonStr, sessionJsonStr, StringComparison.OrdinalIgnoreCase))
                {
                    // write
                    await SessionUtility.SaveSerializedSessionMetaDataAsync(sessionJsonStr, _sessionMetaDataFileName);

                    _lastSessionJsonStr = sessionJsonStr;
                    sessionDataSaved    = true;
                }
            }
            catch (Exception ex)
            {
                LoggingService.LogError($"[{nameof(SessionManager)}] Failed to save session metadata: {ex.Message}");
                Analytics.TrackEvent("SessionManager_FailedToSaveSessionMetaData", new Dictionary <string, string>()
                {
                    { "Exception", ex.Message }
                });
                actionAfterSaving?.Invoke();
                _semaphoreSlim.Release();
                return; // Failed to save the session - do not proceed to delete backup files
            }

            if (sessionDataSaved)
            {
                try
                {
                    await DeleteOrphanedBackupFilesAsync(sessionData);

                    DeleteOrphanedTokensInFutureAccessList(sessionData);
                }
                catch (Exception ex)
                {
                    Analytics.TrackEvent("SessionManager_FailedToDeleteOrphanedBackupFiles",
                                         new Dictionary <string, string>()
                    {
                        { "Exception", ex.Message }
                    });
                }
            }

            stopwatch.Stop();

            if (sessionDataSaved)
            {
                LoggingService.LogInfo($"[{nameof(SessionManager)}] Successfully saved the current session. Total time: {stopwatch.Elapsed.TotalMilliseconds} milliseconds.", consoleOnly: true);
            }

            actionAfterSaving?.Invoke();
            _semaphoreSlim.Release();
        }
Exemplo n.º 7
0
        public async Task SaveSessionAsync()
        {
            if (!IsBackupEnabled)
            {
                LoggingService.LogInfo("Session backup is disabled.");
                return;
            }

            // Serialize saves
            await _semaphoreSlim.WaitAsync();

            Stopwatch stopwatch = Stopwatch.StartNew();

            ITextEditor[] textEditors        = _notepadsCore.GetAllTextEditors();
            ITextEditor   selectedTextEditor = _notepadsCore.GetSelectedTextEditor();

            FileSystemUtility.ClearFutureAccessList();

            NotepadsSessionDataV1 sessionData = new NotepadsSessionDataV1();

            foreach (ITextEditor textEditor in textEditors)
            {
                if (textEditor.EditingFile != null)
                {
                    // Add the opened file to FutureAccessList so we can access it next launch
                    await FileSystemUtility.TryAddToFutureAccessList(ToToken(textEditor.Id), textEditor.EditingFile);
                }

                if (!_sessionData.TryGetValue(textEditor.Id, out TextEditorSessionData textEditorData))
                {
                    textEditorData = new TextEditorSessionData {
                        Id = textEditor.Id
                    };

                    if (textEditor.IsModified)
                    {
                        if (textEditor.EditingFile != null)
                        {
                            // Persist the last save known to the app, which might not be up-to-date (if the file was modified outside the app)
                            BackupMetadata lastSaved = await SaveLastSavedChangesAsync(textEditor);

                            if (lastSaved == null)
                            {
                                continue;
                            }

                            textEditorData.LastSaved = lastSaved;
                        }

                        // Persist pending changes relative to the last save
                        BackupMetadata pending = await SavePendingChangesAsync(textEditor);

                        if (pending == null)
                        {
                            continue;
                        }

                        textEditorData.Pending = pending;
                    }

                    // We will not create new backup files for this text editor unless it has changes
                    _sessionData.TryAdd(textEditor.Id, textEditorData);
                }

                sessionData.TextEditors.Add(textEditorData);

                if (textEditor == selectedTextEditor)
                {
                    sessionData.SelectedTextEditor = textEditor.Id;
                }
            }

            bool sessionDataSaved = false;

            try
            {
                string sessionJsonStr = JsonConvert.SerializeObject(sessionData, _encodingConverter);

                if (!(ApplicationSettingsStore.Read(SessionDataKey) is string currentValue) || !string.Equals(currentValue, sessionJsonStr, StringComparison.OrdinalIgnoreCase))
                {
                    ApplicationSettingsStore.Write(SessionDataKey, sessionJsonStr);
                    sessionDataSaved = true;
                }
            }
            catch (Exception ex)
            {
                LoggingService.LogError($"Failed to save session metadata: {ex.Message}");
                return; // Failed to save the session - do not proceed to delete backup files
            }

            if (sessionDataSaved)
            {
                await DeleteOrphanedBackupFilesAsync(sessionData);
            }

            stopwatch.Stop();

            if (sessionDataSaved)
            {
                LoggingService.LogInfo($"Successfully saved the current session. Total time: {stopwatch.Elapsed.TotalMilliseconds} milliseconds.", consoleOnly: true);
            }

            _semaphoreSlim.Release();
        }
Exemplo n.º 8
0
        public async Task SaveSessionAsync()
        {
            if (!IsBackupEnabled)
            {
                return;
            }

            // Serialize saves
            await _semaphoreSlim.WaitAsync();

            NotepadsSessionDataV1 sessionData = new NotepadsSessionDataV1();
            HashSet <string>      backupPaths = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

            TextEditor[] textEditors        = _notepadsCore.GetAllTextEditors();
            TextEditor   selectedTextEditor = _notepadsCore.GetSelectedTextEditor();

            FileSystemUtility.ClearFutureAccessList();

            foreach (TextEditor textEditor in textEditors)
            {
                TextEditorSessionData textEditorData = new TextEditorSessionData {
                    Id = textEditor.Id
                };

                if (textEditor.EditingFile != null)
                {
                    // Add the opened file to FutureAccessList so we can access it next launch
                    FileSystemUtility.TryAddToFutureAccessList(ToToken(textEditor.Id), textEditor.EditingFile);

                    // Persist the last save known to the app, which might not be up-to-date (if the file was modified outside the app)
                    BackupMetadata lastSaved = await SaveLastSavedChangesAsync(textEditor);

                    if (lastSaved == null)
                    {
                        continue;
                    }

                    textEditorData.LastSaved = lastSaved;
                    backupPaths.Add(lastSaved.BackupFilePath);
                }

                if (textEditor.IsModified)
                {
                    // Persist pending changes relative to the last save
                    BackupMetadata pending = await SavePendingChangesAsync(textEditor);

                    if (pending == null)
                    {
                        continue;
                    }

                    textEditorData.Pending = pending;
                    backupPaths.Add(pending.BackupFilePath);
                }

                if (textEditorData.LastSaved != null || textEditorData.Pending != null)
                {
                    sessionData.TextEditors.Add(textEditorData);

                    if (textEditor == selectedTextEditor)
                    {
                        sessionData.SelectedTextEditor = textEditor.Id;
                    }
                }
            }

            try
            {
                string sessionJson = JsonConvert.SerializeObject(sessionData, _encodingConverter);
                ApplicationData.Current.LocalSettings.Values[SessionDataKey] = sessionJson;
                LoggingService.LogInfo("Successfully saved the current session.");
            }
            catch
            {
                return; // Failed to save the session - do not proceed to delete backup files
            }

            await DeleteOrphanedBackupFilesAsync(backupPaths);

            _semaphoreSlim.Release();
        }
Exemplo n.º 9
0
        public async Task SaveSessionAsync()
        {
            if (!IsBackupEnabled)
            {
                LoggingService.LogInfo("Session backup is disabled.");
                return;
            }

            // Serialize saves
            await _semaphoreSlim.WaitAsync();

            StorageFolder backupFolder = await SessionUtility.GetBackupFolderAsync();

            LoggingService.LogInfo("Session backup is starting. Backup folder: " + backupFolder.Path);
            Stopwatch stopwatch = Stopwatch.StartNew();

            TextEditor[] textEditors        = _notepadsCore.GetAllTextEditors();
            TextEditor   selectedTextEditor = _notepadsCore.GetSelectedTextEditor();

            FileSystemUtility.ClearFutureAccessList();

            NotepadsSessionDataV1 sessionData = new NotepadsSessionDataV1();

            foreach (TextEditor textEditor in textEditors)
            {
                if (textEditor.EditingFile != null)
                {
                    // Add the opened file to FutureAccessList so we can access it next launch
                    FileSystemUtility.TryAddToFutureAccessList(ToToken(textEditor.Id), textEditor.EditingFile);
                }

                if (!_sessionData.TryGetValue(textEditor.Id, out TextEditorSessionData textEditorData))
                {
                    textEditorData = new TextEditorSessionData {
                        Id = textEditor.Id
                    };

                    if (textEditor.EditingFile != null)
                    {
                        // Persist the last save known to the app, which might not be up-to-date (if the file was modified outside the app)
                        BackupMetadata lastSaved = await SaveLastSavedChangesAsync(textEditor);

                        if (lastSaved == null)
                        {
                            continue;
                        }

                        textEditorData.LastSaved = lastSaved;
                    }

                    if (textEditor.IsModified)
                    {
                        // Persist pending changes relative to the last save
                        BackupMetadata pending = await SavePendingChangesAsync(textEditor);

                        if (pending == null)
                        {
                            continue;
                        }

                        textEditorData.Pending = pending;
                    }

                    // We will not create new backup files for this text editor unless it has changes
                    _sessionData.TryAdd(textEditor.Id, textEditorData);
                }

                if (textEditorData.LastSaved != null || textEditorData.Pending != null)
                {
                    sessionData.TextEditors.Add(textEditorData);

                    if (textEditor == selectedTextEditor)
                    {
                        sessionData.SelectedTextEditor = textEditor.Id;
                    }
                }
            }

            try
            {
                string sessionJson = JsonConvert.SerializeObject(sessionData, _encodingConverter);
                ApplicationData.Current.LocalSettings.Values[SessionDataKey] = sessionJson;
            }
            catch
            {
                return; // Failed to save the session - do not proceed to delete backup files
            }

            await DeleteOrphanedBackupFilesAsync(sessionData);

            stopwatch.Stop();
            LoggingService.LogInfo("Successfully saved the current session. Total time: " + stopwatch.Elapsed.TotalMilliseconds + " milliseconds.");

            _semaphoreSlim.Release();
        }