Actions.vala 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. using Statum;
  2. using Invercargill;
  3. using Invercargill.DataStructures;
  4. namespace Example {
  5. /**
  6. * `POST /_statum/action/{guid}` — logs in: reads the submitted name, builds a
  7. * session `auth` slot from typed models (public + private), embedding a
  8. * `logout` action reference, and redirects home.
  9. */
  10. public class LoginAction : StatumAction {
  11. public override async DirectiveBuilder handle() throws GLib.Error {
  12. var name = "guest";
  13. if (request.form != null) {
  14. var submitted = request.form.get_field("name");
  15. if (submitted != null && ((!)submitted).length > 0) {
  16. name = (!)submitted;
  17. }
  18. }
  19. var is_admin = name == "admin";
  20. var pub = new AuthPublic();
  21. pub.name = name;
  22. pub.role = is_admin ? "admin" : "member";
  23. pub.logout = action_registry.author<LogoutAction>();
  24. pub.update_announcement = action_registry.author<UpdateAnnouncementAction>();
  25. var priv = new AuthPrivate();
  26. priv.is_admin = is_admin;
  27. return directives()
  28. .set_private_typed<AuthPublic, AuthPrivate>("auth", Scope.SESSION, pub, priv)
  29. .navigate("/");
  30. }
  31. }
  32. /**
  33. * `POST /_statum/action/{guid}` — logs out: clears the held `auth` slot and
  34. * redirects home.
  35. */
  36. public class LogoutAction : StatumAction {
  37. public override async DirectiveBuilder handle() throws GLib.Error {
  38. HeldSlot auth;
  39. if (request.held.try_get("auth", out auth)) {
  40. yield state_service.clear_slot(auth.key);
  41. return directives().clear(auth.key).navigate("/");
  42. }
  43. return directives().navigate("/");
  44. }
  45. }
  46. /**
  47. * `POST /_statum/action/{guid}` — bumps the counter by the typed private
  48. * `delta`, using the typed {@link DirectiveBuilder.update_held} so the
  49. * `bump` action reference is preserved automatically across the update.
  50. *
  51. * Actions are instantiated by GType (no generic type argument), so
  52. * `private_type` is overridden to name the private model — otherwise
  53. * `typeof(CounterPrivate)` erodes to `void` and {@link request_private}
  54. * yields `null`.
  55. */
  56. public class BumpCounterAction : TypedStatumAction<CounterPrivate> {
  57. protected override Type private_type { get { return typeof(CounterPrivate); } }
  58. public override async DirectiveBuilder handle() throws GLib.Error {
  59. var delta = request_private != null ? ((!)request_private).delta : 1;
  60. return directives().update_held<CounterPublic>("counter", c => c.value += delta);
  61. }
  62. }
  63. /**
  64. * `POST /_statum/action/{guid}` — updates the global announcement from the
  65. * dashboard. Reads the `text` form field, updates the global slot (which
  66. * pushes to all subscribed clients via SSE), and returns to the dashboard.
  67. */
  68. public class UpdateAnnouncementAction : StatumAction {
  69. public override async DirectiveBuilder handle() throws GLib.Error {
  70. var text = "Updated";
  71. if (request.form != null) {
  72. var submitted = request.form.get_field("text");
  73. if (submitted != null && ((!)submitted).length > 0) {
  74. text = (!)submitted;
  75. }
  76. }
  77. yield state_service.update_global_typed<AnnouncementPublic>("announcement", a => {
  78. a.text = text;
  79. });
  80. yield topic_registry.trigger("announcement:latest");
  81. return directives().notify("info", "Announcement updated").navigate("/dashboard");
  82. }
  83. }
  84. }