Skip to content

Commit

Permalink
Merge pull request #93 from swsnr/update-gnome-shell-types
Browse files Browse the repository at this point in the history
Update to GNOME Shell 46.0.2 types
  • Loading branch information
swsnr authored Sep 7, 2024
2 parents 083c837 + 0b9648f commit 4c920b0
Show file tree
Hide file tree
Showing 13 changed files with 733 additions and 653 deletions.
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
},
"devDependencies": {
"@eslint/js": "^9.9.1",
"@girs/gjs": "^3.3.0",
"@girs/gnome-shell": "^46.0.0-beta6",
"@girs/soup-3.0": "^3.4.4-3.3.0",
"@girs/gjs": "4.0.0-beta.14",
"@girs/gnome-shell": "46.0.2",
"@girs/soup-3.0": "^3.4.4-4.0.0-beta.14",
"@tsconfig/strictest": "^2.0.5",
"eslint": "^9.9.1",
"eslint-config-prettier": "^9.1.0",
Expand Down
1,176 changes: 598 additions & 578 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

3 changes: 0 additions & 3 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,9 +178,6 @@ const initializeExtension = (

// Wire up the current source
const currentSource = settings.get_string("selected-source");
if (currentSource === null) {
throw new Error("Current source 'null'?");
}
const sourceSelector = destroyer.add(SourceSelector.forKey(currentSource));
indicator.updateSelectedSource(sourceSelector.selectedSource.metadata);
const sourceSettings = SourceSettings.fromBaseSettings(extension, settings);
Expand Down
98 changes: 98 additions & 0 deletions src/lib/fixes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright Sebastian Wiesner <[email protected]>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0.If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
//
// Alternatively, the contents of this file may be used under the terms
// of the GNU General Public License Version 2 or later, as described below:
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// Only use type imports here, as this module is used in prefs as well as extension code!
import type GLib from "gi://GLib";
import type Gio from "gi://Gio";
import type Soup from "gi://Soup";
import type Gtk from "gi://Gtk";

/**
* This module provides workarounds and fixes for various incomplete type
* declarations in Gnome shell types and its dependencies.
*/

/**
* @see https://github.com/gjsify/ts-for-gir/issues/196
*/
export interface GLibErrorWithStack extends GLib.Error {
readonly stack: string;
}

/**
* @see https://github.com/gjsify/ts-for-gir/issues/171#issuecomment-2117301067
*/
export interface PromisifiedFileOutputStream extends Gio.FileOutputStream {
// eslint-disable-next-line @typescript-eslint/no-misused-promises
splice_async(
source: Gio.InputStream,
flags: Gio.OutputStreamSpliceFlags,
io_priority: number,
cancellable?: Gio.Cancellable | null,
): Promise<ReturnType<Gio.OutputStream["splice_finish"]>>;
}

/**
* @see https://github.com/gjsify/ts-for-gir/issues/171#issuecomment-2117301067
*/
export interface PromisifiedGioFile extends Gio.File {
// eslint-disable-next-line @typescript-eslint/no-misused-promises
create_async(
flags: Gio.FileCreateFlags,
io_priority: number,
cancellable?: Gio.Cancellable | null,
): Promise<ReturnType<Gio.File["create_finish"]>>;

// eslint-disable-next-line @typescript-eslint/no-misused-promises
delete_async(
io_priority: number,
cancellable?: Gio.Cancellable | null,
): Promise<ReturnType<Gio.File["delete_finish"]>>;
}

/**
* @see https://github.com/gjsify/ts-for-gir/issues/171#issuecomment-2117301067
*/
export interface PromisifiedSoupSession extends Soup.Session {
// eslint-disable-next-line @typescript-eslint/no-misused-promises
send_async(
msg: Soup.Message,
io_priority: number,
cancellable?: Gio.Cancellable | null,
): Promise<ReturnType<Soup.Session["send_finish"]>>;

// eslint-disable-next-line @typescript-eslint/no-misused-promises
send_and_read_async(
msg: Soup.Message,
io_priority: number,
cancellable?: Gio.Cancellable | null,
): Promise<ReturnType<Soup.Session["send_and_read_finish"]>>;
}

/**
* @see https://github.com/gjsify/ts-for-gir/issues/200
* @see https://github.com/gjsify/ts-for-gir/issues/140
*/
export interface PromisifiedGtkFileDialog extends Gtk.FileDialog {
// eslint-disable-next-line @typescript-eslint/no-misused-promises
select_folder(
parent?: Gtk.Window | null,
cancellable?: Gio.Cancellable | null,
): Promise<ReturnType<Gtk.FileDialog["select_folder_finish"]>>;
}
15 changes: 10 additions & 5 deletions src/lib/network/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ import Soup from "gi://Soup";
import { IOError } from "../common/gio.js";

import type { ExtensionMetadata } from "resource:///org/gnome/shell/extensions/extension.js";
import {
PromisifiedFileOutputStream,
PromisifiedGioFile,
PromisifiedSoupSession,
} from "../fixes.js";

export const createSession = (
extensionMetadata: ExtensionMetadata,
Expand Down Expand Up @@ -83,7 +88,7 @@ export const getString = async (
cancellable: Gio.Cancellable,
): Promise<string> => {
const message = Soup.Message.new("GET", url);
const response = await session
const response = await (session as PromisifiedSoupSession)
.send_and_read_async(message, 0, cancellable)
.catch((cause: unknown) => {
throw new HttpRequestError(url, `Failed to get data from ${url}`, {
Expand Down Expand Up @@ -146,7 +151,7 @@ const deletePartialDownloadIgnoreError = async (
cancellable: Gio.Cancellable | null,
): Promise<void> => {
try {
await file.delete_async(0, cancellable);
await (file as PromisifiedGioFile).delete_async(0, cancellable);
} catch (error) {
console.warn(
`Failed to delete result of partial download at ${file.get_path() ?? ""}`,
Expand All @@ -173,7 +178,7 @@ export const downloadToFile = async (
return;
}
const message = Soup.Message.new("GET", url);
const source = await session
const source = await (session as PromisifiedSoupSession)
.send_async(message, 0, cancellable)
.catch((cause: unknown) => {
throw new HttpRequestError(url, `Failed to make GET request to ${url}`, {
Expand Down Expand Up @@ -215,15 +220,15 @@ export const downloadToFile = async (
}
// Now open the target file for reading, and safely delete it in case of error.
try {
const sink = await target
const sink = await (target as PromisifiedGioFile)
.create_async(Gio.FileCreateFlags.NONE, 0, null)
.catch((cause: unknown) => {
throw new IOError(
`Failed to open target file at ${target.get_path() ?? ""} to download from ${url}`,
{ cause },
);
});
await sink
await (sink as PromisifiedFileOutputStream)
.splice_async(
source,
Gio.OutputStreamSpliceFlags.CLOSE_SOURCE |
Expand Down
6 changes: 1 addition & 5 deletions src/lib/services/desktop-background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,6 @@ export class DesktopBackgroundService {
* @param image The image file to use as new background
*/
setBackgroundImageFile(image: Gio.File): void {
const uri = image.get_uri();
if (uri === null) {
throw new Error("Failed obtain URI from file");
}
this.backgroundImage = uri;
this.backgroundImage = image.get_uri();
}
}
14 changes: 4 additions & 10 deletions src/lib/services/image-metadata-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,9 @@ export class ImageMetadataStore {
* Store metadata for the given image.
*/
storedMetadataForImage(image: ImageFile) {
const uri = image.file.get_uri();
if (uri === null) {
throw new Error("Failed to get URI for current image, not storing!");
}
const stored: StoredMetadata = {
metadata: image.metadata,
uri: uri,
uri: image.file.get_uri(),
};
this.settings.set_string("current-metadata", JSON.stringify(stored));
}
Expand All @@ -87,11 +83,9 @@ export class ImageMetadataStore {
* @returns The stored image or null if no image was stored or the store was invalid
*/
loadFromMetadata(): ImageFile | null {
const storedRaw = this.settings.get_string("current-metadata");
if (storedRaw === null) {
return null;
}
const stored = parseStoredMetadata(storedRaw);
const stored = parseStoredMetadata(
this.settings.get_string("current-metadata"),
);
if (stored !== null) {
return {
metadata: stored.metadata,
Expand Down
4 changes: 1 addition & 3 deletions src/lib/services/refresh-error-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,7 @@ export class RefreshErrorHandler
// If the inner error is a GLib error use its message instead of the wrapper message for better accuracy
// and locatization.
const errorMessage =
(error.cause instanceof GLib.Error
? error.cause.message
: error.message) ?? "";
error.cause instanceof GLib.Error ? error.cause.message : error.message;
notification.body = i18n.format(description, errorMessage);
}
}
Expand Down
6 changes: 1 addition & 5 deletions src/lib/source/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,7 @@ export class SourceSettings {
extension: Extension,
settings: Gio.Settings,
): SourceSettings {
const schemaId = settings.schemaId;
if (schemaId === null) {
throw new Error("Base settings have no schema ID!");
}
return new SourceSettings(extension, schemaId);
return new SourceSettings(extension, settings.schemaId);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/lib/sources/apod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ const getImage = async (
cancellable: Gio.Cancellable,
): Promise<DownloadableImage> => {
const apiKey = settings.get_string("api-key");
if (apiKey === null || apiKey.length === 0) {
if (apiKey.length === 0) {
throw new InvalidAPIKeyError(metadata);
}

Expand Down
8 changes: 1 addition & 7 deletions src/lib/sources/bing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import Soup from "gi://Soup";

import metadata from "./metadata/bing.js";
import { Source } from "../source/source.js";
import { HttpRequestError, getJSON } from "../network/http.js";
import { getJSON } from "../network/http.js";
import { DownloadableImage } from "../download.js";
import { decodeQuery, encodeQuery } from "../network/uri.js";

Expand Down Expand Up @@ -76,12 +76,6 @@ export const getTodaysImages = async (
urlbaseUHD,
GLib.UriFlags.NONE,
);
if (imageUrl === null) {
throw new HttpRequestError(
url,
`Failed to join ${urlbaseUHD} to https://www.bing.com`,
);
}
const startdate = image.startdate;
const suggestedFilename = decodeQuery(imageUrl)["id"];
return {
Expand Down
15 changes: 4 additions & 11 deletions src/lib/ui/error-detail-dialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,12 @@ import { pgettext } from "resource:///org/gnome/shell/extensions/extension.js";

import { ModalDialog } from "resource:///org/gnome/shell/ui/modalDialog.js";
import { unfoldCauses } from "../common/error.js";
import { GLibErrorWithStack } from "../fixes.js";

/**
* Shortcut for `GLib.markup_escape_text`.
*/
function e(s: string): string {
const escaped = GLib.markup_escape_text(s, -1);
if (escaped === null) {
// This can't happen I believe, because markup_escape_text would always return a string when given a string, but
// let's guard against it nonetheless.
throw new Error(`Failed to escape markup in ${s}`);
}
return escaped;
}
const e = (s: string): string => GLib.markup_escape_text(s, -1);

const formatStacktrace = (stack: string | undefined): string => {
return (
Expand All @@ -63,7 +56,7 @@ const formatOneError = (error: unknown): string => {
const stack = formatStacktrace(error.stack);
return `<b>${e(error.name)}: ${e(error.message)}</b>\n${stack}`;
} else if (error instanceof GLib.Error) {
const stack = formatStacktrace(error.stack);
const stack = formatStacktrace((error as GLibErrorWithStack).stack);
return `<b>${error.toString()}</b>\n${stack}`;
} else if (typeof error === "string") {
return e(`<b>${error}</b>`);
Expand All @@ -90,7 +83,7 @@ export const ErrorDetailDialog = GObject.registerClass(
class ErrorDetailDialog extends ModalDialog {
private readonly messageLabel: St.Label;

constructor(params?: ModalDialog.ConstructorProperties) {
constructor(params?: Partial<ModalDialog.ConstructorProps>) {
super(params);

const contentBox = new St.BoxLayout({
Expand Down
33 changes: 11 additions & 22 deletions src/prefs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import SOURCES from "./lib/sources/metadata/all.js";
import { SourceMetadata } from "./lib/source/source.js";

import type { ExtensionMetadata } from "@girs/gnome-shell/extensions/extension";
import { PromisifiedGtkFileDialog } from "./lib/fixes.js";

Gio._promisify(Gtk.FileDialog.prototype, "select_folder");

Expand All @@ -55,17 +56,12 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.`;

const getTemplate = (name: string): string => {
const uri = GLib.uri_resolve_relative(
const getTemplate = (name: string): string =>
GLib.uri_resolve_relative(
import.meta.url,
`ui/${name}.ui`,
GLib.UriFlags.NONE,
);
if (uri === null) {
throw new Error(`Failed to resolve URI for template ${name}!`);
}
return uri;
};

interface AllSettings {
readonly extension: Gio.Settings;
Expand Down Expand Up @@ -137,13 +133,15 @@ const SourcesPage = GObject.registerClass(
if (picturesDirectory) {
dialog.initialFolder = Gio.file_new_for_path(picturesDirectory);
}
// eslint-disable-next-line @typescript-eslint/no-confusing-void-expression
const file = await (dialog.select_folder(
const file = await (dialog as PromisifiedGtkFileDialog).select_folder(
this.root as Gtk.Window,
null,
) as unknown as Promise<Gio.File>);
const value = new GLib.Variant("ms", file.get_uri());
this.settings.extension.set_value("image-download-folder", value);
);
if (file) {
const value = new GLib.Variant("ms", file.get_uri());
this.settings.extension.set_value("image-download-folder", value);
} else {
console.warn("No folder selected; dialog cancelled?");
}
}

private initialize(
Expand Down Expand Up @@ -176,9 +174,6 @@ const SourcesPage = GObject.registerClass(
});

const selectedKey = this.settings.extension.get_string("selected-source");
if (selectedKey === null) {
throw new Error("'selected-source' is null?");
}
const selectedSource = buttons.get(selectedKey)?.source;
if (typeof selectedSource === "undefined") {
throw new Error(`${selectedKey} does not denote a known source!`);
Expand All @@ -187,9 +182,6 @@ const SourcesPage = GObject.registerClass(
buttons.get(selectedKey)?.button.set_active(true);
this.settings.extension.connect("changed::selected-source", () => {
const newKey = this.settings.extension.get_string("selected-source");
if (newKey === null) {
throw new Error("'selected-source' is null?");
}
const item = buttons.get(newKey);
if (typeof item === "undefined") {
throw new Error(`Source ${newKey} not known?`);
Expand Down Expand Up @@ -303,9 +295,6 @@ export default class PictureOfTheDayPreferences extends ExtensionPreferences {
// Load relevant settings
const extensionSettings = this.getSettings();
const schema_id = extensionSettings.schemaId;
if (schema_id === null) {
throw new Error("Schema ID of settings schema unexpectedly null?");
}
const allSettings: AllSettings = {
extension: extensionSettings,
sourceAPOD: this.getSettings(`${schema_id}.source.${apod.key}`),
Expand Down

0 comments on commit 4c920b0

Please sign in to comment.