[gnome-sound-recorder/wip/cdavis/typescript] general: Enable strict type mode
- From: Christopher Davis <christopherdavis src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-sound-recorder/wip/cdavis/typescript] general: Enable strict type mode
- Date: Tue, 9 Aug 2022 03:31:40 +0000 (UTC)
commit cb9f513fcf4b47a9017c3fd3399cb1cdbc2ab9d0
Author: Christopher Davis <christopherdavis gnome org>
Date: Mon Aug 8 23:11:38 2022 -0400
general: Enable strict type mode
Enables strict type checking on our TypeScript code.
package.json | 2 +-
src/application.ts | 15 ++++--
src/recorder.ts | 132 +++++++++++++++++++++++++--------------------
src/recorderWidget.ts | 9 ++--
src/recording.ts | 45 ++++++++--------
src/recordingList.ts | 117 +++++++++++++++++++++-------------------
src/recordingListWidget.ts | 14 +++--
src/row.ts | 32 +++++------
src/waveform.ts | 39 ++++++++------
src/window.ts | 21 +++++---
types/ambient.d.ts | 3 ++
11 files changed, 241 insertions(+), 188 deletions(-)
---
diff --git a/package.json b/package.json
index 22c9a22..ff2d74e 100644
--- a/package.json
+++ b/package.json
@@ -18,7 +18,7 @@
"@gi.ts/cli": "^1.5.5"
},
"scripts": {
- "typecheck": "tsc",
+ "typecheck": "tsc --strict",
"generate-types": "gi-ts generate",
"config": "gi-ts config"
}
diff --git a/src/application.ts b/src/application.ts
index 99a05cd..d7c2766 100644
--- a/src/application.ts
+++ b/src/application.ts
@@ -26,7 +26,6 @@ import GLib from 'gi://GLib';
import GObject from 'gi://GObject';
import Gst from 'gi://Gst';
import Gtk from 'gi://Gtk?version=4.0';
-import { AboutWindow } from 'types/adw.js';
export const RecordingsDir = Gio.file_new_for_path(GLib.build_filenamev([GLib.get_user_data_dir(),
pkg.name]));
export const CacheDir = Gio.file_new_for_path(GLib.build_filenamev([GLib.get_user_cache_dir(), pkg.name]));
@@ -35,7 +34,7 @@ export const Settings = new Gio.Settings({ schema: pkg.name });
import { Window, WindowClass } from './window.js';
export const Application = GObject.registerClass(class Application extends Adw.Application {
- private window: WindowClass;
+ private window?: WindowClass;
_init(): void {
super._init({ application_id: pkg.name, resource_base_path: '/org/gnome/SoundRecorder/' });
@@ -71,7 +70,9 @@ export const Application = GObject.registerClass(class Application extends Adw.A
let quitAction = new Gio.SimpleAction({ name: 'quit' });
quitAction.connect('activate', () => {
- this.get_active_window().close();
+ if (this.window) {
+ this.window.close();
+ }
});
this.add_action(quitAction);
@@ -103,7 +104,7 @@ export const Application = GObject.registerClass(class Application extends Adw.A
try {
CacheDir.make_directory_with_parents(null);
RecordingsDir.make_directory_with_parents(null);
- } catch (e) {
+ } catch (e: any) {
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.EXISTS))
console.error(`Failed to create directory ${e}`);
@@ -119,6 +120,10 @@ export const Application = GObject.registerClass(class Application extends Adw.A
}
_showAbout(): void {
+ let appName = GLib.get_application_name();
+ if (!appName)
+ appName = _('Sound Recorder');
+
let aboutDialog = new Adw.AboutWindow({
artists: [
'Reda Lazri <the red shortcut gmail com>',
@@ -135,7 +140,7 @@ export const Application = GObject.registerClass(class Application extends Adw.A
],
/* Translators: Replace "translator-credits" with your names, one name per line */
translator_credits: _('translator-credits'),
- application_name: GLib.get_application_name(),
+ application_name: appName,
comments: _('A Sound Recording Application for GNOME'),
license_type: Gtk.License.GPL_2_0,
application_icon: pkg.name,
diff --git a/src/recorder.ts b/src/recorder.ts
index 84d17e0..22ac8bc 100644
--- a/src/recorder.ts
+++ b/src/recorder.ts
@@ -60,10 +60,10 @@ export const EncodingProfiles = [
extension: 'm4a' },
];
-var AudioChannels = {
- 0: { name: 'stereo', channels: 2 },
- 1: { name: 'mono', channels: 1 },
-};
+var AudioChannels = [
+ { name: 'stereo', channels: 2 },
+ { name: 'mono', channels: 1 },
+];
export type RecorderClass = InstanceType<typeof Recorder>;
@@ -87,44 +87,49 @@ export const Recorder = GObject.registerClass({
_current_peak!: number;
pipeline: Gst.Pipeline;
- level: Gst.Element;
- ebin: Gst.Element;
- filesink: Gst.Element;
- recordBus: Gst.Bus;
- handlerId: number;
- file: Gio.File;
- timeout: number;
- _pipeState: Gst.State;
-
- _init(): void {
+ level?: Gst.Element;
+ ebin?: Gst.Element;
+ filesink?: Gst.Element;
+ recordBus?: Gst.Bus | null;
+ handlerId?: number | null;
+ file?: Gio.File;
+ timeout?: number | null;
+ _pipeState?: Gst.State;
+
+ constructor() {
+ super();
this._peaks = [];
- super._init({});
- let srcElement: Gst.Element, audioConvert: Gst.Element, caps: Gst.Caps;
+ let srcElement: Gst.Element;
+ let audioConvert: Gst.Element;
+ let caps: Gst.Caps;
+
+ this.pipeline = new Gst.Pipeline({ name: 'pipe' });
+
try {
- this.pipeline = new Gst.Pipeline({ name: 'pipe' });
- srcElement = Gst.ElementFactory.make('pulsesrc', 'srcElement');
- audioConvert = Gst.ElementFactory.make('audioconvert', 'audioConvert');
- caps = Gst.Caps.from_string('audio/x-raw');
- this.level = Gst.ElementFactory.make('level', 'level');
- this.ebin = Gst.ElementFactory.make('encodebin', 'ebin');
- this.filesink = Gst.ElementFactory.make('filesink', 'filesink');
+ srcElement = Gst.ElementFactory.make('pulsesrc', 'srcElement')!;
+ audioConvert = Gst.ElementFactory.make('audioconvert', 'audioConvert')!;
+ caps = Gst.Caps.from_string('audio/x-raw')!;
+ this.level = Gst.ElementFactory.make('level', 'level')!;
+ this.ebin = Gst.ElementFactory.make('encodebin', 'ebin')!;
+ this.filesink = Gst.ElementFactory.make('filesink', 'filesink')!;
} catch (error) {
log(`Not all elements could be created.\n${error}`);
}
try {
- this.pipeline.add(srcElement);
- this.pipeline.add(audioConvert);
- this.pipeline.add(this.level);
- this.pipeline.add(this.ebin);
- this.pipeline.add(this.filesink);
+ this.pipeline.add(srcElement!);
+ this.pipeline.add(audioConvert!);
+ this.pipeline.add(this.level!);
+ this.pipeline.add(this.ebin!);
+ this.pipeline.add(this.filesink!);
} catch (error) {
log(`Not all elements could be addded.\n${error}`);
}
- srcElement.link(audioConvert);
- audioConvert.link_filtered(this.level, caps);
+ srcElement!.link(audioConvert!);
+ audioConvert!.link_filtered(this.level!, caps!);
+
}
start(): void {
@@ -139,15 +144,15 @@ export const Recorder = GObject.registerClass({
this.recordBus = this.pipeline.get_bus();
this.recordBus.add_signal_watch();
this.handlerId = this.recordBus.connect('message', (_, message: Gst.Message) => {
- if (message !== null)
+ if (message)
this._onMessageReceived(message);
});
- this.ebin.set_property('profile', this._getProfile());
- this.filesink.set_property('location', this.file.get_path());
- this.level.link(this.ebin);
- this.ebin.link(this.filesink);
+ this.ebin!.set_property('profile', this._getProfile());
+ this.filesink!.set_property('location', this.file.get_path());
+ this.level!.link(this.ebin!);
+ this.ebin!.link(this.filesink!);
this.state = Gst.State.PLAYING;
@@ -168,7 +173,7 @@ export const Recorder = GObject.registerClass({
this.state = Gst.State.PLAYING;
}
- stop(): RecordingClass {
+ stop(): RecordingClass | undefined {
this.state = Gst.State.NULL;
this.duration = 0;
if (this.timeout) {
@@ -176,10 +181,11 @@ export const Recorder = GObject.registerClass({
this.timeout = null;
}
- if (this.recordBus) {
+ if (this.recordBus && this.handlerId) {
this.recordBus.remove_watch();
this.recordBus.disconnect(this.handlerId);
this.recordBus = null;
+ this.handlerId = null;
}
@@ -190,7 +196,7 @@ export const Recorder = GObject.registerClass({
return recording;
}
- return null;
+ return undefined;
}
_onMessageReceived(message: Gst.Message): void {
@@ -217,7 +223,10 @@ export const Recorder = GObject.registerClass({
this.stop();
break;
case Gst.MessageType.WARNING:
- log(message.parse_warning()[0].toString());
+ let warning = message.parse_warning()[0];
+ if (warning) {
+ log(warning.toString());
+ }
break;
case Gst.MessageType.ERROR:
log(message.parse_error().toString());
@@ -230,19 +239,24 @@ export const Recorder = GObject.registerClass({
return AudioChannels[channelIndex].channels;
}
- _getProfile(): GstPbutils.EncodingContainerProfile {
+ _getProfile(): GstPbutils.EncodingContainerProfile | undefined {
let profileIndex = Settings.get_enum('audio-profile');
const profile = EncodingProfiles[profileIndex];
let audioCaps = Gst.Caps.from_string(profile.audioCaps);
- audioCaps.set_value('channels', this._getChannel());
-
- let encodingProfile = GstPbutils.EncodingAudioProfile.new(audioCaps, null, null, 1);
- let containerCaps = Gst.Caps.from_string(profile.containerCaps);
- let containerProfile = GstPbutils.EncodingContainerProfile.new('record', null, containerCaps, null);
- containerProfile.add_profile(encodingProfile);
+ audioCaps?.set_value('channels', this._getChannel());
+
+ if (audioCaps) {
+ let encodingProfile = GstPbutils.EncodingAudioProfile.new(audioCaps, null, null, 1);
+ let containerCaps = Gst.Caps.from_string(profile.containerCaps);
+ if (containerCaps) {
+ let containerProfile = GstPbutils.EncodingContainerProfile.new('record', null,
containerCaps, null);
+ containerProfile.add_profile(encodingProfile);
+ return containerProfile;
+ }
+ }
- return containerProfile;
+ return undefined;
}
get duration(): number {
@@ -256,12 +270,14 @@ export const Recorder = GObject.registerClass({
// eslint-disable-next-line camelcase
set current_peak(peak: number) {
- if (peak > 0)
- peak = 0;
+ if (this._peaks) {
+ if (peak > 0)
+ peak = 0;
- this._current_peak = Math.pow(10, peak / 20);
- this._peaks.push(this._current_peak);
- this.notify('current-peak');
+ this._current_peak = Math.pow(10, peak / 20);
+ this._peaks.push(this._current_peak);
+ this.notify('current-peak');
+ }
}
set duration(val: number) {
@@ -269,16 +285,18 @@ export const Recorder = GObject.registerClass({
this.notify('duration');
}
- get state(): Gst.State {
+ get state(): Gst.State | undefined {
return this._pipeState;
}
- set state(s: Gst.State) {
+ set state(s: Gst.State | undefined) {
this._pipeState = s;
- const ret = this.pipeline.set_state(this._pipeState);
+ if (this._pipeState) {
+ const ret = this.pipeline.set_state(this._pipeState);
- if (ret === Gst.StateChangeReturn.FAILURE)
- log('Unable to update the recorder pipeline state');
+ if (ret === Gst.StateChangeReturn.FAILURE)
+ log('Unable to update the recorder pipeline state');
+ }
}
});
diff --git a/src/recorderWidget.ts b/src/recorderWidget.ts
index dd3da79..f282853 100644
--- a/src/recorderWidget.ts
+++ b/src/recorderWidget.ts
@@ -39,8 +39,8 @@ export const RecorderWidget = GObject.registerClass({
waveform: WaveFormClass;
actionsGroup: Gio.SimpleActionGroup;
- _init(recorder: RecorderClass): void {
- super._init({});
+ constructor(recorder: RecorderClass) {
+ super();
this.recorder = recorder;
this.waveform = new WaveForm({
@@ -122,8 +122,9 @@ export const RecorderWidget = GObject.registerClass({
case Gtk.ResponseType.YES: {
const recording = this.recorder.stop();
this.state = RecorderState.Stopped;
-
- recording.delete();
+ if (recording) {
+ recording.delete();
+ }
this.emit('canceled');
break;
}
diff --git a/src/recording.ts b/src/recording.ts
index 9df4ace..3b35c47 100644
--- a/src/recording.ts
+++ b/src/recording.ts
@@ -25,24 +25,25 @@ export const Recording = GObject.registerClass({
'name',
'Recording Name', 'Recording name in string',
GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT,
- null),
+ ''),
},
}, class Recording extends GObject.Object {
_file: Gio.File;
_peaks: number[];
_loadedPeaks: number[];
- _extension: string;
+ _extension?: string;
_timeModified: GLib.DateTime;
_timeCreated: GLib.DateTime;
- _duration: number;
+ _duration?: number;
- pipeline: Gst.Bin;
+ pipeline?: Gst.Bin | null;
+
+ constructor(file: Gio.File) {
+ super();
- _init(file: Gio.File): void {
this._file = file;
this._peaks = []
this._loadedPeaks = [];
- super._init({});
let info = file.query_info('time::created,time::modified,standard::content-type', 0, null);
const contentType = info.get_attribute_string('standard::content-type');
@@ -70,18 +71,18 @@ export const Recording = GObject.registerClass({
discoverer.discover_uri_async(this.uri);
}
- get name(): string {
+ get name(): string | null {
return this._file.get_basename();
}
- set name(filename: string) {
+ set name(filename: string | null) {
if (filename && filename !== this.name) {
this._file = this._file.set_display_name(filename, null);
this.notify('name');
}
}
- get extension(): string {
+ get extension(): string | undefined {
return this._extension;
}
@@ -115,8 +116,9 @@ export const Recording = GObject.registerClass({
this.emit('peaks-updated');
let enc = new TextEncoder();
const buffer = new GLib.Bytes(enc.encode(JSON.stringify(data)));
- this.waveformCache.replace_contents_bytes_async(buffer, null, false,
Gio.FileCreateFlags.REPLACE_DESTINATION, null, (obj: Gio.File, res: Gio.AsyncResult) => {
- obj.replace_contents_finish(res);
+ this.waveformCache.replace_contents_bytes_async(buffer, null, false,
Gio.FileCreateFlags.REPLACE_DESTINATION, null, (obj: Gio.File | null, res: Gio.AsyncResult) => {
+ if (obj)
+ obj.replace_contents_finish(res);
});
}
}
@@ -145,11 +147,11 @@ export const Recording = GObject.registerClass({
loadPeaks(): void {
if (this.waveformCache.query_exists(null)) {
- this.waveformCache.load_bytes_async(null, (obj: Gio.File, res: Gio.AsyncResult) => {
- const bytes = obj.load_bytes_finish(res)[0];
+ this.waveformCache.load_bytes_async(null, (obj: Gio.File | null, res: Gio.AsyncResult) => {
+ const bytes = obj?.load_bytes_finish(res)[0];
try {
let decoder = new TextDecoder('utf-8');
- this._peaks = JSON.parse(decoder.decode(bytes.get_data()));
+ this._peaks = JSON.parse(decoder.decode(bytes?.get_data()!));
this.emit('peaks-updated');
} catch (error) {
log(`Error reading waveform data file: ${this.name}_data`);
@@ -166,21 +168,20 @@ export const Recording = GObject.registerClass({
let uridecodebin = this.pipeline.get_by_name('uridecodebin');
- uridecodebin.set_property('uri', this.uri);
+ uridecodebin?.set_property('uri', this.uri);
let fakesink = this.pipeline.get_by_name('faked');
- fakesink.set_property('qos', false);
- fakesink.set_property('sync', true);
+ fakesink?.set_property('qos', false);
+ fakesink?.set_property('sync', true);
const bus = this.pipeline.get_bus();
this.pipeline.set_state(Gst.State.PLAYING);
- bus.add_signal_watch();
+ bus?.add_signal_watch();
- bus.connect('message', (_bus: Gst.Bus, message: Gst.Message) => {
- let s: Gst.Structure;
+ bus?.connect('message', (_bus: Gst.Bus, message: Gst.Message) => {
switch (message.type) {
case Gst.MessageType.ELEMENT:
- s = message.get_structure();
+ let s = message.get_structure();
if (s && s.has_name('level')) {
const peakVal = s.get_value('peak') as unknown as GObject.ValueArray;
@@ -192,7 +193,7 @@ export const Recording = GObject.registerClass({
break;
case Gst.MessageType.EOS:
this.peaks = this._loadedPeaks;
- this.pipeline.set_state(Gst.State.NULL);
+ this.pipeline?.set_state(Gst.State.NULL);
this.pipeline = null;
break;
}
diff --git a/src/recordingList.ts b/src/recordingList.ts
index 5a3d614..5c9c4c0 100644
--- a/src/recordingList.ts
+++ b/src/recordingList.ts
@@ -9,14 +9,13 @@ import { Recording, RecordingClass } from './recording.js';
export type RecordingListClass = InstanceType<typeof RecordingList>;
export const RecordingList = GObject.registerClass(class RecordingList extends Gio.ListStore {
- _enumerator: Gio.FileEnumerator;
+ _enumerator?: Gio.FileEnumerator;
cancellable: Gio.Cancellable;
dirMonitor: Gio.FileMonitor;
- _init(): void {
- super._init({ });
-
+ constructor() {
+ super();
this.cancellable = new Gio.Cancellable();
// Monitor Direcotry actions
this.dirMonitor = RecordingsDir.monitor_directory(Gio.FileMonitorFlags.WATCH_MOVES,
this.cancellable);
@@ -56,79 +55,85 @@ export const RecordingList = GObject.registerClass(class RecordingList extends G
const fileEnumerator = oldDir.enumerate_children('standard::name',
Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, this.cancellable);
let allCopied = true;
- const copyFiles = function (obj: Gio.FileEnumerator, res: Gio.AsyncResult) {
- try {
- const fileInfos = obj.next_files_finish(res);
- if (fileInfos.length) {
- fileInfos.forEach((info: Gio.FileInfo) => {
- const name = info.get_name();
- const src = oldDir.get_child(name);
- /* Translators: ""%s (Old)"" is the new name assigned to a file moved from
- the old recordings location */
- const dest = RecordingsDir.get_child(_('%s (Old)').format(name));
-
- // @ts-expect-error
- src.copy_async(dest, Gio.FileCopyFlags.OVERWRITE, GLib.PRIORITY_LOW,
this.cancellable, null, (objCopy: Gio.File, resCopy: Gio.AsyncResult) => {
- try {
- objCopy.copy_finish(resCopy);
- objCopy.trash_async(GLib.PRIORITY_LOW, this.cancellable, null);
- this.dirMonitor.emit_event(dest, src, Gio.FileMonitorEvent.MOVED_IN);
- } catch (e) {
- if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
- console.error(`Failed to copy recording ${name} to the new location`);
- log(e);
- }
- allCopied = false;
- }
- });
+ fileEnumerator.next_files_async(5, GLib.PRIORITY_LOW, this.cancellable, (obj, res) => {
+ this._copyFiles(obj, res, allCopied);
+ });
+ }
- });
- fileEnumerator.next_files_async(5, GLib.PRIORITY_LOW, this.cancellable, copyFiles);
- } else {
- fileEnumerator.close(this.cancellable);
- if (allCopied) {
- oldDir.delete_async(GLib.PRIORITY_LOW, this.cancellable, (objDelete: Gio.File,
resDelete: Gio.AsyncResult) => {
- try {
- objDelete.delete_finish(resDelete);
- } catch (e) {
- log('Failed to remove the old Recordings directory. Ignore if you\'re using
flatpak');
+ _copyFiles(fileEnumerator: Gio.FileEnumerator | null, res: Gio.AsyncResult, allCopied: boolean) {
+ let oldDir = fileEnumerator?.container;
+ try {
+ const fileInfos = fileEnumerator?.next_files_finish(res);
+ if (fileInfos && fileInfos.length) {
+ fileInfos.forEach((info: Gio.FileInfo) => {
+ const name = info.get_name();
+ const src = oldDir?.get_child(name);
+ /* Translators: ""%s (Old)"" is the new name assigned to a file moved from
+ the old recordings location */
+ const dest = RecordingsDir.get_child(_('%s (Old)').format(name));
+
+ // @ts-expect-error
+ src?.copy_async(dest, Gio.FileCopyFlags.OVERWRITE, GLib.PRIORITY_LOW, this.cancellable,
null, (objCopy: Gio.File, resCopy: Gio.AsyncResult) => {
+ try {
+ objCopy.copy_finish(resCopy);
+ objCopy.trash_async(GLib.PRIORITY_LOW, this.cancellable, null);
+ this.dirMonitor.emit_event(dest, src, Gio.FileMonitorEvent.MOVED_IN);
+ } catch (e: any) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
+ console.error(`Failed to copy recording ${name} to the new location`);
log(e);
}
- });
- }
- }
- } catch (e) {
- if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
- console.error(`Failed to copy old recordings ${e}`);
+ allCopied = false;
+ }
+ });
+ });
+ fileEnumerator?.next_files_async(5, GLib.PRIORITY_LOW, this.cancellable, (obj, res) => {
+ this._copyFiles(obj, res, allCopied);
+ });
+ } else {
+ fileEnumerator?.close(this.cancellable);
+ if (allCopied) {
+ oldDir?.delete_async(GLib.PRIORITY_LOW, this.cancellable, (objDelete: Gio.File | null,
resDelete: Gio.AsyncResult) => {
+ try {
+ objDelete?.delete_finish(resDelete);
+ } catch (e: any) {
+ log('Failed to remove the old Recordings directory. Ignore if you\'re using
flatpak');
+ log(e);
+ }
+ });
+ }
}
- }.bind(this);
- fileEnumerator.next_files_async(5, GLib.PRIORITY_LOW, this.cancellable, copyFiles);
+ } catch (e: any) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ console.error(`Failed to copy old recordings ${e}`);
+
+ }
}
- _enumerateDirectory(obj: Gio.File, res: Gio.AsyncResult): void {
- this._enumerator = obj.enumerate_children_finish(res);
+ _enumerateDirectory(obj: Gio.File | null, res: Gio.AsyncResult): void {
+ this._enumerator = obj?.enumerate_children_finish(res);
if (this._enumerator === null) {
log('The contents of the Recordings directory were not indexed.');
return;
}
- this._enumerator.next_files_async(5, GLib.PRIORITY_LOW, this.cancellable,
this._onNextFiles.bind(this));
+ this._enumerator?.next_files_async(5, GLib.PRIORITY_LOW, this.cancellable,
this._onNextFiles.bind(this));
}
- _onNextFiles(obj: Gio.FileEnumerator, res: Gio.AsyncResult): void {
+ _onNextFiles(obj: Gio.FileEnumerator | null, res: Gio.AsyncResult): void {
try {
- let fileInfos = obj.next_files_finish(res);
- if (fileInfos.length) {
+ let fileInfos = obj?.next_files_finish(res);
+ if (fileInfos && fileInfos.length) {
fileInfos.forEach(info => {
const file = RecordingsDir.get_child(info.get_name());
const recording = new Recording(file);
this.sortedInsert(recording);
});
- this._enumerator.next_files_async(5, GLib.PRIORITY_LOW, this.cancellable,
this._onNextFiles.bind(this));
+ this._enumerator?.next_files_async(5, GLib.PRIORITY_LOW, this.cancellable,
this._onNextFiles.bind(this));
} else {
- this._enumerator.close(this.cancellable);
+ this._enumerator?.close(this.cancellable);
}
- } catch (e) {
+ } catch (e: any) {
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
console.error(`Failed to load recordings ${e}`);
diff --git a/src/recordingListWidget.ts b/src/recordingListWidget.ts
index b8a40ac..e34ea18 100644
--- a/src/recordingListWidget.ts
+++ b/src/recordingListWidget.ts
@@ -18,11 +18,11 @@ export const RecordingsListWidget = GObject.registerClass({
}, class RecordingsListWidget extends Adw.Bin {
_player: GstPlayer.Player;
list: Gtk.ListBox;
- activeRow: RowClass;
- activePlayingRow: RowClass;
+ activeRow?: RowClass | null;
+ activePlayingRow?: RowClass | null;
- _init(model: Gio.ListModel, player: GstPlayer.Player): void {
- super._init();
+ constructor(model: Gio.ListModel, player: GstPlayer.Player) {
+ super();
this.list = Gtk.ListBox.new();
this.list.valign = Gtk.Align.START;
this.list.margin_start = 8;
@@ -40,7 +40,8 @@ export const RecordingsListWidget = GObject.registerClass({
this.activePlayingRow.state = RowState.Paused;
this.activePlayingRow.waveform.position = 0.0;
} else if (state === GstPlayer.PlayerState.PLAYING) {
- this.activePlayingRow.state = RowState.Playing;
+ if (this.activePlayingRow)
+ this.activePlayingRow.state = RowState.Playing;
}
});
@@ -51,7 +52,10 @@ export const RecordingsListWidget = GObject.registerClass({
}
});
+ // @ts-expect-error
this.list.bind_model(model, (recording: RecordingClass) => {
+ // This is weird - does using `constructor()` break how it's recognized?
+ // @ts-expect-error
let row = new Row(recording);
row.waveform.connect('gesture-pressed', _ => {
diff --git a/src/row.ts b/src/row.ts
index 4ab6cc1..00b8a9e 100644
--- a/src/row.ts
+++ b/src/row.ts
@@ -43,13 +43,13 @@ export const Row = GObject.registerClass({
_rightStack!: Gtk.Stack;
_name!: Gtk.Label;
_entry!: Gtk.Entry;
- _date: Gtk.Label;
- _duration: Gtk.Label;
- _revealer: Gtk.Revealer;
- _playbackControls: Gtk.Box;
- _saveBtn: Gtk.Button;
- _playBtn: Gtk.Button;
- _pauseBtn: Gtk.Button;
+ _date!: Gtk.Label;
+ _duration!: Gtk.Label;
+ _revealer!: Gtk.Revealer;
+ _playbackControls!: Gtk.Box;
+ _saveBtn!: Gtk.Button;
+ _playBtn!: Gtk.Button;
+ _pauseBtn!: Gtk.Button;
_recording: RecordingClass;
_expanded: boolean;
@@ -58,7 +58,7 @@ export const Row = GObject.registerClass({
waveform: WaveFormClass;
actionGroup: Gio.SimpleActionGroup;
- exportDialog: Gtk.FileChooserNative;
+ exportDialog?: Gtk.FileChooserNative | null;
saveRenameAction: Gio.SimpleAction;
renameAction: Gio.SimpleAction;
@@ -66,12 +66,13 @@ export const Row = GObject.registerClass({
playAction: Gio.SimpleAction;
keyController: Gtk.EventControllerKey;
- _init(recording: RecordingClass): void {
+ constructor(recording: RecordingClass) {
+ super();
+
this._recording = recording;
this._expanded = false;
this._editMode = false;
-
- super._init({});
+ this._state = RowState.Paused;
this.waveform = new WaveForm({
margin_top: 18,
@@ -86,7 +87,7 @@ export const Row = GObject.registerClass({
this._recording.loadPeaks();
}
- if (recording.timeModified !== null && !recording.timeModified.equal(recording.timeCreated))
+ if (recording.timeModified)
this._date.label = displayDateTime(recording.timeModified);
else
this._date.label = displayDateTime(recording.timeCreated);
@@ -104,10 +105,11 @@ export const Row = GObject.registerClass({
this.exportDialog.set_current_name(`${this._recording.name}.${this._recording.extension}`);
this.exportDialog.connect('response', (_dialog: Gtk.FileChooserNative, response: number) => {
if (response === Gtk.ResponseType.ACCEPT) {
- const dest = this.exportDialog.get_file();
- this._recording.save(dest);
+ const dest = this.exportDialog?.get_file();
+ if (dest)
+ this._recording.save(dest);
}
- this.exportDialog.destroy();
+ this.exportDialog?.destroy();
this.exportDialog = null;
});
this.exportDialog.show();
diff --git a/src/waveform.ts b/src/waveform.ts
index e1cc860..523e311 100644
--- a/src/waveform.ts
+++ b/src/waveform.ts
@@ -59,19 +59,19 @@ export const WaveForm = GObject.registerClass({
}, class WaveForm extends Gtk.DrawingArea {
_peaks: number[];
_position: number;
- _dragGesture: Gtk.GestureDrag;
+ _dragGesture?: Gtk.GestureDrag;
_hcId: number;
- _lastX: number;
+ _lastX?: number;
lastPosition: number;
waveType: WaveType;
- _init(params, type: WaveType): void {
+ constructor(params: Partial<Gtk.DrawingArea.ConstructorProperties> | undefined, type: WaveType) {
+ super(params);
this._peaks = [];
this._position = 0;
this.lastPosition = 0;
this.waveType = type;
- super._init(params);
if (this.waveType === WaveType.Player) {
this._dragGesture = Gtk.GestureDrag.new();
@@ -94,8 +94,10 @@ export const WaveForm = GObject.registerClass({
}
dragUpdate(_gesture: Gtk.GestureDrag, offsetX: number): void {
- this._position = this._clamped(offsetX + this._lastX);
- this.queue_draw();
+ if (this._lastX) {
+ this._position = this._clamped(offsetX + this._lastX);
+ this.queue_draw();
+ }
}
dragEnd(): void {
@@ -103,7 +105,8 @@ export const WaveForm = GObject.registerClass({
this.emit('position-changed', this.position);
}
- drawFunc(da: WaveFormClass, ctx: Cairo.Context) {
+ drawFunc(superDa: Gtk.DrawingArea, ctx: Cairo.Context) {
+ let da = superDa as WaveFormClass;
const maxHeight = da.get_allocated_height();
const vertiCenter = maxHeight / 2;
const horizCenter = da.get_allocated_width() / 2;
@@ -148,12 +151,14 @@ export const WaveForm = GObject.registerClass({
});
}
- set peak(p) {
- if (this._peaks.length > this.get_allocated_width() / (2 * GUTTER))
- this._peaks.pop();
+ set peak(p: number) {
+ if (this._peaks) {
+ if (this._peaks.length > this.get_allocated_width() / (2 * GUTTER))
+ this._peaks.pop();
- this._peaks.unshift(p.toFixed(2));
- this.queue_draw();
+ this._peaks.unshift(p.toFixed(2));
+ this.queue_draw();
+ }
}
set peaks(p: number[]) {
@@ -162,10 +167,12 @@ export const WaveForm = GObject.registerClass({
}
set position(pos: number) {
- this._position = this._clamped(-pos * this._peaks.length * GUTTER);
- this._lastX = this._position;
- this.queue_draw();
- this.notify('position');
+ if (this._peaks) {
+ this._position = this._clamped(-pos * this._peaks.length * GUTTER);
+ this._lastX = this._position;
+ this.queue_draw();
+ this.notify('position');
+ }
}
get position(): number {
diff --git a/src/window.ts b/src/window.ts
index 534581f..beee9a2 100644
--- a/src/window.ts
+++ b/src/window.ts
@@ -61,15 +61,16 @@ export const Window = GObject.registerClass({
_recordingListWidget: RecordingsListWidgetClass;
toastUndo: boolean;
- undoSignalID: number;
+ undoSignalID: number | null;
undoAction: Gio.SimpleAction;
_state: WindowState;
- _init(params): void {
- super._init(Object.assign({
- icon_name: pkg.name,
- }, params));
+ constructor(params: Partial<Adw.Application.ConstructorProperties>) {
+ super(params);
+
+ this.iconName = pkg.name;
+ this._state = WindowState.Empty;
this.recorder = new Recorder();
this.recorderWidget = new RecorderWidget(this.recorder);
@@ -94,7 +95,13 @@ export const Window = GObject.registerClass({
this._recordingListWidget.connect('row-deleted', (_listBox: Gtk.ListBox, recording: RecordingClass,
index: number) => {
this._recordingList.remove(index);
- this.sendNotification(_('"%s" deleted').format(recording.name), recording, index);
+ let message: string;
+ if (recording.name) {
+ message = _('"%s" deleted').format(recording.name);
+ } else {
+ message = _('Recording deleted');
+ }
+ this.sendNotification(message, recording, index);
});
const builder = Gtk.Builder.new_from_resource('/org/gnome/SoundRecorder/gtk/help-overlay.ui');
@@ -108,7 +115,7 @@ export const Window = GObject.registerClass({
let openMenuAction = new Gio.SimpleAction({ name: 'open-primary-menu', state: new GLib.Variant('b',
true) });
openMenuAction.connect('activate', action => {
- const state = action.get_state().get_boolean();
+ const state = action.get_state()?.get_boolean();
action.state = new GLib.Variant('b', !state);
});
this.add_action(openMenuAction);
diff --git a/types/ambient.d.ts b/types/ambient.d.ts
index ba0bd11..d77bf73 100644
--- a/types/ambient.d.ts
+++ b/types/ambient.d.ts
@@ -64,4 +64,7 @@ declare class TextEncoder {
declare interface String {
format(...replacements: string[]): string;
format(...replacements: number[]): string;
+}
+declare interface Number {
+ toFixed(digits: number): number;
}
\ No newline at end of file
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]