Skip to content

Commit

Permalink
feat: auto save setting (#645)
Browse files Browse the repository at this point in the history
* feat: auto save setting

* feat: auto-save upon switching tabs

* fix: don't trigger auto-save for handles without parent context

* fix: don't disable bridge. predictions by default (yet)
  • Loading branch information
solvedDev committed Oct 15, 2022
1 parent 58abb3e commit 65f3268
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 35 deletions.
15 changes: 11 additions & 4 deletions src/components/Editors/Text/TextTab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ import { wait } from '/@/utils/wait'

const throttledCacheUpdate = debounce<(tab: TextTab) => Promise<void> | void>(
async (tab) => {
// Updates the isUnsaved status of the tab
tab.updateUnsavedStatus()

if (!tab.editorModel || tab.editorModel.isDisposed()) return

const fileContent = tab.editorModel?.getValue()
Expand Down Expand Up @@ -78,6 +75,13 @@ export class TextTab extends FileTab {
)
}

fileDidChange() {
// Updates the isUnsaved status of the tab
this.updateUnsavedStatus()

super.fileDidChange()
}

async onActivate() {
if (this.isActive) return
this.isActive = true
Expand Down Expand Up @@ -121,6 +125,7 @@ export class TextTab extends FileTab {
this.disposables.push(
this.editorModel?.onDidChangeContent(() => {
throttledCacheUpdate(this)
this.fileDidChange()
})
)
this.disposables.push(
Expand All @@ -131,7 +136,9 @@ export class TextTab extends FileTab {

this.editorInstance?.layout()
}
onDeactivate() {
async onDeactivate() {
await super.onDeactivate()

// MonacoEditor is defined
if (this.tabSystem.hasFired) {
const viewState = this.editorInstance.saveViewState()
Expand Down
4 changes: 3 additions & 1 deletion src/components/Editors/TreeEditor/Tab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,9 @@ export class TreeTab extends FileTab {
async onActivate() {
this.treeEditor.activate()
}
onDeactivate() {
async onDeactivate() {
await super.onDeactivate()

this._treeEditor?.deactivate()
}

Expand Down
1 change: 1 addition & 0 deletions src/components/Editors/TreeEditor/TreeEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export class TreeEditor {
this.valueSuggestions = []

this.parent.updateCache()
this.parent.fileDidChange()
})

App.getApp().then(async (app) => {
Expand Down
58 changes: 54 additions & 4 deletions src/components/TabSystem/FileTab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,27 @@ import {
} from '../FileSystem/Polyfill'
import { download } from '../FileSystem/saveOrDownload'
import { writableToUint8Array } from '/@/utils/file/writableToUint8Array'
import { settingsState } from '../Windows/Settings/SettingsState'
import { debounce } from 'lodash-es'

export type TReadOnlyMode = 'forced' | 'manual' | 'off'

const shouldAutoSave = async () => {
const app = await App.getApp()

// Check that either the auto save setting is enabled or fallback to activating it by default on mobile
return (
settingsState?.editor?.autoSaveChanges ?? app.mobile.isCurrentDevice()
)
}

const throttledFileDidChange = debounce<(tab: FileTab) => Promise<void> | void>(
async (tab) => {
tab.tryAutoSave()
},
3000 // 3s delay for fileDidChange
)

export abstract class FileTab extends Tab {
public isForeignFile = false
public isSaving = false
Expand Down Expand Up @@ -72,6 +90,11 @@ export abstract class FileTab extends Tab {

await super.setup()
}
async onDeactivate() {
await this.tryAutoSave()

super.onDeactivate()
}

get name() {
return this.fileHandle.name
Expand Down Expand Up @@ -105,6 +128,24 @@ export abstract class FileTab extends Tab {

abstract setReadOnly(readonly: TReadOnlyMode): Promise<void> | void

/**
* **Important:** This function needs to be called when appropriate by the tabs implementing this class
*/
fileDidChange() {
throttledFileDidChange(this)
}

// Logic for auto-saving
async tryAutoSave() {
// File handle has no parent context -> Auto-saving would have undesirable consequences such as constant file downloads
if (this.fileHandleWithoutParentContext()) return

// Check whether we should auto save and that the file has been changed
if ((await shouldAutoSave()) && this.isUnsaved) {
await this.save()
}
}

async save() {
if (this.isSaving) return
this.isSaving = true
Expand Down Expand Up @@ -153,12 +194,21 @@ export abstract class FileTab extends Tab {
if (!this.isForeignFile) this.parent.openedFiles.add(this.getPath())
}

protected async writeFile(value: BufferSource | Blob | string) {
// Current file handle is a virtual file without parent
if (
/**
* Check whether the given file handle has no parent context -> Save by "Save As" or file download
* @param fileHandle
* @returns boolean
*/
protected fileHandleWithoutParentContext() {
return (
this.fileHandle instanceof VirtualFileHandle &&
!this.fileHandle.hasParentContext
) {
)
}

protected async writeFile(value: BufferSource | Blob | string) {
// Current file handle is a virtual file without parent
if (this.fileHandleWithoutParentContext()) {
// Download the file if the user is using a file system polyfill
if (isUsingFileSystemPolyfill.value) {
download(
Expand Down
52 changes: 30 additions & 22 deletions src/components/Windows/Settings/setupSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import { FontSelection } from './Controls/FontSelection'
import { LocaleManager } from '../../Locales/Manager'

export async function setupSettings(settings: SettingsWindow) {
const app = await App.getApp()

settings.addControl(
new ButtonToggle({
category: 'appearance',
Expand All @@ -29,7 +31,7 @@ export async function setupSettings(settings: SettingsWindow) {
options: ['auto', 'dark', 'light'],
default: 'auto',
onChange: () => {
App.getApp().then((app) => app.themeManager.updateTheme())
app.themeManager.updateTheme()
},
})
)
Expand All @@ -46,7 +48,7 @@ export async function setupSettings(settings: SettingsWindow) {
},
default: 'bridge.default.dark',
onChange: () => {
App.getApp().then((app) => app.themeManager.updateTheme())
app.themeManager.updateTheme()
},
})
)
Expand All @@ -63,7 +65,7 @@ export async function setupSettings(settings: SettingsWindow) {
},
default: 'bridge.default.light',
onChange: () => {
App.getApp().then((app) => app.themeManager.updateTheme())
app.themeManager.updateTheme()
},
})
)
Expand All @@ -82,7 +84,7 @@ export async function setupSettings(settings: SettingsWindow) {
},
default: 'bridge.noSelection',
onChange: () => {
App.getApp().then((app) => app.themeManager.updateTheme())
app.themeManager.updateTheme()
},
})
)
Expand All @@ -101,7 +103,7 @@ export async function setupSettings(settings: SettingsWindow) {
},
default: 'bridge.noSelection',
onChange: () => {
App.getApp().then((app) => app.themeManager.updateTheme())
app.themeManager.updateTheme()
},
})
)
Expand Down Expand Up @@ -143,7 +145,6 @@ export async function setupSettings(settings: SettingsWindow) {
'monospace',
],
onChange: async (val) => {
const app = await App.getApp()
app.projectManager.updateAllEditorOptions({
fontFamily: val,
})
Expand All @@ -160,7 +161,6 @@ export async function setupSettings(settings: SettingsWindow) {
default: '14px',
options: ['8px', '10px', '12px', '14px', '16px', '18px', '20px'],
onChange: async (val) => {
const app = await App.getApp()
app.projectManager.updateAllEditorOptions({
fontSize: Number(val.replace('px', '')),
})
Expand Down Expand Up @@ -205,7 +205,7 @@ export async function setupSettings(settings: SettingsWindow) {
options: ['tiny', 'small', 'normal', 'large'],
default: 'normal',
onChange: () => {
App.getApp().then((app) => app.windowResize.dispatch())
app.windowResize.dispatch()
},
})
)
Expand Down Expand Up @@ -313,16 +313,7 @@ export async function setupSettings(settings: SettingsWindow) {
default: 'rawText',
})
)
settings.addControl(
new Toggle({
category: 'editor',
name: 'windows.settings.editor.bridgePredictions.name',
description:
'windows.settings.editor.bridgePredictions.description',
key: 'bridgePredictions',
default: true,
})
)

settings.addControl(
new Toggle({
category: 'editor',
Expand All @@ -332,7 +323,6 @@ export async function setupSettings(settings: SettingsWindow) {
key: 'bracketPairColorization',
default: false,
onChange: async (val) => {
const app = await App.getApp()
app.projectManager.updateAllEditorOptions({
// @ts-expect-error The monaco team did not update the types yet
'bracketPairColorization.enabled': val,
Expand All @@ -348,7 +338,6 @@ export async function setupSettings(settings: SettingsWindow) {
key: 'wordWrap',
default: false,
onChange: async (val) => {
const app = await App.getApp()
app.projectManager.updateAllEditorOptions({
wordWrap: val ? 'bounded' : 'off',
})
Expand All @@ -364,7 +353,6 @@ export async function setupSettings(settings: SettingsWindow) {
default: '80',
options: ['40', '60', '80', '100', '120', '140', '160'],
onChange: async (val) => {
const app = await App.getApp()
app.projectManager.updateAllEditorOptions({
wordWrapColumn: Number(val),
})
Expand All @@ -389,6 +377,27 @@ export async function setupSettings(settings: SettingsWindow) {
default: false,
})
)
settings.addControl(
new Toggle({
category: 'editor',
name: 'windows.settings.editor.autoSaveChanges.name',
description: 'windows.settings.editor.autoSaveChanges.description',
key: 'autoSaveChanges',
default: app.mobile.isCurrentDevice(), // Auto save should be on by default on mobile
})
)

// Tree Editor specific settings
settings.addControl(
new Toggle({
category: 'editor',
name: 'windows.settings.editor.bridgePredictions.name',
description:
'windows.settings.editor.bridgePredictions.description',
key: 'bridgePredictions',
default: true,
})
)
settings.addControl(
new Toggle({
category: 'editor',
Expand Down Expand Up @@ -471,7 +480,6 @@ export async function setupSettings(settings: SettingsWindow) {
)

// Actions
const app = await App.getApp()
Object.values(app.actionManager.state).forEach((action) => {
if (action.type === 'action')
settings.addControl(new ActionViewer(action))
Expand Down
13 changes: 9 additions & 4 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -876,10 +876,7 @@
"name": "JSON Editor",
"description": "Choose how you want to edit JSON files"
},
"bridgePredictions": {
"name": "bridge. Predictions",
"description": "Enable bridge. predictions to let the app intelligently decide whether to add a value or object within bridge.'s tree editor. This simplifies editing JSON significantly"
},

"bracketPairColorization": {
"name": "Bracket Pair Colorization",
"description": "Give matching brackets an unique color"
Expand All @@ -900,6 +897,14 @@
"name": "Keep Tabs Open",
"description": "By default, opening a new tab closes the previously opened tab if it was not interacted with"
},
"autoSaveChanges": {
"name": "Auto Save Changes",
"description": "Automatically save changes to files after a short delay"
},
"bridgePredictions": {
"name": "bridge. Predictions",
"description": "Enable bridge. predictions to let the app intelligently decide whether to add a value or object within bridge.'s tree editor. This simplifies editing JSON significantly"
},
"automaticallyOpenTreeNodes": {
"name": "Automatically Open Tree Nodes",
"description": "Inside of bridge.'s tree editor, automatically open nodes when you select them"
Expand Down

0 comments on commit 65f3268

Please sign in to comment.