Skip to content

Commit 1e20bc1

Browse files
authored
feat: add preference to disable table editor shortcuts and migrate preferences to localStorage (#1901)
2 parents 4769c1e + 81611f2 commit 1e20bc1

File tree

3 files changed

+170
-70
lines changed

3 files changed

+170
-70
lines changed

public/js/lib/editor/index.js

Lines changed: 149 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-env browser */
12
/* global CodeMirror, $, editor, Cookies */
23
import { options, Alignment, FormatType } from '@susisu/mte-kernel'
34
import debounce from 'lodash/debounce'
@@ -11,6 +12,39 @@ import CodeMirrorSpellChecker, { supportLanguages, supportLanguageCodes } from '
1112
import { initTableEditor } from './table-editor'
1213
import { availableThemes } from './constants'
1314

15+
// Storage utility class for localStorage operations
16+
class Storage {
17+
static get (key, defaultValue = null) {
18+
try {
19+
const value = localStorage.getItem(key)
20+
return value !== null ? value : defaultValue
21+
} catch (e) {
22+
console.error('Error getting from localStorage:', e)
23+
return defaultValue
24+
}
25+
}
26+
27+
static set (key, value, options = {}) {
28+
try {
29+
localStorage.setItem(key, value)
30+
return true
31+
} catch (e) {
32+
console.error('Error setting to localStorage:', e)
33+
return false
34+
}
35+
}
36+
37+
static remove (key) {
38+
try {
39+
localStorage.removeItem(key)
40+
return true
41+
} catch (e) {
42+
console.error('Error removing from localStorage:', e)
43+
return false
44+
}
45+
}
46+
}
47+
1448
/* config section */
1549
const isMac = CodeMirror.keyMap.default === CodeMirror.keyMap.macDefault
1650
const defaultEditorMode = 'gfm'
@@ -183,6 +217,39 @@ export default class Editor {
183217
CodeMirror.defineMode('markmap', function (config, modeConfig) {
184218
return CodeMirror.overlayMode(CodeMirror.getMode(config, 'gfm'), ignoreOverlay)
185219
})
220+
221+
// Migrate preferences from cookies to localStorage
222+
this.migratePreferences()
223+
}
224+
225+
// Migrate preferences from cookies to localStorage
226+
migratePreferences () {
227+
// Only run migration if window and localStorage are available
228+
if (typeof window === 'undefined' || typeof localStorage === 'undefined' || typeof Cookies === 'undefined') {
229+
return
230+
}
231+
232+
const preferencesToMigrate = [
233+
'indent_type',
234+
'tab_size',
235+
'space_units',
236+
'keymap',
237+
'theme',
238+
'spellcheck',
239+
'linter',
240+
'preferences-override-browser-keymap',
241+
'preferences-disable-table-shortcuts'
242+
]
243+
244+
preferencesToMigrate.forEach(key => {
245+
// Check if preference exists in cookies but not in localStorage
246+
const cookieValue = Cookies.get(key)
247+
if (cookieValue !== undefined && Storage.get(key) === null) {
248+
// Migrate the preference to localStorage
249+
Storage.set(key, cookieValue)
250+
console.log(`Migrated preference ${key} from cookies to localStorage`)
251+
}
252+
})
186253
}
187254

188255
on (event, cb) {
@@ -423,24 +490,24 @@ export default class Editor {
423490
}
424491

425492
setIndent () {
426-
var cookieIndentType = Cookies.get('indent_type')
427-
var cookieTabSize = parseInt(Cookies.get('tab_size'))
428-
var cookieSpaceUnits = parseInt(Cookies.get('space_units'))
429-
if (cookieIndentType) {
430-
if (cookieIndentType === 'tab') {
493+
var storedIndentType = Storage.get('indent_type')
494+
var storedTabSize = parseInt(Storage.get('tab_size'))
495+
var storedSpaceUnits = parseInt(Storage.get('space_units'))
496+
if (storedIndentType) {
497+
if (storedIndentType === 'tab') {
431498
this.editor.setOption('indentWithTabs', true)
432-
if (cookieTabSize) {
433-
this.editor.setOption('indentUnit', cookieTabSize)
499+
if (storedTabSize) {
500+
this.editor.setOption('indentUnit', storedTabSize)
434501
}
435-
} else if (cookieIndentType === 'space') {
502+
} else if (storedIndentType === 'space') {
436503
this.editor.setOption('indentWithTabs', false)
437-
if (cookieSpaceUnits) {
438-
this.editor.setOption('indentUnit', cookieSpaceUnits)
504+
if (storedSpaceUnits) {
505+
this.editor.setOption('indentUnit', storedSpaceUnits)
439506
}
440507
}
441508
}
442-
if (cookieTabSize) {
443-
this.editor.setOption('tabSize', cookieTabSize)
509+
if (storedTabSize) {
510+
this.editor.setOption('tabSize', storedTabSize)
444511
}
445512

446513
var type = this.statusIndicators.find('.indent-type')
@@ -449,14 +516,10 @@ export default class Editor {
449516

450517
const setType = () => {
451518
if (this.editor.getOption('indentWithTabs')) {
452-
Cookies.set('indent_type', 'tab', {
453-
expires: 365
454-
})
519+
Storage.set('indent_type', 'tab')
455520
type.text('Tab Size:')
456521
} else {
457-
Cookies.set('indent_type', 'space', {
458-
expires: 365
459-
})
522+
Storage.set('indent_type', 'space')
460523
type.text('Spaces:')
461524
}
462525
}
@@ -465,13 +528,9 @@ export default class Editor {
465528
const setUnit = () => {
466529
var unit = this.editor.getOption('indentUnit')
467530
if (this.editor.getOption('indentWithTabs')) {
468-
Cookies.set('tab_size', unit, {
469-
expires: 365
470-
})
531+
Storage.set('tab_size', unit)
471532
} else {
472-
Cookies.set('space_units', unit, {
473-
expires: 365
474-
})
533+
Storage.set('space_units', unit)
475534
}
476535
widthLabel.text(unit)
477536
}
@@ -480,16 +539,16 @@ export default class Editor {
480539
type.click(() => {
481540
if (this.editor.getOption('indentWithTabs')) {
482541
this.editor.setOption('indentWithTabs', false)
483-
cookieSpaceUnits = parseInt(Cookies.get('space_units'))
484-
if (cookieSpaceUnits) {
485-
this.editor.setOption('indentUnit', cookieSpaceUnits)
542+
storedSpaceUnits = parseInt(Storage.get('space_units'))
543+
if (storedSpaceUnits) {
544+
this.editor.setOption('indentUnit', storedSpaceUnits)
486545
}
487546
} else {
488547
this.editor.setOption('indentWithTabs', true)
489-
cookieTabSize = parseInt(Cookies.get('tab_size'))
490-
if (cookieTabSize) {
491-
this.editor.setOption('indentUnit', cookieTabSize)
492-
this.editor.setOption('tabSize', cookieTabSize)
548+
storedTabSize = parseInt(Storage.get('tab_size'))
549+
if (storedTabSize) {
550+
this.editor.setOption('indentUnit', storedTabSize)
551+
this.editor.setOption('tabSize', storedTabSize)
493552
}
494553
}
495554
setType()
@@ -525,9 +584,9 @@ export default class Editor {
525584
}
526585

527586
setKeymap () {
528-
var cookieKeymap = Cookies.get('keymap')
529-
if (cookieKeymap) {
530-
this.editor.setOption('keyMap', cookieKeymap)
587+
var storedKeymap = Storage.get('keymap')
588+
if (storedKeymap) {
589+
this.editor.setOption('keyMap', storedKeymap)
531590
}
532591

533592
var label = this.statusIndicators.find('.ui-keymap-label')
@@ -537,9 +596,7 @@ export default class Editor {
537596

538597
const setKeymapLabel = () => {
539598
var keymap = this.editor.getOption('keyMap')
540-
Cookies.set('keymap', keymap, {
541-
expires: 365
542-
})
599+
Storage.set('keymap', keymap)
543600
label.text(keymap)
544601
this.restoreOverrideEditorKeymap()
545602
this.setOverrideBrowserKeymap()
@@ -572,17 +629,15 @@ export default class Editor {
572629

573630
const setTheme = theme => {
574631
this.editor.setOption('theme', theme)
575-
Cookies.set('theme', theme, {
576-
expires: 365
577-
})
632+
Storage.set('theme', theme)
578633
this.statusIndicators.find('.status-theme li').removeClass('active')
579634
this.statusIndicators.find(`.status-theme li[value="${theme}"]`).addClass('active')
580635
}
581636

582-
const cookieTheme = Cookies.get('theme')
583-
if (cookieTheme && availableThemes.find(theme => cookieTheme === theme.value)) {
584-
setTheme(cookieTheme)
585-
activateThemeListItem(cookieTheme)
637+
const storedTheme = Storage.get('theme')
638+
if (storedTheme && availableThemes.find(theme => storedTheme === theme.value)) {
639+
setTheme(storedTheme)
640+
activateThemeListItem(storedTheme)
586641
} else {
587642
activateThemeListItem(this.editor.getOption('theme'))
588643
}
@@ -613,10 +668,10 @@ export default class Editor {
613668
}
614669

615670
getExistingSpellcheckLang () {
616-
const cookieSpellcheck = Cookies.get('spellcheck')
671+
const storedSpellcheck = Storage.get('spellcheck')
617672

618-
if (cookieSpellcheck) {
619-
return cookieSpellcheck === 'false' ? undefined : cookieSpellcheck
673+
if (storedSpellcheck) {
674+
return storedSpellcheck === 'false' ? undefined : storedSpellcheck
620675
} else {
621676
return undefined
622677
}
@@ -637,18 +692,18 @@ export default class Editor {
637692
return $(`<li value="${lang.value}"><a>${lang.name}</a></li>`)
638693
}))
639694

640-
const cookieSpellcheck = Cookies.get('spellcheck')
641-
if (cookieSpellcheck) {
695+
const storedSpellcheck = Storage.get('spellcheck')
696+
if (storedSpellcheck) {
642697
let mode = null
643698
let lang = 'en_US'
644699

645-
if (cookieSpellcheck === 'false' || !cookieSpellcheck) {
700+
if (storedSpellcheck === 'false' || !storedSpellcheck) {
646701
mode = defaultEditorMode
647702
this.activateSpellcheckListItem(false)
648703
} else {
649704
mode = 'spell-checker'
650-
if (supportLanguageCodes.includes(cookieSpellcheck)) {
651-
lang = cookieSpellcheck
705+
if (supportLanguageCodes.includes(storedSpellcheck)) {
706+
lang = storedSpellcheck
652707
}
653708
this.setSpellcheckLang(lang)
654709
}
@@ -674,17 +729,13 @@ export default class Editor {
674729
if (lang === 'disabled') {
675730
spellcheckToggle.removeClass('active')
676731

677-
Cookies.set('spellcheck', false, {
678-
expires: 365
679-
})
732+
Storage.set('spellcheck', 'false')
680733

681734
self.editor.setOption('mode', defaultEditorMode)
682735
} else {
683736
spellcheckToggle.addClass('active')
684737

685-
Cookies.set('spellcheck', lang, {
686-
expires: 365
687-
})
738+
Storage.set('spellcheck', lang)
688739

689740
self.editor.setOption('mode', 'spell-checker')
690741
}
@@ -703,12 +754,10 @@ export default class Editor {
703754
if (!gutters.includes(lintGutter)) {
704755
this.editor.setOption('gutters', [lintGutter, ...gutters])
705756
}
706-
Cookies.set('linter', true, {
707-
expires: 365
708-
})
757+
Storage.set('linter', 'true')
709758
} else {
710759
this.editor.setOption('gutters', gutters.filter(g => g !== lintGutter))
711-
Cookies.remove('linter')
760+
Storage.remove('linter')
712761
}
713762
this.editor.setOption('lint', enable ? linterOptions : false)
714763
}
@@ -726,7 +775,7 @@ export default class Editor {
726775
updateLinterStatus(!lintEnable)
727776
})
728777

729-
const enable = !!Cookies.get('linter')
778+
const enable = Storage.get('linter') === 'true'
730779
this.toggleLinter.bind(this)(enable)
731780
updateLinterStatus(enable)
732781
}
@@ -752,12 +801,10 @@ export default class Editor {
752801
'.ui-preferences-override-browser-keymap label > input[type="checkbox"]'
753802
)
754803
if (overrideBrowserKeymap.is(':checked')) {
755-
Cookies.set('preferences-override-browser-keymap', true, {
756-
expires: 365
757-
})
804+
Storage.set('preferences-override-browser-keymap', 'true')
758805
this.restoreOverrideEditorKeymap()
759806
} else {
760-
Cookies.remove('preferences-override-browser-keymap')
807+
Storage.remove('preferences-override-browser-keymap')
761808
this.resetEditorKeymapToBrowserKeymap()
762809
}
763810
}
@@ -766,10 +813,10 @@ export default class Editor {
766813
var overrideBrowserKeymap = $(
767814
'.ui-preferences-override-browser-keymap label > input[type="checkbox"]'
768815
)
769-
var cookieOverrideBrowserKeymap = Cookies.get(
816+
var storedOverrideBrowserKeymap = Storage.get(
770817
'preferences-override-browser-keymap'
771818
)
772-
if (cookieOverrideBrowserKeymap && cookieOverrideBrowserKeymap === 'true') {
819+
if (storedOverrideBrowserKeymap && storedOverrideBrowserKeymap === 'true') {
773820
overrideBrowserKeymap.prop('checked', true)
774821
} else {
775822
overrideBrowserKeymap.prop('checked', false)
@@ -779,6 +826,39 @@ export default class Editor {
779826
overrideBrowserKeymap.change(() => {
780827
this.setOverrideBrowserKeymap()
781828
})
829+
830+
// Handle table editor shortcuts preference
831+
var disableTableShortcuts = $(
832+
'.ui-preferences-disable-table-shortcuts label > input[type="checkbox"]'
833+
)
834+
var storedDisableTableShortcuts = Storage.get(
835+
'preferences-disable-table-shortcuts'
836+
)
837+
if (storedDisableTableShortcuts && storedDisableTableShortcuts === 'true') {
838+
disableTableShortcuts.prop('checked', true)
839+
} else {
840+
disableTableShortcuts.prop('checked', false)
841+
}
842+
this.setTableShortcutsPreference()
843+
844+
disableTableShortcuts.change(() => {
845+
this.setTableShortcutsPreference()
846+
})
847+
}
848+
849+
setTableShortcutsPreference () {
850+
var disableTableShortcuts = $(
851+
'.ui-preferences-disable-table-shortcuts label > input[type="checkbox"]'
852+
)
853+
if (disableTableShortcuts.is(':checked')) {
854+
Storage.set('preferences-disable-table-shortcuts', 'true')
855+
} else {
856+
Storage.remove('preferences-disable-table-shortcuts')
857+
}
858+
// Notify table editor about the preference change
859+
if (this.tableEditor) {
860+
this.tableEditor.setShortcutsEnabled(!disableTableShortcuts.is(':checked'))
861+
}
782862
}
783863

784864
init (textit) {

public/js/lib/editor/statusbar.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
</a>
1515
<ul class="dropdown-menu" aria-labelledby="preferencesLabel">
1616
<li class="ui-preferences-override-browser-keymap"><a><label>Allow override browser keymap&nbsp;&nbsp;<input type="checkbox"></label></a></li>
17+
<li class="ui-preferences-disable-table-shortcuts"><a><label>Disable table editor shortcuts&nbsp;&nbsp;<input type="checkbox"></label></a></li>
1718
</ul>
1819
</div>
1920
<div class="status-keymap dropup pull-right">

0 commit comments

Comments
 (0)