diff --git a/rollup.config.mjs b/rollup.config.mjs index 4ef09182..2db96b46 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -40,6 +40,7 @@ const importsGeneral = { '@girs/gnome-shell/dist/ui/modalDialog': { name: 'resource://EXT_ROOT/ui/modalDialog.js' }, '@girs/gnome-shell/dist/ui/popupMenu': { name: 'resource://EXT_ROOT/ui/popupMenu.js' }, '@girs/gnome-shell/dist/ui/panelMenu': { name: 'resource://EXT_ROOT/ui/panelMenu.js' }, + '@girs/gnome-shell/dist/ui/animation': { name: 'resource://EXT_ROOT/ui/animation.js' }, //compatibility imports '@girs/gnome-shell-45/dist/ui/messageTray': { name: 'resource://EXT_ROOT/ui/messageTray.js' }, }; diff --git a/src/components/codePanoItem.ts b/src/components/codePanoItem.ts index f24dfff1..ae74e3fe 100644 --- a/src/components/codePanoItem.ts +++ b/src/components/codePanoItem.ts @@ -11,11 +11,9 @@ import { registerGObjectClass } from '@pano/utils/gjs'; export class CodePanoItem extends PanoItem { private codeItemSettings: Gio.Settings; private label: St.Label; - private language: string; - constructor(ext: PanoExtension, clipboardManager: ClipboardManager, dbItem: DBItem, language: string) { + constructor(ext: PanoExtension, clipboardManager: ClipboardManager, dbItem: DBItem, markup: string) { super(ext, clipboardManager, dbItem); - this.language = language; this.codeItemSettings = this.settings.get_child('code-item'); this.label = new St.Label({ @@ -26,30 +24,26 @@ export class CodePanoItem extends PanoItem { this.label.clutterText.ellipsize = Pango.EllipsizeMode.END; this.body.add_child(this.label); this.connect('activated', this.setClipboardContent.bind(this)); - this.setStyle(ext); + this.setStyle(markup); this.codeItemSettings.connect('changed', () => { - this.setStyle.call(this, ext); + //TODO: do this is the scrollview, so that code items can be replaced by text items, if we disable the formatter + + //TODO:debug if this get's fired when changing style of the highlighter, what happens here, if we change the selected highlighter + this.setStyle.call(this, 'TODO'); }); } - private setStyle(ext: PanoExtension) { + private setStyle(markup: string) { const headerBgColor = this.codeItemSettings.get_string('header-bg-color'); const headerColor = this.codeItemSettings.get_string('header-color'); const bodyBgColor = this.codeItemSettings.get_string('body-bg-color'); const bodyFontFamily = this.codeItemSettings.get_string('body-font-family'); const bodyFontSize = this.codeItemSettings.get_int('body-font-size'); - const characterLength = this.codeItemSettings.get_int('char-length'); this.header.set_style(`background-color: ${headerBgColor}; color: ${headerColor};`); this.body.set_style(`background-color: ${bodyBgColor}`); this.label.set_style(`font-size: ${bodyFontSize}px; font-family: ${bodyFontFamily};`); - const markup = ext.markdownDetector?.markupCode(this.language, this.dbItem.content.trim(), characterLength); - - if (!markup) { - throw new Error("Couldn't generate markup"); - } - this.label.clutterText.set_markup(markup); } diff --git a/src/components/indicator/settingsMenu.ts b/src/components/indicator/settingsMenu.ts index b056fff3..352639d9 100644 --- a/src/components/indicator/settingsMenu.ts +++ b/src/components/indicator/settingsMenu.ts @@ -51,12 +51,12 @@ export class SettingsMenu extends PanelMenuButton { this.ext = ext; this.onToggle = onToggle; this.settings = getCurrentExtensionSettings(this.ext); - const isInIncognito = this.settings.get_boolean('is-in-incognito'); + const initialIsInIncognito = this.settings.get_boolean('is-in-incognito'); this.icon = new St.Icon({ gicon: Gio.icon_new_for_string( `${this.ext.path}/icons/hicolor/scalable/actions/${ICON_PACKS[this.settings.get_uint('icon-pack')]}-indicator${ - isInIncognito ? '-incognito-symbolic' : '-symbolic' + initialIsInIncognito ? '-incognito-symbolic' : '-symbolic' }.svg`, ), styleClass: 'system-status-icon indicator-icon', diff --git a/src/components/loadingPanoItem.ts b/src/components/loadingPanoItem.ts new file mode 100644 index 00000000..69391f03 --- /dev/null +++ b/src/components/loadingPanoItem.ts @@ -0,0 +1,58 @@ +import Clutter from '@girs/clutter-14'; +import * as Animation from '@girs/gnome-shell/dist/ui/animation'; +import GObject from '@girs/gobject-2.0'; +import Graphene from '@girs/graphene-1.0'; +import St from '@girs/st-14'; +import type { DBItem } from '@pano/utils/db'; +import { registerGObjectClass, SignalsDefinition } from '@pano/utils/gjs'; + +export type LoadingPanoItemSignalType = 'activated'; + +interface LoadingPanoItemSignals extends SignalsDefinition { + activated: Record; +} + +@registerGObjectClass +export class LoadingPanoItem extends St.BoxLayout { + static metaInfo: GObject.MetaInfo, Record, LoadingPanoItemSignals> = { + GTypeName: 'LoadingPanoItem', + Signals: { + activated: {}, + }, + }; + + readonly dbItem: DBItem; + + constructor(dbItem: DBItem) { + super({ + name: 'loading-pano-item', + visible: true, + pivotPoint: Graphene.Point.alloc().init(0.5, 0.5), + reactive: true, + styleClass: 'loading-pano-item', + vertical: true, + trackHover: true, + }); + + this.dbItem = dbItem; + + //TODO + //@ts-expect-error + const spinner: Clutter.Actor = new Animation.Spinner(32, { animate: true }); + + const spinnerContainer = new St.Bin({ + yExpand: true, + xExpand: true, + child: spinner, + }); + + this.add_child(spinnerContainer); + + //@ts-expect-error + spinner.play(); + + this.connect('activated', () => { + //do nothing + }); + } +} diff --git a/src/components/panoScrollView.ts b/src/components/panoScrollView.ts index 38e832e9..4a5a9df6 100644 --- a/src/components/panoScrollView.ts +++ b/src/components/panoScrollView.ts @@ -6,6 +6,7 @@ import type { ExtensionBase } from '@girs/gnome-shell/dist/extensions/sharedInte import GObject from '@girs/gobject-2.0'; import Shell from '@girs/shell-14'; import St from '@girs/st-14'; +import { LoadingPanoItem } from '@pano/components/loadingPanoItem'; import { PanoItem } from '@pano/components/panoItem'; import { SearchBox } from '@pano/components/searchBox'; import type PanoExtension from '@pano/extension'; @@ -36,6 +37,8 @@ interface PanoScrollViewSignals extends SignalsDefinition, Record, PanoScrollViewSignals> = { @@ -58,7 +61,7 @@ export class PanoScrollView extends St.ScrollView { private list: St.BoxLayout; private settings: Gio.Settings; - private currentFocus: PanoItem | null = null; + private currentFocus: Child | null = null; private currentFilter: string | null = null; private currentItemTypeFilter: ItemType | null = null; private showFavorites: boolean | null = null; @@ -132,7 +135,7 @@ export class PanoScrollView extends St.ScrollView { return Clutter.EVENT_PROPAGATE; } - if (event.get_key_symbol() == Clutter.KEY_BackSpace) { + if (event.get_key_symbol() === Clutter.KEY_BackSpace) { this.emit('scroll-backspace-press'); return Clutter.EVENT_STOP; } @@ -146,21 +149,30 @@ export class PanoScrollView extends St.ScrollView { return Clutter.EVENT_STOP; }); - db.query(new ClipboardQueryBuilder().build()).forEach((dbItem) => { - const panoItem = createPanoItemFromDb(ext, this.clipboardManager, dbItem); - if (panoItem) { - panoItem.connect('motion-event', () => { - if (this.isHovering(this.searchBox)) { - this.searchBox.focus(); - } - }); - this.connectOnRemove(panoItem); - this.connectOnFavorite(panoItem); - this.list.add_child(panoItem); - } - }); + const items = db.query(new ClipboardQueryBuilder().build()); + + for (let index = 0; index < items.length; ++index) { + const dbItem = items[index]!; + const panoItem = new LoadingPanoItem(dbItem); + + this.list.add_child(panoItem); + + //TODO: this doesn't work atm, fix it! + void createPanoItemFromDb(ext, this.clipboardManager, dbItem).then((newItem) => { + if (newItem) { + panoItem.connect('motion-event', () => { + if (this.isHovering(this.searchBox)) { + this.searchBox.focus(); + } + }); + this.connectOnRemove(newItem); + this.connectOnFavorite(newItem); + this.list.set_child_at_index(newItem, index); + } + }); + } - const firstItem = this.list.get_first_child() as PanoItem | null; + const firstItem = this.list.get_first_child() as Child | null; if (firstItem !== null) { firstItem.emit('activated'); } @@ -240,20 +252,20 @@ export class PanoScrollView extends St.ScrollView { }); } - private removeItem(item: PanoItem) { + private removeItem(item: Child) { item.hide(); this.list.remove_child(item); } - private getItem(panoItem: PanoItem): PanoItem | undefined { + private getItem(panoItem: Child): Child | undefined { return this.getItems().find((item) => item.dbItem.id === panoItem.dbItem.id); } - private getItems(): PanoItem[] { - return this.list.get_children() as PanoItem[]; + private getItems(): Child[] { + return this.list.get_children() as Child[]; } - private getVisibleItems(): PanoItem[] { + private getVisibleItems(): Child[] { return this.getItems().filter((item) => item.is_visible()); } @@ -413,7 +425,7 @@ export class PanoScrollView extends St.ScrollView { this.emit('scroll-focus-out'); } - private scrollToItem(item: PanoItem) { + private scrollToItem(item: Child) { const box = item.get_allocation_box(); let adjustment: St.Adjustment | undefined; diff --git a/src/components/searchBox.ts b/src/components/searchBox.ts index e2ba9f45..675d4188 100644 --- a/src/components/searchBox.ts +++ b/src/components/searchBox.ts @@ -93,7 +93,7 @@ export class SearchBox extends St.BoxLayout { this.emitSearchTextChange(); }); - this.search.clutterText.connect('key-press-event', (_: St.Entry, event: Clutter.Event) => { + this.search.clutterText.connect('key-press-event', (_entry: St.Entry, event: Clutter.Event) => { if ( event.get_key_symbol() === Clutter.KEY_Down || (event.get_key_symbol() === Clutter.KEY_Right && @@ -190,7 +190,7 @@ export class SearchBox extends St.BoxLayout { } this.settings.connect('changed::icon-pack', () => { - if (null == this.currentIndex) { + if (this.currentIndex === null) { this.search.set_primary_icon(this.createSearchEntryIcon('edit-find-symbolic', 'search-entry-icon')); } else { this.search.set_primary_icon( diff --git a/src/prefs/customization/codeItemStyle.ts b/src/prefs/customization/codeItemStyle.ts index f0715054..abc0ba69 100644 --- a/src/prefs/customization/codeItemStyle.ts +++ b/src/prefs/customization/codeItemStyle.ts @@ -126,15 +126,15 @@ export class CodeItemStyleRow extends ItemExpanderRow { private onEnabledChanged(_enabled: boolean): void { this.scan(); - //TODOD: recreate those items, which where classified as text previosuly (check if the were created with the correct highliter) + //TODOD: recreate those items, which where classified as text previously (check if the were created with the correct highlighter) } private refreshCallback(): void { this.scan(); } - private onCodeHighlighterChanged(name: string): void { - this.markdownDetector?.detectHighlighter(name); + private async onCodeHighlighterChanged(name: string): Promise { + await this.markdownDetector?.detectHighlighter(name); this.scan(); } @@ -146,18 +146,18 @@ export class CodeItemStyleRow extends ItemExpanderRow { } }; - const value = this.settings.get_uint(PangoMarkdown.codeHighlighterKey); - const stringValue = this.codeHighlighterOptions[value]; + const initialCodeHighlighter = this.settings.get_uint(PangoMarkdown.codeHighlighterKey); + const initialCodeHighlighterValue = this.codeHighlighterOptions[initialCodeHighlighter]; if (!this.markdownDetector) { - this.markdownDetector = new PangoMarkdown(stringValue); + this.markdownDetector = new PangoMarkdown(initialCodeHighlighterValue); } let enablingPossible = true; const currentHighlighter = this.markdownDetector.currentHighlighter; - if (this.markdownDetector.detectedHighlighter.length == 0 || currentHighlighter === null) { + if (this.markdownDetector.detectedHighlighter.length === 0 || currentHighlighter === null) { enablingPossible = false; } @@ -169,7 +169,7 @@ export class CodeItemStyleRow extends ItemExpanderRow { // make all rows in-sensitive, except the enable row, if enabling is possible this.enableProperties[0].sensitive = true; this.enableProperties[1].sensitive = enablingPossible; - this.enableProperties[2].sensitive = enablingPossible && defaultValueForEnabled != isEnabled; + this.enableProperties[2].sensitive = enablingPossible && defaultValueForEnabled !== isEnabled; this.enableProperties[3]?.set_sensitive(true); for (const row of this.rows) { @@ -208,7 +208,7 @@ export class CodeItemStyleRow extends ItemExpanderRow { // make all rows sensitive, so that things can be changed this.enableProperties[0].sensitive = true; this.enableProperties[1].sensitive = true; - this.enableProperties[2].sensitive = defaultValueForEnabled != isEnabled; + this.enableProperties[2].sensitive = defaultValueForEnabled !== isEnabled; this.enableProperties[3]?.set_sensitive(true); for (const row of this.rows) { @@ -233,12 +233,12 @@ export class CodeItemStyleRow extends ItemExpanderRow { return record[key]; }; - const setValueFor = (key: string, value: T | undefined): void => { + const setValueFor = (key: string, val: T | undefined): void => { const record: Record = JSON.parse(currentHighlighter!.options); - if (value === undefined) { + if (val === undefined) { delete record[key]; } else { - record[key] = value; + record[key] = val; } const stringified = JSON.stringify(record); @@ -284,9 +284,9 @@ export class CodeItemStyleRow extends ItemExpanderRow { return index >= 0 ? index : 0; }; - const value = getValueFor(key); + const initialValue = getValueFor(key); - dropDown.set_selected(getIndexFor(value)); + dropDown.set_selected(getIndexFor(initialValue)); dropDown.connect('notify::selected', () => { setValueFor(key, options[dropDown.get_selected()]!); @@ -301,7 +301,7 @@ export class CodeItemStyleRow extends ItemExpanderRow { halign: Gtk4.Align.CENTER, }); - if (defaultValue === value) { + if (defaultValue === initialCodeHighlighterValue) { clearButton.sensitive = false; } diff --git a/src/prefs/customization/itemExpanderRow.ts b/src/prefs/customization/itemExpanderRow.ts index c0fbb77c..34066953 100644 --- a/src/prefs/customization/itemExpanderRow.ts +++ b/src/prefs/customization/itemExpanderRow.ts @@ -18,9 +18,9 @@ export class ItemExpanderRow extends Adw.ExpanderRow { this.extensionSettings = getCurrentExtensionSettings(ext); - const iconPack = this.extensionSettings.get_uint('icon-pack'); + const initialIconPack = this.extensionSettings.get_uint('icon-pack'); - const image = Gtk4.Image.new_from_icon_name(`${ICON_PACKS[iconPack]}-${iconName}`); + const image = Gtk4.Image.new_from_icon_name(`${ICON_PACKS[initialIconPack]}-${iconName}`); this.extensionSettings.connect('changed::icon-pack', () => { const iconPack = this.extensionSettings.get_uint('icon-pack'); diff --git a/src/prefs/customization/utils.ts b/src/prefs/customization/utils.ts index 581e3883..632f2662 100644 --- a/src/prefs/customization/utils.ts +++ b/src/prefs/customization/utils.ts @@ -41,10 +41,10 @@ export const createSwitchRow = ( row.add_suffix(refreshButton); } - const value = settings.get_boolean(schemaKey); + const initialValue = settings.get_boolean(schemaKey); const switch_ = new Gtk4.Switch({ - active: value, + active: initialValue, valign: Gtk4.Align.CENTER, halign: Gtk4.Align.CENTER, }); @@ -62,7 +62,7 @@ export const createSwitchRow = ( const defaultValue = settings.get_default_value(schemaKey)?.get_boolean(); - if (defaultValue === value) { + if (defaultValue === initialValue) { clearButton.sensitive = false; } @@ -125,10 +125,10 @@ export const createColorRow = (title: string, subtitle: string, settings: Gio.Se halign: Gtk4.Align.CENTER, }); - const value = settings.get_string(schemaKey); + const initialValue = settings.get_string(schemaKey); const defaultValue = settings.get_default_value(schemaKey)?.get_string()[0]; - if (defaultValue === value) { + if (defaultValue === initialValue) { clearButton.sensitive = false; } @@ -144,9 +144,9 @@ export const createColorRow = (title: string, subtitle: string, settings: Gio.Se clearButton.sensitive = true; } - const rgba = new Gdk4.RGBA(); - rgba.parse(value); - colorButton.set_rgba(rgba); + const newRgba = new Gdk4.RGBA(); + newRgba.parse(value); + colorButton.set_rgba(newRgba); }); clearButton.connect('clicked', () => { @@ -172,11 +172,11 @@ export const createSpinRow = ( subtitle, }); - const value = settings.get_int(schemaKey); + const initialValue = settings.get_int(schemaKey); const spinButton = new Gtk4.SpinButton({ adjustment: new Gtk4.Adjustment({ stepIncrement: increment, lower, upper }), - value, + value: initialValue, valign: Gtk4.Align.CENTER, halign: Gtk4.Align.CENTER, }); @@ -194,7 +194,7 @@ export const createSpinRow = ( const defaultValue = settings.get_default_value(schemaKey)?.get_int32(); - if (defaultValue === value) { + if (defaultValue === initialValue) { clearButton.sensitive = false; } @@ -253,10 +253,10 @@ export const createFontRow = (title: string, subtitle: string, settings: Gio.Set halign: Gtk4.Align.CENTER, }); - const value = getFont(); + const initialValue = getFont(); const defaultValue = getDefaultFont(); - if (defaultValue === value) { + if (defaultValue === initialValue) { clearButton.sensitive = false; } @@ -296,7 +296,7 @@ export const createDropdownRow = ( subtitle, }); - const value = settings.get_uint(schemaKey); + const initialValue = settings.get_uint(schemaKey); const dropDown = new Gtk4.DropDown({ valign: Gtk4.Align.CENTER, @@ -304,7 +304,7 @@ export const createDropdownRow = ( model: Gtk4.StringList.new(options), }); - dropDown.set_selected(value); + dropDown.set_selected(initialValue); dropDown.connect('notify::selected', () => { settings.set_uint(schemaKey, dropDown.get_selected()); @@ -321,7 +321,7 @@ export const createDropdownRow = ( const defaultValue = settings.get_default_value(schemaKey)?.get_uint32(); - if (defaultValue === value) { + if (defaultValue === initialValue) { clearButton.sensitive = false; } diff --git a/src/prefs/dangerZone/clearHistory.ts b/src/prefs/dangerZone/clearHistory.ts index 84f8f00d..9fdb9927 100644 --- a/src/prefs/dangerZone/clearHistory.ts +++ b/src/prefs/dangerZone/clearHistory.ts @@ -31,7 +31,7 @@ export class ClearHistoryRow extends Adw.ActionRow { buttons: Gtk4.ButtonsType.OK_CANCEL, }); md.get_widget_for_response(Gtk4.ResponseType.OK)?.add_css_class('destructive-action'); - md.connect('response', async (_, response) => { + md.connect('response', async (_source, response) => { if (response === Gtk4.ResponseType.OK) { let isDbusRunning = true; try { diff --git a/src/prefs/general/incognitoShortcutRow.ts b/src/prefs/general/incognitoShortcutRow.ts index 3a0a6e09..6ccaae90 100644 --- a/src/prefs/general/incognitoShortcutRow.ts +++ b/src/prefs/general/incognitoShortcutRow.ts @@ -7,6 +7,47 @@ import { getAcceleratorName } from '@pano/prefs/general/helper'; import { registerGObjectClass } from '@pano/utils/gjs'; import { getCurrentExtensionSettings, gettext } from '@pano/utils/shell'; +export const keyvalIsForbidden = (keyval: number) => { + return [ + Gdk4.KEY_Home, + Gdk4.KEY_Left, + Gdk4.KEY_Up, + Gdk4.KEY_Right, + Gdk4.KEY_Down, + Gdk4.KEY_Page_Up, + Gdk4.KEY_Page_Down, + Gdk4.KEY_End, + Gdk4.KEY_Tab, + Gdk4.KEY_KP_Enter, + Gdk4.KEY_Return, + Gdk4.KEY_Mode_switch, + ].includes(keyval); +}; + +export const isValidAccel = (mask: number, keyval: number) => { + return Gtk4.accelerator_valid(keyval, mask) || (keyval === Gdk4.KEY_Tab && mask !== 0); +}; + +export const isValidBinding = (mask: number, keycode: number, keyval: number) => { + return !( + mask === 0 || + (mask === Gdk4.ModifierType.SHIFT_MASK && + keycode !== 0 && + ((keyval >= Gdk4.KEY_a && keyval <= Gdk4.KEY_z) || + (keyval >= Gdk4.KEY_A && keyval <= Gdk4.KEY_Z) || + (keyval >= Gdk4.KEY_0 && keyval <= Gdk4.KEY_9) || + (keyval >= Gdk4.KEY_kana_fullstop && keyval <= Gdk4.KEY_semivoicedsound) || + (keyval >= Gdk4.KEY_Arabic_comma && keyval <= Gdk4.KEY_Arabic_sukun) || + (keyval >= Gdk4.KEY_Serbian_dje && keyval <= Gdk4.KEY_Cyrillic_HARDSIGN) || + (keyval >= Gdk4.KEY_Greek_ALPHAaccent && keyval <= Gdk4.KEY_Greek_omega) || + (keyval >= Gdk4.KEY_hebrew_doublelowline && keyval <= Gdk4.KEY_hebrew_taf) || + (keyval >= Gdk4.KEY_Thai_kokai && keyval <= Gdk4.KEY_Thai_lekkao) || + (keyval >= Gdk4.KEY_Hangul_Kiyeog && keyval <= Gdk4.KEY_Hangul_J_YeorinHieuh) || + (keyval === Gdk4.KEY_space && mask === 0) || + keyvalIsForbidden(keyval))) + ); +}; + @registerGObjectClass export class IncognitoShortcutRow extends Adw.ActionRow { private settings: Gio.Settings; @@ -48,7 +89,7 @@ export class IncognitoShortcutRow extends Adw.ActionRow { editor.add_controller(ctl); // See https://github.com/tuberry/color-picker/blob/1a278db139f00787e365fce5977d30b535529edb/color-picker%40tuberry/prefs.js - ctl.connect('key-pressed', (_, keyval, keycode, state) => { + ctl.connect('key-pressed', (_source, keyval, keycode, state) => { let mask = state & Gtk4.accelerator_get_default_mod_mask(); mask &= ~Gdk4.ModifierType.LOCK_MASK; if (!mask && keyval === Gdk4.KEY_Escape) { @@ -77,44 +118,3 @@ export class IncognitoShortcutRow extends Adw.ActionRow { this.set_activatable_widget(shortcutLabel); } } - -export const keyvalIsForbidden = (keyval: number) => { - return [ - Gdk4.KEY_Home, - Gdk4.KEY_Left, - Gdk4.KEY_Up, - Gdk4.KEY_Right, - Gdk4.KEY_Down, - Gdk4.KEY_Page_Up, - Gdk4.KEY_Page_Down, - Gdk4.KEY_End, - Gdk4.KEY_Tab, - Gdk4.KEY_KP_Enter, - Gdk4.KEY_Return, - Gdk4.KEY_Mode_switch, - ].includes(keyval); -}; - -export const isValidAccel = (mask: number, keyval: number) => { - return Gtk4.accelerator_valid(keyval, mask) || (keyval === Gdk4.KEY_Tab && mask !== 0); -}; - -export const isValidBinding = (mask: number, keycode: number, keyval: number) => { - return !( - mask === 0 || - (mask === Gdk4.ModifierType.SHIFT_MASK && - keycode !== 0 && - ((keyval >= Gdk4.KEY_a && keyval <= Gdk4.KEY_z) || - (keyval >= Gdk4.KEY_A && keyval <= Gdk4.KEY_Z) || - (keyval >= Gdk4.KEY_0 && keyval <= Gdk4.KEY_9) || - (keyval >= Gdk4.KEY_kana_fullstop && keyval <= Gdk4.KEY_semivoicedsound) || - (keyval >= Gdk4.KEY_Arabic_comma && keyval <= Gdk4.KEY_Arabic_sukun) || - (keyval >= Gdk4.KEY_Serbian_dje && keyval <= Gdk4.KEY_Cyrillic_HARDSIGN) || - (keyval >= Gdk4.KEY_Greek_ALPHAaccent && keyval <= Gdk4.KEY_Greek_omega) || - (keyval >= Gdk4.KEY_hebrew_doublelowline && keyval <= Gdk4.KEY_hebrew_taf) || - (keyval >= Gdk4.KEY_Thai_kokai && keyval <= Gdk4.KEY_Thai_lekkao) || - (keyval >= Gdk4.KEY_Hangul_Kiyeog && keyval <= Gdk4.KEY_Hangul_J_YeorinHieuh) || - (keyval === Gdk4.KEY_space && mask === 0) || - keyvalIsForbidden(keyval))) - ); -}; diff --git a/src/prefs/general/shortcutRow.ts b/src/prefs/general/shortcutRow.ts index 16a77c0c..103ec507 100644 --- a/src/prefs/general/shortcutRow.ts +++ b/src/prefs/general/shortcutRow.ts @@ -7,6 +7,47 @@ import { getAcceleratorName } from '@pano/prefs/general/helper'; import { registerGObjectClass } from '@pano/utils/gjs'; import { getCurrentExtensionSettings, gettext } from '@pano/utils/shell'; +export const keyvalIsForbidden = (keyval: number) => { + return [ + Gdk4.KEY_Home, + Gdk4.KEY_Left, + Gdk4.KEY_Up, + Gdk4.KEY_Right, + Gdk4.KEY_Down, + Gdk4.KEY_Page_Up, + Gdk4.KEY_Page_Down, + Gdk4.KEY_End, + Gdk4.KEY_Tab, + Gdk4.KEY_KP_Enter, + Gdk4.KEY_Return, + Gdk4.KEY_Mode_switch, + ].includes(keyval); +}; + +export const isValidAccel = (mask: number, keyval: number) => { + return Gtk4.accelerator_valid(keyval, mask) || (keyval === Gdk4.KEY_Tab && mask !== 0); +}; + +export const isValidBinding = (mask: number, keycode: number, keyval: number) => { + return !( + mask === 0 || + (mask === Gdk4.ModifierType.SHIFT_MASK && + keycode !== 0 && + ((keyval >= Gdk4.KEY_a && keyval <= Gdk4.KEY_z) || + (keyval >= Gdk4.KEY_A && keyval <= Gdk4.KEY_Z) || + (keyval >= Gdk4.KEY_0 && keyval <= Gdk4.KEY_9) || + (keyval >= Gdk4.KEY_kana_fullstop && keyval <= Gdk4.KEY_semivoicedsound) || + (keyval >= Gdk4.KEY_Arabic_comma && keyval <= Gdk4.KEY_Arabic_sukun) || + (keyval >= Gdk4.KEY_Serbian_dje && keyval <= Gdk4.KEY_Cyrillic_HARDSIGN) || + (keyval >= Gdk4.KEY_Greek_ALPHAaccent && keyval <= Gdk4.KEY_Greek_omega) || + (keyval >= Gdk4.KEY_hebrew_doublelowline && keyval <= Gdk4.KEY_hebrew_taf) || + (keyval >= Gdk4.KEY_Thai_kokai && keyval <= Gdk4.KEY_Thai_lekkao) || + (keyval >= Gdk4.KEY_Hangul_Kiyeog && keyval <= Gdk4.KEY_Hangul_J_YeorinHieuh) || + (keyval === Gdk4.KEY_space && mask === 0) || + keyvalIsForbidden(keyval))) + ); +}; + @registerGObjectClass export class ShortcutRow extends Adw.ActionRow { private settings: Gio.Settings; @@ -48,7 +89,7 @@ export class ShortcutRow extends Adw.ActionRow { editor.add_controller(ctl); // See https://github.com/tuberry/color-picker/blob/1a278db139f00787e365fce5977d30b535529edb/color-picker%40tuberry/prefs.js - ctl.connect('key-pressed', (_, keyval, keycode, state) => { + ctl.connect('key-pressed', (_source, keyval, keycode, state) => { let mask = state & Gtk4.accelerator_get_default_mod_mask(); mask &= ~Gdk4.ModifierType.LOCK_MASK; if (!mask && keyval === Gdk4.KEY_Escape) { @@ -76,44 +117,3 @@ export class ShortcutRow extends Adw.ActionRow { this.set_activatable_widget(shortcutLabel); } } - -export const keyvalIsForbidden = (keyval: number) => { - return [ - Gdk4.KEY_Home, - Gdk4.KEY_Left, - Gdk4.KEY_Up, - Gdk4.KEY_Right, - Gdk4.KEY_Down, - Gdk4.KEY_Page_Up, - Gdk4.KEY_Page_Down, - Gdk4.KEY_End, - Gdk4.KEY_Tab, - Gdk4.KEY_KP_Enter, - Gdk4.KEY_Return, - Gdk4.KEY_Mode_switch, - ].includes(keyval); -}; - -export const isValidAccel = (mask: number, keyval: number) => { - return Gtk4.accelerator_valid(keyval, mask) || (keyval === Gdk4.KEY_Tab && mask !== 0); -}; - -export const isValidBinding = (mask: number, keycode: number, keyval: number) => { - return !( - mask === 0 || - (mask === Gdk4.ModifierType.SHIFT_MASK && - keycode !== 0 && - ((keyval >= Gdk4.KEY_a && keyval <= Gdk4.KEY_z) || - (keyval >= Gdk4.KEY_A && keyval <= Gdk4.KEY_Z) || - (keyval >= Gdk4.KEY_0 && keyval <= Gdk4.KEY_9) || - (keyval >= Gdk4.KEY_kana_fullstop && keyval <= Gdk4.KEY_semivoicedsound) || - (keyval >= Gdk4.KEY_Arabic_comma && keyval <= Gdk4.KEY_Arabic_sukun) || - (keyval >= Gdk4.KEY_Serbian_dje && keyval <= Gdk4.KEY_Cyrillic_HARDSIGN) || - (keyval >= Gdk4.KEY_Greek_ALPHAaccent && keyval <= Gdk4.KEY_Greek_omega) || - (keyval >= Gdk4.KEY_hebrew_doublelowline && keyval <= Gdk4.KEY_hebrew_taf) || - (keyval >= Gdk4.KEY_Thai_kokai && keyval <= Gdk4.KEY_Thai_lekkao) || - (keyval >= Gdk4.KEY_Hangul_Kiyeog && keyval <= Gdk4.KEY_Hangul_J_YeorinHieuh) || - (keyval === Gdk4.KEY_space && mask === 0) || - keyvalIsForbidden(keyval))) - ); -}; diff --git a/src/styles/stylesheet.css b/src/styles/stylesheet.css index 6936feab..b286b3b7 100644 --- a/src/styles/stylesheet.css +++ b/src/styles/stylesheet.css @@ -27,7 +27,7 @@ color: #f6d32d; } -.pano-item { +.pano-item .loading-pano-item{ border-radius: 11px; margin: 6px; border: 4px solid transparent; diff --git a/src/utils/clipboardManager.ts b/src/utils/clipboardManager.ts index 0de4faca..e28edb36 100644 --- a/src/utils/clipboardManager.ts +++ b/src/utils/clipboardManager.ts @@ -19,6 +19,7 @@ const MimeType = { SENSITIVE: ['x-kde-passwordManagerHint'], } as const; +// eslint-disable-next-line no-shadow export const enum ContentType { IMAGE, FILE, @@ -244,7 +245,7 @@ export class ClipboardManager extends GObject.Object { return clipboardMimeTypes.find((m) => targetMimeTypes.indexOf(m) >= 0); } - private async getContent(clipboardType: St.ClipboardType): Promise { + private getContent(clipboardType: St.ClipboardType): Promise { return new Promise((resolve) => { const cbMimeTypes = this.clipboard.get_mimetypes(clipboardType); if (this.haveMimeType(cbMimeTypes, MimeType.SENSITIVE)) { diff --git a/src/utils/code/cancellables.ts b/src/utils/code/cancellables.ts new file mode 100644 index 00000000..10eb5b69 --- /dev/null +++ b/src/utils/code/cancellables.ts @@ -0,0 +1,31 @@ +import Gio from 'gi://Gio?version=2.0'; + +export type CancellableWrapper = { + value: Gio.Cancellable; + id: string; +}; + +export class CancellableCollection { + private cancellables: Record = {}; + + getNew(): CancellableWrapper { + const id = new Date().getUTCMilliseconds().toString(); + const cancellable = new Gio.Cancellable(); + + this.cancellables[id] = cancellable; + + return { id, value: cancellable }; + } + + remove(cancellable: CancellableWrapper | undefined) { + if (cancellable) { + delete this.cancellables[cancellable.id]; + } + } + + removeAll() { + for (const cancellable of Object.values(this.cancellables)) { + cancellable.cancel(); + } + } +} diff --git a/src/utils/gjs.ts b/src/utils/gjs.ts index 30a8636d..1963bd77 100644 --- a/src/utils/gjs.ts +++ b/src/utils/gjs.ts @@ -14,13 +14,10 @@ export function registerGObjectClass< // Note that we use 'hasOwnProperty' because otherwise we would get inherited meta infos. // This would be bad because we would inherit the GObjectName too, which is supposed to be unique. if (Object.prototype.hasOwnProperty.call(target, 'metaInfo')) { - // eslint-disable-next-line - // @ts-ignore - // eslint-disable-next-line + // @ts-expect-error this is heavily js inspired code, this is correct like this, but TS wan't get it return GObject.registerClass(target.metaInfo!, target) as typeof target; } else { - // eslint-disable-next-line - // @ts-ignore + // @ts-expect-error this is heavily js inspired code, this is correct like this, but TS wan't get it return GObject.registerClass(target) as typeof target; } } diff --git a/src/utils/linkParser.ts b/src/utils/linkParser.ts index cc1ededa..71b8e402 100644 --- a/src/utils/linkParser.ts +++ b/src/utils/linkParser.ts @@ -1,4 +1,4 @@ -import Gio from '@girs/gio-2.0'; +import Gio, { type Promisified2 } from '@girs/gio-2.0'; import GLib from '@girs/glib-2.0'; import type { ExtensionBase } from '@girs/gnome-shell/dist/extensions/sharedInternals'; import Soup from '@girs/soup-3.0'; @@ -29,20 +29,18 @@ export const getDocument = async (url: string): Promise => { const message = Soup.Message.new('GET', url); message.requestHeaders.append('User-Agent', DEFAULT_USER_AGENT); //note: casting required, since this is a gjs convention, to return an promise, instead of accepting a 4. value as callback (thats a C convention, since there's no Promise out of the box, but a callback works) - const response = (await session.send_and_read_async( - message, - GLib.PRIORITY_DEFAULT, - null, - )) as any as GLib.Bytes | null; + const response = await ( + session.send_and_read_async as Promisified2 + )(message, GLib.PRIORITY_DEFAULT, null); - if (response == null) { + if (!response) { debug(`no response from ${url}`); return defaultResult; } const bytes = response.get_data(); - if (bytes == null) { + if (bytes === null) { debug(`no data from ${url}`); return defaultResult; } @@ -98,9 +96,9 @@ export const getDocument = async (url: string): Promise => { titleMatch = true; } }, - ontext(data) { + ontext(partialText) { if (titleMatch && !title) { - titleTag += data; + titleTag += partialText; } }, onclosetag(name) { @@ -148,17 +146,16 @@ export const getImage = async ( const message = Soup.Message.new('GET', imageUrl); message.requestHeaders.append('User-Agent', DEFAULT_USER_AGENT); //note: casting required, since this is a gjs convention, to return an promise, instead of accepting a 4. value as callback (thats a C convention, since there's no Promise out of the box, but a callback works) - const response = (await session.send_and_read_async( - message, - GLib.PRIORITY_DEFAULT, - null, - )) as any as GLib.Bytes | null; + const response = await ( + session.send_and_read_async as Promisified2 + )(message, GLib.PRIORITY_DEFAULT, null); + if (!response) { debug('no response while fetching the image'); return [null, null]; } const data = response.get_data(); - if (!data || data.length == 0) { + if (!data || data.length === 0) { debug('empty response while fetching the image'); return [null, null]; } diff --git a/src/utils/pango.ts b/src/utils/pango.ts index 816e3156..90441369 100644 --- a/src/utils/pango.ts +++ b/src/utils/pango.ts @@ -13,16 +13,17 @@ import { PygmentsCodeHighlighter } from '@pano/utils/code/pygments'; export class PangoMarkdown { private _detectedHighlighter: CodeHighlighter[] = []; - private _currentHighlighter: CodeHighlighter | null = null; public static readonly availableCodeHighlighter: CodeHighlighter[] = [new PygmentsCodeHighlighter()]; constructor(preferredHighlighter: string | null = null, settings: Gio.Settings | null = null) { - this.detectHighlighter(preferredHighlighter, settings); - if (settings) { - this.enableWatch(settings); - } + // this is fine, since the properties this sets are async safe, alias they are set at the end, so that everything is set when it's needed, and when something uses this class, before it is ready, it will behave correctly + void this.detectHighlighter(preferredHighlighter, settings).then(() => { + if (settings) { + this.enableWatch(settings); + } + }); } get detectedHighlighter() { @@ -34,42 +35,52 @@ export class PangoMarkdown { } // this is called in the constructor and can be called at any moment later by settings etc. - public detectHighlighter(preferredHighlighter: string | null = null, settings: Gio.Settings | null = null) { + public async detectHighlighter( + preferredHighlighter: string | null = null, + settings: Gio.Settings | null = null, + ): Promise { + // this is to be async safe this._detectedHighlighter = []; + const localDetectedHighlighter = []; + let currentHighlighter: CodeHighlighter | null = this._currentHighlighter; + this._currentHighlighter = null; for (const codeHighlighter of PangoMarkdown.availableCodeHighlighter) { - if (codeHighlighter.isInstalled()) { + if (await codeHighlighter.isInstalled()) { if (settings) { codeHighlighter.options = settings.get_string(PangoMarkdown.getSchemaKeyForOptions(codeHighlighter)); } - this._detectedHighlighter.push(codeHighlighter); + localDetectedHighlighter.push(codeHighlighter); if (preferredHighlighter === null) { - if (this._currentHighlighter === null) { - this._currentHighlighter = codeHighlighter; + if (currentHighlighter === null) { + currentHighlighter = codeHighlighter; } - } else if (codeHighlighter.name == preferredHighlighter) { - this._currentHighlighter = codeHighlighter; + } else if (codeHighlighter.name === preferredHighlighter) { + currentHighlighter = codeHighlighter; } } } + + this._detectedHighlighter = localDetectedHighlighter; + this._currentHighlighter = currentHighlighter; } - public detectLanguage(text: string): Language | undefined { + public async detectLanguage(text: string): Promise { if (this._currentHighlighter === null) { return undefined; } - return this._currentHighlighter.detectLanguage(text); + return await this._currentHighlighter.detectLanguage(text); } - public markupCode(language: string, text: string, characterLength: number): string | undefined { + public async markupCode(language: string, text: string, characterLength: number): Promise { if (this._currentHighlighter === null) { return undefined; } - return this._currentHighlighter.markupCode(language, text, characterLength); + return await this._currentHighlighter.markupCode(language, text, characterLength); } public static getSchemaKeyForOptions(highlighter: CodeHighlighter): string { @@ -80,7 +91,7 @@ export class PangoMarkdown { public static readonly enabledKey = 'code-highlighter-enabled'; private enableWatch(settings: Gio.Settings) { - const settingsChanged = () => { + const settingsChanged = async () => { const isEnabled = settings.get_boolean(PangoMarkdown.enabledKey); if (!isEnabled) { this._currentHighlighter = null; @@ -89,7 +100,7 @@ export class PangoMarkdown { const highlighterValue = settings.get_uint(PangoMarkdown.codeHighlighterKey); - this.detectHighlighter(PangoMarkdown.availableCodeHighlighter[highlighterValue]!.name); + await this.detectHighlighter(PangoMarkdown.availableCodeHighlighter[highlighterValue]!.name); }; settings.connect(`changed::${PangoMarkdown.enabledKey}`, settingsChanged); @@ -99,7 +110,7 @@ export class PangoMarkdown { for (const codeHighlighter of PangoMarkdown.availableCodeHighlighter) { const schemaKey = `changed::${PangoMarkdown.getSchemaKeyForOptions(codeHighlighter)}`; settings.connect(schemaKey, () => { - if (this._currentHighlighter?.name == codeHighlighter.name) { + if (this._currentHighlighter?.name === codeHighlighter.name) { this._currentHighlighter.options = settings.get_string(schemaKey); } }); @@ -107,4 +118,10 @@ export class PangoMarkdown { codeHighlighter; } } + + public stopProcesses() { + for (const highlighter of this._detectedHighlighter) { + highlighter.stopProcesses(); + } + } } diff --git a/src/utils/panoItemFactory.ts b/src/utils/panoItemFactory.ts index cfe0b447..554fb703 100644 --- a/src/utils/panoItemFactory.ts +++ b/src/utils/panoItemFactory.ts @@ -93,7 +93,7 @@ const findOrCreateDbItem = async (ext: PanoExtension, clip: ClipboardContent): P metaData: value.operation, }); - case ContentType.IMAGE: + case ContentType.IMAGE: { const checksum = GLib.compute_checksum_for_bytes(GLib.ChecksumType.MD5, new GLib.Bytes(value)); if (!checksum) { return null; @@ -114,8 +114,9 @@ const findOrCreateDbItem = async (ext: PanoExtension, clip: ClipboardContent): P size: value.length, }), }); + } - case ContentType.TEXT: + case ContentType.TEXT: { const trimmedValue = value.trim(); if (trimmedValue.toLowerCase().startsWith('http') && isValidUrl(trimmedValue)) { @@ -190,7 +191,7 @@ const findOrCreateDbItem = async (ext: PanoExtension, clip: ClipboardContent): P }); } - const detectedLanguage = ext.markdownDetector?.detectLanguage(trimmedValue); + const detectedLanguage = await ext.markdownDetector?.detectLanguage(trimmedValue); if (detectedLanguage && detectedLanguage.relevance >= MINIMUM_LANGUAGE_RELEVANCE) { return db.save({ @@ -214,42 +215,33 @@ const findOrCreateDbItem = async (ext: PanoExtension, clip: ClipboardContent): P matchValue: value, searchValue: value, }); - + } default: return null; } }; -export const createPanoItem = async ( - ext: PanoExtension, - clipboardManager: ClipboardManager, - clip: ClipboardContent, -): Promise => { - let dbItem: DBItem | null = null; - - try { - dbItem = await findOrCreateDbItem(ext, clip); - } catch (err) { - debug(`err: ${err}`); - return null; - } - - if (dbItem) { - if (getCurrentExtensionSettings(ext).get_boolean('send-notification-on-copy')) { - sendNotification(ext, dbItem); +export const removeItemResources = (ext: ExtensionBase, dbItem: DBItem) => { + db.delete(dbItem.id); + if (dbItem.itemType === 'LINK') { + const { image } = JSON.parse(dbItem.metaData || '{}'); + if (image && Gio.File.new_for_uri(`file://${getCachePath(ext)}/${image}.png`).query_exists(null)) { + Gio.File.new_for_uri(`file://${getCachePath(ext)}/${image}.png`).delete(null); + } + } else if (dbItem.itemType === 'IMAGE') { + const imageFilePath = `file://${getImagesPath(ext)}/${dbItem.content}.png`; + const imageFile = Gio.File.new_for_uri(imageFilePath); + if (imageFile.query_exists(null)) { + imageFile.delete(null); } - - return createPanoItemFromDb(ext, clipboardManager, dbItem); } - - return null; }; -export const createPanoItemFromDb = ( +export const createPanoItemFromDb = async ( ext: PanoExtension, clipboardManager: ClipboardManager, dbItem: DBItem | null, -): PanoItem | null => { +): Promise => { if (!dbItem) { return null; } @@ -269,7 +261,7 @@ export const createPanoItemFromDb = ( } if (!language) { - const detectedLanguage = ext.markdownDetector?.detectLanguage(dbItem.content.trim()); + const detectedLanguage = await ext.markdownDetector?.detectLanguage(dbItem.content.trim()); if (detectedLanguage && detectedLanguage.relevance >= MINIMUM_LANGUAGE_RELEVANCE) { language = detectedLanguage.language; } @@ -284,12 +276,16 @@ export const createPanoItemFromDb = ( }; if (language && ext.markdownDetector) { - try { - panoItem = new CodePanoItem(ext, clipboardManager, dbItem, language); - // this might fail in some really rare cases - } catch (err) { - debug(`Couldn't create a code item: ${err}`); + const characterLength = getCurrentExtensionSettings(ext).get_child('code-item').get_int('char-length'); + + const markup = await ext.markdownDetector?.markupCode(language, dbItem.content.trim(), characterLength); + + // this might return undefined in some really rare cases + if (!markup) { + debug("Couldn't create a code item: Couldn't generate markup"); panoItem = createTextPanoItem(); + } else { + panoItem = new CodePanoItem(ext, clipboardManager, dbItem, markup); } } else { panoItem = createTextPanoItem(); @@ -319,15 +315,15 @@ export const createPanoItemFromDb = ( } panoItem.connect('on-remove', (_, dbItemStr: string) => { - const dbItem: DBItem = JSON.parse(dbItemStr); - removeItemResources(ext, dbItem); + const removeDbItem: DBItem = JSON.parse(dbItemStr); + removeItemResources(ext, removeDbItem); }); panoItem.connect('on-favorite', (_, dbItemStr: string) => { - const dbItem: DBItem = JSON.parse(dbItemStr); + const favoriteDbItem: DBItem = JSON.parse(dbItemStr); db.update({ - ...dbItem, - copyDate: new Date(dbItem.copyDate), + ...favoriteDbItem, + copyDate: new Date(favoriteDbItem.copyDate), }); }); @@ -342,22 +338,6 @@ function converter(color: string): string | null { } } -export const removeItemResources = (ext: ExtensionBase, dbItem: DBItem) => { - db.delete(dbItem.id); - if (dbItem.itemType === 'LINK') { - const { image } = JSON.parse(dbItem.metaData || '{}'); - if (image && Gio.File.new_for_uri(`file://${getCachePath(ext)}/${image}.png`).query_exists(null)) { - Gio.File.new_for_uri(`file://${getCachePath(ext)}/${image}.png`).delete(null); - } - } else if (dbItem.itemType === 'IMAGE') { - const imageFilePath = `file://${getImagesPath(ext)}/${dbItem.content}.png`; - const imageFile = Gio.File.new_for_uri(imageFilePath); - if (imageFile.query_exists(null)) { - imageFile.delete(null); - } - } -}; - const sendNotification = (ext: ExtensionBase, dbItem: DBItem) => { const _ = gettext(ext); if (dbItem.itemType === 'IMAGE') { @@ -415,3 +395,28 @@ const sendNotification = (ext: ExtensionBase, dbItem: DBItem) => { ); } }; + +export const createPanoItem = async ( + ext: PanoExtension, + clipboardManager: ClipboardManager, + clip: ClipboardContent, +): Promise => { + let dbItem: DBItem | null = null; + + try { + dbItem = await findOrCreateDbItem(ext, clip); + } catch (err) { + debug(`err: ${err}`); + return null; + } + + if (dbItem) { + if (getCurrentExtensionSettings(ext).get_boolean('send-notification-on-copy')) { + sendNotification(ext, dbItem); + } + + return createPanoItemFromDb(ext, clipboardManager, dbItem); + } + + return null; +}; diff --git a/src/utils/shell.ts b/src/utils/shell.ts index 598e5a06..cc71d593 100644 --- a/src/utils/shell.ts +++ b/src/utils/shell.ts @@ -22,7 +22,7 @@ const deleteFile = (file: Gio.File) => { }); }; -const deleteDirectory = async (file: Gio.File) => { +const deleteDirectory = async (file: Gio.File): Promise => { try { const iter: Gio.FileEnumerator | undefined = await new Promise((resolve, reject) => { file.enumerate_children_async( @@ -30,9 +30,9 @@ const deleteDirectory = async (file: Gio.File) => { Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, GLib.PRIORITY_DEFAULT, null, - (file, res) => { + (childFile, res) => { try { - resolve(file?.enumerate_children_finish(res)); + resolve(childFile?.enumerate_children_finish(res)); } catch (e) { reject(e); } @@ -88,12 +88,23 @@ const deleteDirectory = async (file: Gio.File) => { await Promise.all(branches); } catch (e) { } finally { - return deleteFile(file); + await deleteFile(file); } }; export const getAppDataPath = (ext: ExtensionBase): string => `${GLib.get_user_data_dir()}/${ext.uuid}`; +export const getCurrentExtensionSettings = (ext: ExtensionBase): Gio.Settings => ext.getSettings(); + +export const getDbPath = (ext: ExtensionBase): string => { + const path = getCurrentExtensionSettings(ext).get_string('database-location'); + if (!path) { + return getAppDataPath(ext); + } + + return path; +}; + export const getImagesPath = (ext: ExtensionBase): string => `${getAppDataPath(ext)}/images`; export const getCachePath = (ext: ExtensionBase): string => `${GLib.get_user_cache_dir()}/${ext.uuid}`; @@ -144,16 +155,6 @@ export const deleteAppDirs = async (ext: ExtensionBase): Promise => { } }; -export const getDbPath = (ext: ExtensionBase): string => { - const path = getCurrentExtensionSettings(ext).get_string('database-location'); - if (!path) { - return getAppDataPath(ext); - } - - return path; -}; -export const getCurrentExtensionSettings = (ext: ExtensionBase): Gio.Settings => ext.getSettings(); - export const loadInterfaceXML = (ext: ExtensionBase, iface: string): any => { const uri = `file:///${ext.path}/dbus/${iface}.xml`; const file = Gio.File.new_for_uri(uri); @@ -180,7 +181,7 @@ export const playAudio = () => { const attr_event_id = GSound.ATTR_EVENT_ID; //TODO: log this in a better way! - if (attr_event_id == null) { + if (attr_event_id === null) { console.error("Can't use GSound.ATTR_EVENT_ID since it's null!"); return; } @@ -201,13 +202,13 @@ export const removeSoundContext = () => { export let debounceIds: number[] = []; -export function debounce(func: (...args: T) => void | Promise, wait: number) { +export function debounce(func: (...args: T) => void | Promise, wait: number) { let sourceId: null | number; return function (...args: T) { - const debouncedFunc = function (this: unknown) { + const debouncedFunc = function (this: S) { debounceIds = debounceIds.filter((id) => id !== sourceId); sourceId = null; - func.apply(this, args); + void func.apply(this, args); return GLib.SOURCE_REMOVE; };