diff --git a/.devcontainer/development/Containerfile b/.devcontainer/development/Containerfile index 0b75da1..2af67f8 100644 --- a/.devcontainer/development/Containerfile +++ b/.devcontainer/development/Containerfile @@ -1,7 +1,7 @@ FROM registry.opensuse.org/opensuse/tumbleweed:latest -RUN zypper -n install sudo vim gawk git openssh-clients bash-completion \ - npm22 nodejs22 glib2-tools gettext-tools jq rsync zip +RUN zypper -n install sudo man vim gawk git openssh-clients bash-completion \ + npm22 nodejs22 glib2-tools gettext-tools jq yq rsync zip RUN npm install --global yarn diff --git a/.devcontainer/development/devcontainer.json b/.devcontainer/development/devcontainer.json index fbaff65..dc4279d 100644 --- a/.devcontainer/development/devcontainer.json +++ b/.devcontainer/development/devcontainer.json @@ -24,7 +24,8 @@ "dbaeumer.vscode-eslint", "github.vscode-github-actions", "mrorz.language-gettext", - "SonarSource.sonarlint-vscode" + "SonarSource.sonarlint-vscode", + "jq-syntax-highlighting.jq-syntax-highlighting" ] } } diff --git a/.mailmap b/.mailmap new file mode 100644 index 0000000..4b83e05 --- /dev/null +++ b/.mailmap @@ -0,0 +1,8 @@ +Alexandre Dufournet <3389563+alexduf@users.noreply.github.com> +Alexandre Dufournet +Alexandre Dufournet +Alexandre Dufournet Alex Dufournet +Alexandre Dufournet alexduf +Alexandre Dufournet alex + +Benjamin Balder Bach \ No newline at end of file diff --git a/changelog.yaml b/changelog.yaml new file mode 100644 index 0000000..695c73b --- /dev/null +++ b/changelog.yaml @@ -0,0 +1,4 @@ +1.0.0: + Released: ~ + Changes: + - Intial Release \ No newline at end of file diff --git a/package.json b/package.json index 862ac96..edef323 100644 --- a/package.json +++ b/package.json @@ -5,10 +5,6 @@ "main": "extension.js", "repository": "git@github.com:mackdk/gnome-github-notifications.git", "author": "Thomas Florio ", - "contributors": [ - "Alexandre Dufournet ", - "Mohammad Amin Chitgarha " - ], "license": "GPL-2.0-or-later", "scripts": { "clean": "rm -rf build", @@ -19,7 +15,7 @@ "deploy": "yarn build && yarn sync", "test": "TS_NODE_PROJECT='src/test/tsconfig.json' yarn run nyc mocha", "followLogs": "journalctl -f | grep \"\\[$(jq -r .name src/main/resources/metadata.json)\\]\"", - "updateTranslations": "test -d build/dist && (cd build/dist && xgettext --no-location -L JavaScript -o ../../src/main/po/gnome-github-manager.pot *.js && xgettext --no-location -L Glade -j -o ../../src/main/po/gnome-github-manager.pot ui/*.ui; sed -i 's/\"POT-Creation-Date[^\"]*\"/\"POT-Creation-Date: \\\\n\"/' ../../src/main/po/gnome-github-manager.pot) || echo 'Compile the extension first!'", + "updateTranslations": "test -d build/dist && (cd build/dist && xgettext --no-location --from-code UTF-8 -L JavaScript -o ../../src/main/po/gnome-github-manager.pot *.js && xgettext --no-location --from-code UTF-8 -L Glade -j -o ../../src/main/po/gnome-github-manager.pot ui/*.ui; sed -i 's/\"POT-Creation-Date[^\"]*\"/\"POT-Creation-Date: \\\\n\"/' ../../src/main/po/gnome-github-manager.pot) || echo 'Compile the extension first!'", "createZip": "test -d build/dist && (cd build/dist && zip -r ../gnome-github-manager-v$(jq -r .version ../../package.json).zip ./*) || echo 'Compile the extension first!'", "package": "yarn build && yarn createZip" }, @@ -53,6 +49,7 @@ "dependencies": { "@girs/gjs": "4.0.0-beta.36", "@girs/gnome-shell": "48.0.4", + "@girs/gnomedesktop-4.0": "^4.0.0-4.0.0-beta.36", "@girs/soup-3.0": "3.6.5-4.0.0-beta.36" }, "resolutions": { diff --git a/rollup.config.ts b/rollup.config.ts index 166dd9b..3125244 100644 --- a/rollup.config.ts +++ b/rollup.config.ts @@ -30,6 +30,7 @@ const explicitMappings = new Map( '@girs/gio-2.0': 'gi://Gio', '@girs/glib-2.0': 'gi://GLib', '@girs/gobject-2.0': 'gi://GObject', + '@girs/gnomedesktop-4.0': 'gi://GnomeDesktop?version=4.0', '@girs/gtk-4.0': 'gi://Gtk', '@girs/soup-3.0': 'gi://Soup?version=3.0', '@girs/st-16': 'gi://St', @@ -66,30 +67,6 @@ const typescriptPluginOptions: RollupTypescriptOptions = { }, }; -// prettier-ignore -// JQ template to convert a property file to a json -const jqPropertiesToJson = - // Start by splitting every line - 'split("\\n")' + - // Discard empty lines - '| map(select(length > 0)) ' + - // Discards comments - '| map(select(startswith("#") | not)) ' + - // Split on equals to divide the key from value - '| map(split("=")) ' + - // map to the json object - '| map({' + - // convert property key to json field removing initial and final spaces - '(.[0] | gsub("(^\\\\s+|\\\\s+$)"; "")): ' + - // convert property value to json value, splitting comma separated values into an array and removing spaces - '.[1] | tostring | gsub("(^\\\\s+|\\\\s+$)"; "") | gsub("\\\\s+"; " ") | gsub(",\\\\s+"; ",") | split(",")' + - '}) ' + - // add the new entry - '| add'; - -// JQ template to convert from package.json to extension-info.json adding the translators -const jqPackageToExtensionInfo = '{("version"): .version, ("authors"): [.author, .contributors[]]} + {$translators}'; - export default defineConfig([ { input: `${typescriptSourcesPath}/extension.ts`, @@ -130,13 +107,24 @@ export default defineConfig([ // Generate extension-info.json // prettier-ignore 'generate-extension-info': 'jq ' + + // Translators are extract from the PO files '--argjson translators "$(' + - `sed -nr 's/.*(Project-Id-Version|Language-Team): ([^\\"]+).*$/\\2/p' src/main/po/*.po ` + - '| paste -d "=" - - ' + - `| jq -R -s '${jqPropertiesToJson}'` + + 'jq -Rscf ./src/support/jq/po-header-to-json.jq src/main/po/*.po' + + ')" ' + + // Authors are extract from the git shortlog, considering only the commits after the fork + '--argjson authors "$(' + + 'git shortlog -sn gnome-github-notifications..HEAD | jq -Rscf src/support/jq/shortlog-to-authors.jq' + + ')" ' + + // Original authors are extract from the git shortlog, considering only the commits BEFORE the fork + '--argjson originalAuthors "$(' + + 'git shortlog -sn gnome-github-notifications | jq -Rscf src/support/jq/shortlog-to-authors.jq' + ')" ' + + // Changelog data + '--argjson changelog "$(yq eval -o json changelog.yaml)" ' + + // Just to keep consistent indentation with other JSON files '--indent 4 ' + - `'${jqPackageToExtensionInfo}' ` + + // Combine everything together + '--from-file ./src/support/jq/package-to-extension-info.jq ' + 'package.json ' + `> ${distributionPath}/extension-info.json`, }), diff --git a/src/main/po/gnome-github-manager.pot b/src/main/po/gnome-github-manager.pot index bb0460c..d29c86e 100644 --- a/src/main/po/gnome-github-manager.pot +++ b/src/main/po/gnome-github-manager.pot @@ -42,25 +42,34 @@ msgid "" "All the customizations made will be lost. This operation cannot be undone." msgstr "" -msgid "Version {0} r{1}" -msgstr "" - msgid "" "Integrate GitHub within the GNOME Desktop Environment.\n" "\n" "Based on GitHub Notifications by Alexandre Dufournet." msgstr "" -msgid "Source code on GitHub" +msgid "User Guide" msgstr "" -msgid "Reset to default" +msgid "GitHub Manager contributors" +msgstr "" + +msgid "GitHub Notifications contributors" +msgstr "" + +msgid "Translation to {0}" +msgstr "" + +msgid "Version {0} (development)" msgstr "" -msgid "Report an issue" +msgid "Version {0} released on {1}" +msgstr "" + +msgid "Reset to default" msgstr "" -msgid "User guide" +msgid "Report an Issue" msgstr "" msgid "About GitHub Manager" diff --git a/src/main/po/it.po b/src/main/po/it.po index 1289494..f045fac 100644 --- a/src/main/po/it.po +++ b/src/main/po/it.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: Italiano\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2023-04-30 20:45+0200\n" +"PO-Revision-Date: 2025-03-14 12:13+0100\n" "Last-Translator: \n" "Language-Team: Thomas Florio\n" "Language: it\n" @@ -16,7 +16,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 3.1.1\n" +"X-Generator: Poedit 3.4\n" msgid "Github Notifications" msgstr "Notifiche di GitHub" @@ -44,9 +44,6 @@ msgstr "" "Tutte le personalizzazioni verranno perse. Questa operazione non può essere " "annullata." -msgid "Version {0} r{1}" -msgstr "Versione {0} r{1}" - msgid "" "Integrate GitHub within the GNOME Desktop Environment.\n" "\n" @@ -56,18 +53,30 @@ msgstr "" "\n" "Basato su GitHub Notifications di Alexandre Dufournet." -msgid "Source code on GitHub" -msgstr "Codice sorgente su GitHub" +msgid "User Guide" +msgstr "Manuale Utente" + +msgid "GitHub Manager contributors" +msgstr "Sviluppatori di GitHub Manager" + +msgid "GitHub Notifications contributors" +msgstr "Sviluppatori di GitHub Notifications" + +msgid "Translation to {0}" +msgstr "Traduzione in {0}" + +msgid "Version {0} (development)" +msgstr "Versione {0} (ramo di sviluppo)" + +msgid "Version {0} released on {1}" +msgstr "Versione {0} rilasciata il {1}" msgid "Reset to default" msgstr "Ripristina predefiniti" -msgid "Report an issue" +msgid "Report an Issue" msgstr "Segnala un problema" -msgid "User guide" -msgstr "Manuale utente" - msgid "About GitHub Manager" msgstr "Informazioni su GitHub Manager" @@ -161,3 +170,6 @@ msgstr "Pulsante secondario" msgid "Secondary action button, shown on the left." msgstr "L'azione secondaria, mostrata a sinistra." + +#~ msgid "Source code on GitHub" +#~ msgstr "Codice sorgente su GitHub" diff --git a/src/main/resources/github.svg b/src/main/resources/gnome-github-manager.svg similarity index 100% rename from src/main/resources/github.svg rename to src/main/resources/gnome-github-manager.svg diff --git a/src/main/resources/ui/AdwPreferences.ui b/src/main/resources/ui/AdwPreferences.ui index 84903e7..ebeb1d6 100644 --- a/src/main/resources/ui/AdwPreferences.ui +++ b/src/main/resources/ui/AdwPreferences.ui @@ -11,11 +11,11 @@
- Report an issue + Report an Issue actions.reportBug - User guide + User Guide actions.userGuide
diff --git a/src/main/typescript/core/GitHubManager.ts b/src/main/typescript/core/GitHubManager.ts index 8f0ed20..190a681 100644 --- a/src/main/typescript/core/GitHubManager.ts +++ b/src/main/typescript/core/GitHubManager.ts @@ -22,7 +22,7 @@ export class GitHubManager implements Disposable { public constructor(name: string, path: string, settings: Gio.Settings) { GitHubManager.LOGGER.trace('Building and wiring components'); - this.githubIcon = Gio.icon_new_for_string(`${path}/github.svg`); + this.githubIcon = Gio.icon_new_for_string(`${path}/gnome-github-manager.svg`); this.eventDispatcher = new EventDispatcher(); this.settings = new SettingsWrapper(settings, this.eventDispatcher); diff --git a/src/main/typescript/preferences/PreferencesController.ts b/src/main/typescript/preferences/PreferencesController.ts index 409fa15..5e6a880 100644 --- a/src/main/typescript/preferences/PreferencesController.ts +++ b/src/main/typescript/preferences/PreferencesController.ts @@ -1,8 +1,9 @@ +import Adw from '@girs/adw-1'; import Gdk from '@girs/gdk-4.0'; -import Pixbuf from '@girs/gdkpixbuf-2.0'; import Gio from '@girs/gio-2.0'; import { gettext as _ } from '@girs/gnome-shell/dist/extensions/prefs'; import { ExtensionMetadata } from '@girs/gnome-shell/dist/types'; +import GnomeDesktop from '@girs/gnomedesktop-4.0'; import Gtk from '@girs/gtk-4.0'; import { Logger, formatString } from '@github-manager/utils'; @@ -19,10 +20,17 @@ interface DropDownParameters { items: string[]; } +interface ChangelogEntry { + released: string | null; + changes: string[]; +} + interface ExtensionInfo { version: string; authors: string[]; + originalAuthors: string[]; translators: Record; + changelog: Record; } export function createAndBindWidget( @@ -139,47 +147,87 @@ function resetToDefault(dialog: Gtk.Window, settings: Gio.Settings): void { function about(dialog: Gtk.Window, metadata: ExtensionMetadata): void { try { - const githubIcon = Pixbuf.Pixbuf.new_from_file_at_scale(`${metadata.path}/github.svg`, -1, 128, true); - const paintableLogo = Gtk.Image.new_from_pixbuf(githubIcon).get_paintable(); + const baseUrl = metadata.url; + if (baseUrl === undefined) { + throw new Error('Unable to identify the extension url'); + } const extensionInfo = getAdditionalExtensionInfo(`${metadata.path}/extension-info.json`); - const translatorsMap = new Map(Object.entries(extensionInfo.translators)); - const aboutDialog = new Gtk.AboutDialog({ + const iconTheme = Gtk.IconTheme.get_for_display(dialog.get_display()); + const originalSearchPath = iconTheme.get_search_path(); + + // Adding the extension path to the icon theme directory, so that icons can be retrieved by name + iconTheme.add_search_path(metadata.path); + + const aboutDialog = new Adw.AboutWindow({ transientFor: dialog, modal: true, - authors: extensionInfo.authors, - programName: metadata.name, - version: formatString(_('Version {0} r{1}'), extensionInfo.version, metadata.version), + applicationName: metadata.name, + applicationIcon: 'gnome-github-manager', + developerName: 'Thomas Florio aka mackdk', + version: extensionInfo.version, comments: _( 'Integrate GitHub within the GNOME Desktop Environment.\n\n' + 'Based on GitHub Notifications by Alexandre Dufournet.' ), - translatorCredits: Array.from(translatorsMap.entries()) - .map(([lang, translators]) => `${lang}:\n\t${translators.join('\n\t')}`) - .join('\n\n'), licenseType: Gtk.License.GPL_2_0, - website: metadata.url, - websiteLabel: _('Source code on GitHub'), + supportUrl: `${baseUrl}/discussions`, + issueUrl: `${baseUrl}/issues/new`, + copyright: '© 2022-2025 Thomas Florio', + website: baseUrl, }); - if (paintableLogo !== null) { - aboutDialog.logo = paintableLogo; - } - - aboutDialog.set_system_information(null); + aboutDialog.add_link(_('User Guide'), `${baseUrl}/wiki/User-guide`); + aboutDialog.add_credit_section(_('GitHub Manager contributors'), extensionInfo.authors); + aboutDialog.add_credit_section(_('GitHub Notifications contributors'), extensionInfo.originalAuthors); - const titleBar = aboutDialog.get_titlebar(); - if (titleBar instanceof Gtk.HeaderBar) { - titleBar.get_title_widget()?.set_visible(true); - } + // Add a section for each translated language + Object.entries(extensionInfo.translators).forEach(([languageCode, translators]: [string, string[]]): void => { + const language = getLocaleDisplayName(languageCode); + const sectionTitle = formatString(_('Translation to {0}'), language); + aboutDialog.add_credit_section(sectionTitle, translators); + }); + // Handle the changelog + const releaseNotes = Object.entries(extensionInfo.changelog) + .map(([version, entry]: [string, ChangelogEntry]): string => { + return ( + `

${getReleaseDescription(version, entry.released)}

` + + `
    ${getChangesAsListItems(entry.changes)}
` + ); + }) + .join(''); + const majorVersion = extensionInfo.version.substring(0, extensionInfo.version.indexOf('.')); + + aboutDialog.set_release_notes(releaseNotes); + aboutDialog.set_release_notes_version(majorVersion); + + aboutDialog.connect('close-request', () => { + // Cleanup the search path + iconTheme.set_search_path(originalSearchPath); + return false; + }); aboutDialog.present(); } catch (err) { LOGGER.error('Unable to open about dialog', err); } } +function getReleaseDescription(version: string, releasedDate: string | null): string { + if (releasedDate === null) { + return formatString(_('Version {0} (development)'), version); + } + + // Localize in the current locale + const formattedDate = new Date(releasedDate).toLocaleDateString(undefined, { dateStyle: 'long' }); + return formatString(_('Version {0} released on {1}'), version, formattedDate); +} + +function getChangesAsListItems(changes: string[]): string { + return changes.map((e) => `
  • ${e}
  • `).join(''); +} + function getAdditionalExtensionInfo(filename: string): ExtensionInfo { const [success, bytes] = Gio.File.new_for_path(filename).load_contents(null); if (!success) { @@ -192,3 +240,10 @@ function getAdditionalExtensionInfo(filename: string): ExtensionInfo { function openUrl(url: string, dialog: Gtk.Window): void { Gtk.show_uri(dialog, url, Gdk.CURRENT_TIME); } + +function getLocaleDisplayName(localeCode: string): string { + // Use Gnome utility to get the localized locale name + const result = GnomeDesktop.get_language_from_locale(localeCode, null); + // Remove the encoding part if present + return result.replace(/\[[^[\]]*\]$/, '').trim(); +} diff --git a/src/support/jq/package-to-extension-info.jq b/src/support/jq/package-to-extension-info.jq new file mode 100644 index 0000000..4952195 --- /dev/null +++ b/src/support/jq/package-to-extension-info.jq @@ -0,0 +1,25 @@ +# Creates the extension-info.json starting with the package.json and additional data + +# Create an object +{ + "version": .version, # Process the information from the input package.json + "authors": $authors, # Add the authors information for everyone who committed after the fork + "originalAuthors": $originalAuthors, # Add the authors information for everyone who committed before the fork + "translators": $translators, # Add the translators for each language + "changelog": ( # Add the changelog, ensuring the keys are lowercase + $changelog + | walk( + if type == "object" then + with_entries( + if .key | type == "string" then + .key |= ascii_downcase + else + . + end + ) + else + . + end + ) + ) +} \ No newline at end of file diff --git a/src/support/jq/po-header-to-json.jq b/src/support/jq/po-header-to-json.jq new file mode 100644 index 0000000..8e5ce3f --- /dev/null +++ b/src/support/jq/po-header-to-json.jq @@ -0,0 +1,16 @@ +# Extract the translators map in JSON from the + +# For each line +split("\n") + # Filter only the lines containing the information we need + | map(select(startswith("\"Language-Team:") or startswith("\"Language:"))) + # Extract only the values from those lines + | map(sub("^\"(Language|Language-Team): (?[\\w, ]+).*$"; "\(.value)")) + # Remove the empty lines + | map(select(length > 0)) + # Convert the values into arrays in order to split the translator names + | [ .[] | if (index(.) % 2) == 0 then split(", *"; null) else [.] end ] + # Convert the array into an object + | . as $array | reduce (range(0; $array | length; 2)) as $i ({}; + . + {($array[$i + 1][0]): $array[$i]} + ) \ No newline at end of file diff --git a/src/support/jq/shortlog-to-authors.jq b/src/support/jq/shortlog-to-authors.jq new file mode 100644 index 0000000..6c45da0 --- /dev/null +++ b/src/support/jq/shortlog-to-authors.jq @@ -0,0 +1,10 @@ +# converts the output of git shortlog to an array of authors + +# For each line +split("\n") + # Trim leading an trailing whitespace + | map(sub("^[ \t]+|[ \t]+$"; "")) + # Remove the number of commits and keep only the name + | map(sub("^[0-9]+\t"; "")) + # Discard empty lines + | map(select(length > 0)) diff --git a/yarn.lock b/yarn.lock index e90b4e3..9619334 100644 --- a/yarn.lock +++ b/yarn.lock @@ -478,7 +478,7 @@ "@girs/gmodule-2.0" "2.0.0-4.0.0-beta.36" "@girs/gobject-2.0" "2.84.4-4.0.0-beta.36" -"@girs/gnomedesktop-4.0@4.0.0-4.0.0-beta.36", "@girs/gnomedesktop-4.0@^4.0.0-4.0.0-beta.34": +"@girs/gnomedesktop-4.0@4.0.0-4.0.0-beta.36", "@girs/gnomedesktop-4.0@^4.0.0-4.0.0-beta.34", "@girs/gnomedesktop-4.0@^4.0.0-4.0.0-beta.36": version "4.0.0-4.0.0-beta.36" resolved "https://registry.yarnpkg.com/@girs/gnomedesktop-4.0/-/gnomedesktop-4.0-4.0.0-4.0.0-beta.36.tgz#1f80d353229f8d5b7ddf0ba5384cf432d0074ff5" integrity sha512-nLP86/2bNZaXn7gQcUZRD1NW42inm8DT/ojJE19K+ezS6f5tz6/eJlOma+WtXCK3/irEYpJMgF2R50eKnC4emA== @@ -730,7 +730,7 @@ "@girs/gmodule-2.0" "2.0.0-4.0.0-beta.36" "@girs/gobject-2.0" "2.84.4-4.0.0-beta.36" -"@girs/st-16@16.0.0-4.0.0-beta.36", "@girs/st-16@16.0.0-4.0.0-beta36", "@girs/st-16@^16.0.0-4.0.0-beta.34": +"@girs/st-16@16.0.0-4.0.0-beta.36", "@girs/st-16@^16.0.0-4.0.0-beta.34": version "16.0.0-4.0.0-beta.36" resolved "https://registry.yarnpkg.com/@girs/st-16/-/st-16-16.0.0-4.0.0-beta.36.tgz#4dc62dd31fa711a23c30506c8a13eb27cd00d72a" integrity sha512-CjdqjkavmVbUsTkADTQ0alQIa+7HIpJLZhxdExrgh7I2ORzUWhnLCql60VdU8qTI0aHHhs0BX/JQh1e4rkIJ/g==