| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117 |
- using Inversion;
- using Spry;
- using Spry.Authentication;
- using Statum;
- using ClankerSaddle.Services;
- namespace ClankerSaddle {
- /**
- * The shipped Spry change-own-password action with this app's
- * post-change destination (the profile page) - the form fields and
- * verification (current/new/confirm) are Spry's.
- */
- public class ChangeMyPasswordAction : Spry.Actions.ChangePasswordAction {
- protected override string login_uri { get { return "/login"; } }
- }
- /**
- * The public shape of the DEVICE-scope `ui` slot (goal 41 task 2):
- * the theme + colour mode the client applies to
- * `document.documentElement` (`data-theme`/`data-mode`). Plain
- * strings on purpose — the slot is cached client-side across
- * sessions, so it must stay readable by old frames.
- */
- public class UiSlotPublic : Object {
- /** The Spry theme slug ("" = the core default). */
- public string theme { get; set; default = ""; }
- /** auto | light | dark. */
- public string mode { get; set; default = MODE_DEFAULT; }
- }
- /**
- * Saves the account's appearance choice (goal 41 task 2): the
- * form on /profile posts `theme` (the Spry core default "" or one
- * of the seven shipped theme slugs) and `mode`
- * (auto|light|dark). The choice is persisted per user in
- * `user_preferences` AND written to the DEVICE-scope `ui` slot —
- * the client's theme hook reads that slot on init and after every
- * statum render, so the new look applies immediately and survives
- * sessions on this device.
- */
- public class SetThemeAction : StatumAction {
- /** The seven optional themes shipped with Spry 0.3. */
- public const string[] THEME_SLUGS = {
- "airy", "dwarf", "formal", "glassy", "h2o", "living-sky", "modern"
- };
- /** The three colour modes. */
- public const string[] MODES = { "auto", "light", "dark" };
- /** The DEVICE-scope slot type the client theme hook reads. */
- public const string UI_SLOT_TYPE = "ui";
- private UserService users = inject<UserService>();
- private UserPreferenceService preferences = inject<UserPreferenceService>();
- /** True when `slug` names a theme the client may apply. */
- public static bool is_valid_theme(string slug) {
- if (slug == "" || slug == "core") {
- return true;
- }
- foreach (string candidate in THEME_SLUGS) {
- if (candidate == slug) {
- return true;
- }
- }
- return false;
- }
- /** True when `mode` is a colour mode the client may apply. */
- public static bool is_valid_mode(string mode) {
- foreach (string candidate in MODES) {
- if (candidate == mode) {
- return true;
- }
- }
- return false;
- }
- public override async DirectiveBuilder handle() throws GLib.Error {
- var guard = yield SpryAuth.require_login(directives(), request, users);
- if (guard != null) {
- return (!)guard;
- }
- string theme = Forms.field(request, "theme");
- string mode = Forms.field(request, "mode");
- if (mode.length == 0) {
- mode = MODE_DEFAULT;
- }
- if (!is_valid_theme(theme)) {
- return directives().notify("error",
- "Unknown theme '" + theme + "' - pick one of the themes offered below");
- }
- if (!is_valid_mode(mode)) {
- return directives().notify("error",
- "Unknown colour mode '" + mode + "' - use auto, light or dark");
- }
- var me = SpryAuth.current(request);
- try {
- yield preferences.set_theme(me.user_id, theme);
- yield preferences.set_mode(me.user_id, mode);
- var slot = new UiSlotPublic() { theme = theme, mode = mode };
- return directives()
- .set_typed<UiSlotPublic>(UI_SLOT_TYPE, Statum.Scope.DEVICE, slot)
- .notify("success", "Appearance saved — the new theme applies immediately");
- } catch (Error e) {
- return directives().notify("error",
- @"Could not save the appearance choice: $(e.message)");
- }
- }
- }
- }
|