From 6ebe7be2e67be7b9b54d61ce5704f6fb466c536f Mon Sep 17 00:00:00 2001
From: Tad Fisher <tadfisher@gmail.com>
Date: Sun, 16 May 2021 14:54:43 -0700
Subject: [PATCH] gnome-shell: add module

---
 modules/misc/news.nix                         |  12 ++
 modules/modules.nix                           |   1 +
 modules/programs/gnome-shell.nix              | 115 ++++++++++++++++++
 tests/default.nix                             |   1 +
 .../modules/programs/gnome-shell/default.nix  |   1 +
 .../programs/gnome-shell/gnome-shell.nix      |  94 ++++++++++++++
 6 files changed, 224 insertions(+)
 create mode 100644 modules/programs/gnome-shell.nix
 create mode 100644 tests/modules/programs/gnome-shell/default.nix
 create mode 100644 tests/modules/programs/gnome-shell/gnome-shell.nix

diff --git a/modules/misc/news.nix b/modules/misc/news.nix
index ef3b98f5..16f372c8 100644
--- a/modules/misc/news.nix
+++ b/modules/misc/news.nix
@@ -1595,6 +1595,18 @@ in {
           when idle or active. See https://github.com/hyprwm/hypridle for more.
         '';
       }
+
+      {
+        time = "2024-05-06T07:36:13+00:00";
+        condition = hostPlatform.isLinux;
+        message = ''
+          A new module is available: 'programs.gnome-shell'.
+
+          GNOME Shell is the graphical shell of the GNOME desktop environment.
+          It provides basic functions like launching applications and switching
+          between windows, and is also a widget engine.
+        '';
+      }
     ];
   };
 }
diff --git a/modules/modules.nix b/modules/modules.nix
index b59812b7..98f7de80 100644
--- a/modules/modules.nix
+++ b/modules/modules.nix
@@ -103,6 +103,7 @@ let
     ./programs/git-credential-oauth.nix
     ./programs/git.nix
     ./programs/gitui.nix
+    ./programs/gnome-shell.nix
     ./programs/gnome-terminal.nix
     ./programs/go.nix
     ./programs/gpg.nix
diff --git a/modules/programs/gnome-shell.nix b/modules/programs/gnome-shell.nix
new file mode 100644
index 00000000..36a57cea
--- /dev/null
+++ b/modules/programs/gnome-shell.nix
@@ -0,0 +1,115 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.programs.gnome-shell;
+
+  extensionOpts = { config, ... }: {
+    options = {
+      id = mkOption {
+        type = types.str;
+        example = "user-theme@gnome-shell-extensions.gcampax.github.com";
+        description = ''
+          ID of the GNOME Shell extension. If not provided, it
+          will be obtained from `package.extensionUuid`.
+        '';
+      };
+
+      package = mkOption {
+        type = types.package;
+        example = "pkgs.gnome.gnome-shell-extensions";
+        description = ''
+          Package providing a GNOME Shell extension in
+          `$out/share/gnome-shell/extensions/''${id}`.
+        '';
+      };
+    };
+
+    config = mkIf (hasAttr "extensionUuid" config.package) {
+      id = mkDefault config.package.extensionUuid;
+    };
+  };
+
+  themeOpts = {
+    options = {
+      name = mkOption {
+        type = types.str;
+        example = "Plata-Noir";
+        description = ''
+          Name of the GNOME Shell theme.
+        '';
+      };
+
+      package = mkOption {
+        type = types.nullOr types.package;
+        default = null;
+        example = literalExpression "pkgs.plata-theme";
+        description = ''
+          Package providing a GNOME Shell theme in
+          `$out/share/themes/''${name}/gnome-shell`.
+        '';
+      };
+    };
+  };
+
+in {
+  meta.maintainers = [ maintainers.terlar ];
+
+  options.programs.gnome-shell = {
+    enable = mkEnableOption "GNOME Shell customization";
+
+    extensions = mkOption {
+      type = types.listOf (types.submodule extensionOpts);
+      default = [ ];
+      example = literalExpression ''
+        [
+          { package = pkgs.gnomeExtensions.dash-to-panel; }
+          {
+            id = "user-theme@gnome-shell-extensions.gcampax.github.com";
+            package = pkgs.gnome.gnome-shell-extensions;
+          }
+        ]
+      '';
+      description = ''
+        List of GNOME Shell extensions.
+      '';
+    };
+
+    theme = mkOption {
+      type = types.nullOr (types.submodule themeOpts);
+      default = null;
+      example = literalExpression ''
+        {
+          name = "Plata-Noir";
+          package = pkgs.plata-theme;
+        }
+      '';
+      description = ''
+        Theme to use for GNOME Shell.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable (mkMerge [
+    (mkIf (cfg.extensions != [ ]) {
+      dconf.settings."org/gnome/shell" = {
+        disable-user-extensions = false;
+        enabled-extensions = catAttrs "id" cfg.extensions;
+      };
+
+      home.packages = catAttrs "package" cfg.extensions;
+    })
+
+    (mkIf (cfg.theme != null) {
+      dconf.settings."org/gnome/shell/extensions/user-theme".name =
+        cfg.theme.name;
+
+      programs.gnome-shell.extensions = [{
+        id = "user-theme@gnome-shell-extensions.gcampax.github.com";
+        package = pkgs.gnome.gnome-shell-extensions;
+      }];
+
+      home.packages = [ cfg.theme.package ];
+    })
+  ]);
+}
diff --git a/tests/default.nix b/tests/default.nix
index e879c13c..2ecffad8 100644
--- a/tests/default.nix
+++ b/tests/default.nix
@@ -193,6 +193,7 @@ in import nmtSrc {
     ./modules/programs/freetube
     ./modules/programs/fuzzel
     ./modules/programs/getmail
+    ./modules/programs/gnome-shell
     ./modules/programs/gnome-terminal
     ./modules/programs/hexchat
     ./modules/programs/i3blocks
diff --git a/tests/modules/programs/gnome-shell/default.nix b/tests/modules/programs/gnome-shell/default.nix
new file mode 100644
index 00000000..58e4598f
--- /dev/null
+++ b/tests/modules/programs/gnome-shell/default.nix
@@ -0,0 +1 @@
+{ gnome-shell = ./gnome-shell.nix; }
diff --git a/tests/modules/programs/gnome-shell/gnome-shell.nix b/tests/modules/programs/gnome-shell/gnome-shell.nix
new file mode 100644
index 00000000..02f44e0c
--- /dev/null
+++ b/tests/modules/programs/gnome-shell/gnome-shell.nix
@@ -0,0 +1,94 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  dummy-gnome-shell-extensions = pkgs.runCommand "dummy-package" { } ''
+    mkdir -p $out/share/gnome-shell/extensions/dummy-package
+    touch $out/share/gnome-shell/extensions/dummy-package/test
+  '';
+
+  test-extension = pkgs.runCommand "test-extension" { } ''
+    mkdir -p $out/share/gnome-shell/extensions/test-extension
+    touch $out/share/gnome-shell/extensions/test-extension/test
+  '';
+
+  test-extension-uuid = pkgs.runCommand "test-extension-uuid" {
+    passthru.extensionUuid = "test-extension-uuid";
+  } ''
+    mkdir -p $out/share/gnome-shell/extensions/test-extension-uuid
+    touch $out/share/gnome-shell/extensions/test-extension-uuid/test
+  '';
+
+  test-theme = pkgs.runCommand "test-theme" { } ''
+    mkdir -p $out/share/themes/Test/gnome-shell
+    touch $out/share/themes/Test/gnome-shell/test
+  '';
+
+  expectedEnabledExtensions = [
+    "user-theme@gnome-shell-extensions.gcampax.github.com"
+    "test-extension"
+    "test-extension-uuid"
+  ];
+
+  actualEnabledExtensions = catAttrs "value"
+    config.dconf.settings."org/gnome/shell".enabled-extensions.value;
+
+in {
+  nixpkgs.overlays = [
+    (self: super: {
+      gnome = super.gnome.overrideScope (gself: gsuper: {
+        gnome-shell-extensions = dummy-gnome-shell-extensions;
+      });
+    })
+  ];
+
+  programs.gnome-shell.enable = true;
+
+  programs.gnome-shell.extensions = [
+    {
+      id = "test-extension";
+      package = test-extension;
+    }
+    { package = test-extension-uuid; }
+  ];
+
+  programs.gnome-shell.theme = {
+    name = "Test";
+    package = test-theme;
+  };
+
+  assertions = [
+    {
+      assertion =
+        config.dconf.settings."org/gnome/shell".disable-user-extensions
+        == false;
+      message = "Expected disable-user-extensions to be false.";
+    }
+    {
+      assertion =
+        all (e: elem e actualEnabledExtensions) expectedEnabledExtensions;
+      message = ''
+        Expected enabled-extensions to contain all of:
+          ${toString expectedEnabledExtensions}
+        But it was:
+          ${toString actualEnabledExtensions}
+      '';
+    }
+    {
+      assertion =
+        config.dconf.settings."org/gnome/shell/extensions/user-theme".name
+        == "Test";
+      message = "Expected extensions/user-theme/name to be 'Test'.";
+    }
+  ];
+
+  test.stubs.dconf = { };
+
+  nmt.script = ''
+    assertFileExists home-path/share/gnome-shell/extensions/dummy-package/test
+    assertFileExists home-path/share/gnome-shell/extensions/test-extension/test
+    assertFileExists home-path/share/gnome-shell/extensions/test-extension-uuid/test
+    assertFileExists home-path/share/themes/Test/gnome-shell/test
+  '';
+}