@wix/himalaya-cli 0.1024.0 → 0.1026.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/schema.d.ts +12 -1
  2. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/core.mjs +6 -0
  3. package/dist/cli.mjs +29 -18
  4. package/dist/preview-assets.generated.bin +0 -0
  5. package/guides/sdk-reference.json +1 -1
  6. package/package.json +2 -2
  7. package/templates/beauty-salon/dev/index.ts +1031 -240
  8. package/templates/beauty-salon/tier5-src/_shared/detail-presence.ts +51 -0
  9. package/templates/beauty-salon/tier5-src/himi-branded-lite-orders/index.ts +4 -1
  10. package/templates/beauty-salon/tier5-src/wix-spaces-collection-detail/index.ts +9 -9
  11. package/templates/beauty-salon/tier5-src/wix-spaces-create-post/index.ts +14 -4
  12. package/templates/beauty-salon/tier5-src/wix-spaces-plan-detail/index.ts +7 -9
  13. package/templates/beauty-salon/tier5-src/wix-spaces-subscription-detail/index.ts +12 -11
  14. package/templates/beauty-salon/tier5-src/wix-spaces-view-profile/index.ts +22 -12
  15. package/templates/branded-lite/config.ts +3 -1
  16. package/templates/branded-lite/dev/index.ts +3455 -813
  17. package/templates/branded-lite/tier5-src/_shared/detail-presence.ts +51 -0
  18. package/templates/branded-lite/tier5-src/himi-branded-lite-orders/index.ts +4 -1
  19. package/templates/branded-lite/tier5-src/himi-branded-lite-portfolio-project/index.ts +7 -2
  20. package/templates/branded-lite/tier5-src/himi-branded-lite-simple-detail/index.ts +4 -3
  21. package/templates/branded-lite/tier5-src/wix-spaces-chat-thread/index.ts +17 -4
  22. package/templates/branded-lite/tier5-src/wix-spaces-collection-detail/index.ts +9 -9
  23. package/templates/branded-lite/tier5-src/wix-spaces-coupon-detail/index.ts +7 -9
  24. package/templates/branded-lite/tier5-src/wix-spaces-create-post/index.ts +14 -4
  25. package/templates/branded-lite/tier5-src/wix-spaces-food-order-detail/index.ts +6 -3
  26. package/templates/branded-lite/tier5-src/wix-spaces-form-detail/index.ts +7 -9
  27. package/templates/branded-lite/tier5-src/wix-spaces-forum-post/index.ts +7 -9
  28. package/templates/branded-lite/tier5-src/wix-spaces-invoice-detail/index.ts +7 -9
  29. package/templates/branded-lite/tier5-src/wix-spaces-member-detail/index.ts +8 -8
  30. package/templates/branded-lite/tier5-src/wix-spaces-photo-albums/index.ts +8 -2
  31. package/templates/branded-lite/tier5-src/wix-spaces-plan-detail/index.ts +7 -9
  32. package/templates/branded-lite/tier5-src/wix-spaces-subscription-detail/index.ts +12 -11
  33. package/templates/branded-lite/tier5-src/wix-spaces-view-profile/index.ts +22 -12
  34. package/templates/community-club/dev/index.ts +345 -97
  35. package/templates/community-club/tier5-src/wix-spaces-create-post/index.ts +14 -4
  36. package/templates/community-club/tier5-src/wix-spaces-view-profile/index.ts +22 -12
  37. package/templates/events-meetups/dev/index.ts +345 -97
  38. package/templates/events-meetups/tier5-src/wix-spaces-create-post/index.ts +14 -4
  39. package/templates/events-meetups/tier5-src/wix-spaces-view-profile/index.ts +22 -12
  40. package/templates/gym-fitness/dev/index.ts +1031 -240
  41. package/templates/gym-fitness/tier5-src/_shared/detail-presence.ts +51 -0
  42. package/templates/gym-fitness/tier5-src/himi-branded-lite-orders/index.ts +4 -1
  43. package/templates/gym-fitness/tier5-src/wix-spaces-collection-detail/index.ts +9 -9
  44. package/templates/gym-fitness/tier5-src/wix-spaces-create-post/index.ts +14 -4
  45. package/templates/gym-fitness/tier5-src/wix-spaces-plan-detail/index.ts +7 -9
  46. package/templates/gym-fitness/tier5-src/wix-spaces-subscription-detail/index.ts +12 -11
  47. package/templates/gym-fitness/tier5-src/wix-spaces-view-profile/index.ts +22 -12
  48. package/templates/nonprofit-cause/dev/index.ts +732 -188
  49. package/templates/nonprofit-cause/tier5-src/_shared/detail-presence.ts +51 -0
  50. package/templates/nonprofit-cause/tier5-src/wix-spaces-create-post/index.ts +14 -4
  51. package/templates/nonprofit-cause/tier5-src/wix-spaces-plan-detail/index.ts +7 -9
  52. package/templates/nonprofit-cause/tier5-src/wix-spaces-subscription-detail/index.ts +12 -11
  53. package/templates/nonprofit-cause/tier5-src/wix-spaces-view-profile/index.ts +22 -12
  54. package/templates/podcast-creator/dev/index.ts +942 -321
  55. package/templates/podcast-creator/tier5-src/_shared/detail-presence.ts +51 -0
  56. package/templates/podcast-creator/tier5-src/himi-branded-lite-simple-detail/index.ts +4 -3
  57. package/templates/podcast-creator/tier5-src/wix-spaces-create-post/index.ts +14 -4
  58. package/templates/podcast-creator/tier5-src/wix-spaces-plan-detail/index.ts +7 -9
  59. package/templates/podcast-creator/tier5-src/wix-spaces-subscription-detail/index.ts +12 -11
  60. package/templates/podcast-creator/tier5-src/wix-spaces-view-profile/index.ts +22 -12
  61. package/templates/portfolio-creative/dev/index.ts +676 -169
  62. package/templates/portfolio-creative/tier5-src/_shared/detail-presence.ts +51 -0
  63. package/templates/portfolio-creative/tier5-src/himi-branded-lite-orders/index.ts +4 -1
  64. package/templates/portfolio-creative/tier5-src/himi-branded-lite-portfolio-project/index.ts +7 -2
  65. package/templates/portfolio-creative/tier5-src/wix-spaces-collection-detail/index.ts +9 -9
  66. package/templates/portfolio-creative/tier5-src/wix-spaces-create-post/index.ts +14 -4
  67. package/templates/portfolio-creative/tier5-src/wix-spaces-view-profile/index.ts +22 -12
  68. package/templates/restaurant/dev/index.ts +529 -149
  69. package/templates/restaurant/tier5-src/_shared/detail-presence.ts +51 -0
  70. package/templates/restaurant/tier5-src/wix-spaces-create-post/index.ts +14 -4
  71. package/templates/restaurant/tier5-src/wix-spaces-food-order-detail/index.ts +6 -3
  72. package/templates/restaurant/tier5-src/wix-spaces-view-profile/index.ts +22 -12
  73. package/templates/retail-store/dev/index.ts +643 -148
  74. package/templates/retail-store/tier5-src/_shared/detail-presence.ts +51 -0
  75. package/templates/retail-store/tier5-src/himi-branded-lite-orders/index.ts +4 -1
  76. package/templates/retail-store/tier5-src/wix-spaces-collection-detail/index.ts +9 -9
  77. package/templates/retail-store/tier5-src/wix-spaces-create-post/index.ts +14 -4
  78. package/templates/retail-store/tier5-src/wix-spaces-view-profile/index.ts +22 -12
  79. package/templates/yoga-studio/dev/index.ts +711 -183
  80. package/templates/yoga-studio/tier5-src/_shared/detail-presence.ts +51 -0
  81. package/templates/yoga-studio/tier5-src/wix-spaces-create-post/index.ts +14 -4
  82. package/templates/yoga-studio/tier5-src/wix-spaces-plan-detail/index.ts +7 -9
  83. package/templates/yoga-studio/tier5-src/wix-spaces-subscription-detail/index.ts +12 -11
  84. package/templates/yoga-studio/tier5-src/wix-spaces-view-profile/index.ts +22 -12
@@ -3,10 +3,15 @@
3
3
  // paint the seeded "apptestercoowner" demo identity when the session is a real
4
4
  // member (or a guest — show honesty instead).
5
5
  import { defineService } from "@himalaya/state";
6
- import { dispatch } from "@himalaya/dispatch";
6
+ import { memberAccess } from "../_shared/member-access.js";
7
7
  import { getMyData, displayName } from "@himalaya/wix-users";
8
8
 
9
- interface S { status: "loading" | "ready"; name: string; handle: string; about: string; initial: string; photo: string }
9
+ // `needsSignIn` / `noSignIn` drive the shared member gate: a guest gets the
10
+ // gate with a Sign in button and no placeholder profile. `profileHidden` stays true until
11
+ // `load` has resolved a member, so a guest never glimpses the placeholder avatar while
12
+ // the membership check is still running; `hideLoading` is false for exactly that moment,
13
+ // so the screen shows a loading line instead of going blank.
14
+ interface S { status: "loading" | "ready"; name: string; handle: string; about: string; initial: string; photo: string; needsSignIn: boolean; noSignIn: boolean; profileHidden: boolean; hideLoading: boolean }
10
15
 
11
16
  const EMPTY: S = {
12
17
  status: "loading",
@@ -15,18 +20,17 @@ const EMPTY: S = {
15
20
  about: "No info to display.",
16
21
  initial: "?",
17
22
  photo: "",
23
+ needsSignIn: false,
24
+ noSignIn: true,
25
+ profileHidden: true,
26
+ hideLoading: false,
18
27
  };
19
28
 
29
+ // The SAME member check create-post and orders use (`memberAccess`: the host's
30
+ // memberIdentity, falling back to wix-users only on hosts that predate that action), so
31
+ // one shared gate never gives two answers in one app.
20
32
  async function sessionEmail(): Promise<string> {
21
- try {
22
- const identity = await dispatch("himi.wix.memberIdentity") as unknown;
23
- const direct = identity as { email?: unknown } | null;
24
- const wrapped = identity as { payload?: { email?: unknown } } | null;
25
- const email = direct?.email ?? wrapped?.payload?.email;
26
- return typeof email === "string" ? email.trim() : "";
27
- } catch {
28
- return "";
29
- }
33
+ return (await memberAccess()).email;
30
34
  }
31
35
 
32
36
  function wixApi() {
@@ -37,7 +41,7 @@ function fromParts(name: string, handleRaw: string, about: string, photo = ""):
37
41
  const clean = name.trim();
38
42
  const handle = "@" + (handleRaw || clean || "member").replace(/\s+/g, "").replace(/^@+/, "").toLowerCase();
39
43
  const initial = (clean.split(/\s+/).filter(Boolean).map((w) => w[0]).join("").slice(0, 2) || "?").toUpperCase();
40
- return { status: "ready", name: clean || "Member", handle, about: about || "No info to display.", initial, photo };
44
+ return { status: "ready", name: clean || "Member", handle, about: about || "No info to display.", initial, photo, needsSignIn: false, noSignIn: true, profileHidden: false, hideLoading: true };
41
45
  }
42
46
 
43
47
  const svc = defineService<S, { load: (s: S) => Promise<S> }>("spacesViewProfile", {
@@ -48,11 +52,17 @@ const svc = defineService<S, { load: (s: S) => Promise<S> }>("spacesViewProfile"
48
52
  if (!email) {
49
53
  return {
50
54
  status: "ready",
55
+ // Kept for hosts without the shared member gate (the Spaces clone renders these
56
+ // fields directly); branded-lite hides the profile block behind `profileHidden`.
51
57
  name: "Sign in to view your profile",
52
58
  handle: "",
53
59
  about: "Your public profile is available after you sign in from Account.",
54
60
  initial: "?",
55
61
  photo: "",
62
+ needsSignIn: true,
63
+ noSignIn: false,
64
+ profileHidden: true,
65
+ hideLoading: true,
56
66
  };
57
67
  }
58
68
  try {
@@ -14,7 +14,7 @@ const SCREENS = runtimeSetting({
14
14
  "himi_branded_home": {
15
15
  "screenId": "himi_branded_home",
16
16
  "renderer": "sdui_intent",
17
- "title": "",
17
+ "title": "__HIMI_APP_NAME__",
18
18
  "schemaVersion": 1,
19
19
  "cacheControl": {
20
20
  "maxAge": 0,
@@ -3993,74 +3993,196 @@ const SCREENS = runtimeSetting({
3993
3993
  "alignment": "center",
3994
3994
  "children": [
3995
3995
  {
3996
- "id": "himi_view_profile.avatar",
3996
+ "id": "himi_view_profile.loading",
3997
3997
  "body": {
3998
3998
  "package": "himalaya",
3999
- "type": "ZStack",
3999
+ "type": "Text",
4000
4000
  "props": {
4001
+ "text": "Loading…",
4002
+ "style": "body",
4003
+ "color": "textSecondary"
4004
+ }
4005
+ },
4006
+ "modifiers": {
4007
+ "hiddenPath": "$service:spacesViewProfile.hideLoading"
4008
+ }
4009
+ },
4010
+ {
4011
+ "id": "",
4012
+ "body": {
4013
+ "package": "himalaya",
4014
+ "type": "VStack",
4015
+ "props": {
4016
+ "spacing": 8,
4001
4017
  "alignment": "center",
4002
4018
  "children": [
4003
4019
  {
4004
- "id": "",
4020
+ "id": "himi_view_profile.avatar",
4005
4021
  "body": {
4006
4022
  "package": "himalaya",
4007
- "type": "Icon",
4023
+ "type": "ZStack",
4008
4024
  "props": {
4009
- "name": "photo",
4010
- "size": 28,
4011
- "tint": "accentPrimary"
4025
+ "alignment": "center",
4026
+ "children": [
4027
+ {
4028
+ "id": "",
4029
+ "body": {
4030
+ "package": "himalaya",
4031
+ "type": "Icon",
4032
+ "props": {
4033
+ "name": "photo",
4034
+ "size": 28,
4035
+ "tint": "accentPrimary"
4036
+ }
4037
+ },
4038
+ "modifiers": {
4039
+ "accessibilityRole": "decorative"
4040
+ }
4041
+ }
4042
+ ]
4012
4043
  }
4013
4044
  },
4014
4045
  "modifiers": {
4015
- "accessibilityRole": "decorative"
4046
+ "width": 96,
4047
+ "height": 96,
4048
+ "background": {
4049
+ "token": "accentSurface"
4050
+ },
4051
+ "cornerRadius": 16
4052
+ }
4053
+ },
4054
+ {
4055
+ "id": "himi_view_profile.name",
4056
+ "body": {
4057
+ "package": "himalaya",
4058
+ "type": "Text",
4059
+ "props": {
4060
+ "text": "$service:spacesViewProfile.name",
4061
+ "style": "title1"
4062
+ }
4063
+ }
4064
+ },
4065
+ {
4066
+ "id": "",
4067
+ "body": {
4068
+ "package": "himalaya",
4069
+ "type": "Text",
4070
+ "props": {
4071
+ "text": "$service:spacesViewProfile.handle",
4072
+ "style": "footnote",
4073
+ "color": "textSecondary",
4074
+ "collapseWhenEmpty": true
4075
+ }
4076
+ }
4077
+ },
4078
+ {
4079
+ "id": "",
4080
+ "body": {
4081
+ "package": "himalaya",
4082
+ "type": "Text",
4083
+ "props": {
4084
+ "text": "$service:spacesViewProfile.about",
4085
+ "style": "body",
4086
+ "collapseWhenEmpty": true
4087
+ }
4016
4088
  }
4017
4089
  }
4018
4090
  ]
4019
4091
  }
4020
4092
  },
4021
4093
  "modifiers": {
4022
- "width": 96,
4023
- "height": 96,
4024
- "background": {
4025
- "token": "accentSurface"
4026
- },
4027
- "cornerRadius": 16
4028
- }
4029
- },
4030
- {
4031
- "id": "himi_view_profile.name",
4032
- "body": {
4033
- "package": "himalaya",
4034
- "type": "Text",
4035
- "props": {
4036
- "text": "$service:spacesViewProfile.name",
4037
- "style": "title1"
4038
- }
4094
+ "hiddenPath": "$service:spacesViewProfile.profileHidden"
4039
4095
  }
4040
4096
  },
4041
4097
  {
4042
- "id": "",
4098
+ "id": "himi_view_profile.member_gate",
4043
4099
  "body": {
4044
4100
  "package": "himalaya",
4045
- "type": "Text",
4046
- "props": {
4047
- "text": "$service:spacesViewProfile.handle",
4048
- "style": "footnote",
4049
- "color": "textSecondary",
4050
- "collapseWhenEmpty": true
4051
- }
4052
- }
4053
- },
4054
- {
4055
- "id": "",
4056
- "body": {
4057
- "package": "himalaya",
4058
- "type": "Text",
4101
+ "type": "VStack",
4059
4102
  "props": {
4060
- "text": "$service:spacesViewProfile.about",
4061
- "style": "body",
4062
- "collapseWhenEmpty": true
4103
+ "spacing": 10,
4104
+ "alignment": "center",
4105
+ "children": [
4106
+ {
4107
+ "id": "",
4108
+ "body": {
4109
+ "package": "himalaya",
4110
+ "type": "Icon",
4111
+ "props": {
4112
+ "name": "person",
4113
+ "size": 28,
4114
+ "tint": "accentPrimary"
4115
+ }
4116
+ },
4117
+ "modifiers": {
4118
+ "accessibilityRole": "decorative"
4119
+ }
4120
+ },
4121
+ {
4122
+ "id": "himi_view_profile.member_gate_title",
4123
+ "body": {
4124
+ "package": "himalaya",
4125
+ "type": "Text",
4126
+ "props": {
4127
+ "text": "Sign in to view your profile",
4128
+ "style": "bodyEmphasis"
4129
+ }
4130
+ }
4131
+ },
4132
+ {
4133
+ "id": "",
4134
+ "body": {
4135
+ "package": "himalaya",
4136
+ "type": "Text",
4137
+ "props": {
4138
+ "text": "Your public profile appears here after you sign in.",
4139
+ "style": "footnote",
4140
+ "color": "textSecondary"
4141
+ }
4142
+ }
4143
+ },
4144
+ {
4145
+ "id": "himi_view_profile.member_gate_signin",
4146
+ "body": {
4147
+ "package": "himalaya",
4148
+ "type": "Button",
4149
+ "props": {
4150
+ "title": "Sign in",
4151
+ "style": "primary",
4152
+ "fullWidth": true,
4153
+ "action": {
4154
+ "actionId": "himi.open",
4155
+ "params": {
4156
+ "screenId": "himi_account",
4157
+ "intent": "profile",
4158
+ "returnScreenId": "himi_view_profile"
4159
+ }
4160
+ }
4161
+ }
4162
+ }
4163
+ }
4164
+ ]
4063
4165
  }
4166
+ },
4167
+ "modifiers": {
4168
+ "background": {
4169
+ "token": "surfaceElevated"
4170
+ },
4171
+ "border": {
4172
+ "width": 1,
4173
+ "color": {
4174
+ "token": "border"
4175
+ }
4176
+ },
4177
+ "cornerRadius": 16,
4178
+ "padding": {
4179
+ "leading": 16,
4180
+ "trailing": 16,
4181
+ "top": 20,
4182
+ "bottom": 20
4183
+ },
4184
+ "fillMaxWidth": true,
4185
+ "hiddenPath": "$service:spacesViewProfile.noSignIn"
4064
4186
  }
4065
4187
  }
4066
4188
  ]
@@ -4682,16 +4804,31 @@ const SCREENS = runtimeSetting({
4682
4804
  }
4683
4805
  },
4684
4806
  {
4685
- "id": "himi_feed.signin_card",
4807
+ "id": "himi_feed.member_gate",
4686
4808
  "body": {
4687
4809
  "package": "himalaya",
4688
4810
  "type": "VStack",
4689
4811
  "props": {
4690
- "spacing": 8,
4691
- "alignment": "leading",
4812
+ "spacing": 10,
4813
+ "alignment": "center",
4692
4814
  "children": [
4693
4815
  {
4694
4816
  "id": "",
4817
+ "body": {
4818
+ "package": "himalaya",
4819
+ "type": "Icon",
4820
+ "props": {
4821
+ "name": "person",
4822
+ "size": 28,
4823
+ "tint": "accentPrimary"
4824
+ }
4825
+ },
4826
+ "modifiers": {
4827
+ "accessibilityRole": "decorative"
4828
+ }
4829
+ },
4830
+ {
4831
+ "id": "himi_feed.member_gate_title",
4695
4832
  "body": {
4696
4833
  "package": "himalaya",
4697
4834
  "type": "Text",
@@ -4714,7 +4851,7 @@ const SCREENS = runtimeSetting({
4714
4851
  }
4715
4852
  },
4716
4853
  {
4717
- "id": "himi_feed.signin",
4854
+ "id": "himi_feed.member_gate_signin",
4718
4855
  "body": {
4719
4856
  "package": "himalaya",
4720
4857
  "type": "Button",
@@ -4750,9 +4887,10 @@ const SCREENS = runtimeSetting({
4750
4887
  "padding": {
4751
4888
  "leading": 16,
4752
4889
  "trailing": 16,
4753
- "top": 16,
4754
- "bottom": 16
4890
+ "top": 20,
4891
+ "bottom": 20
4755
4892
  },
4893
+ "fillMaxWidth": true,
4756
4894
  "hiddenPath": "$service:himiBrandedFeed.noSignIn"
4757
4895
  }
4758
4896
  },
@@ -5254,65 +5392,172 @@ const SCREENS = runtimeSetting({
5254
5392
  }
5255
5393
  },
5256
5394
  {
5257
- "id": "",
5258
- "body": {
5259
- "package": "himalaya",
5260
- "type": "Text",
5261
- "props": {
5262
- "text": "$service:spacesCreatePost.hint",
5263
- "style": "footnote",
5264
- "color": "textSecondary",
5265
- "collapseWhenEmpty": true
5266
- }
5267
- }
5268
- },
5269
- {
5270
- "id": "himi_create_post.title_field",
5395
+ "id": "himi_create_post.member_gate",
5271
5396
  "body": {
5272
5397
  "package": "himalaya",
5273
- "type": "TextField",
5398
+ "type": "VStack",
5274
5399
  "props": {
5275
- "placeholder": "Title",
5276
- "value": "",
5277
- "submitLabel": "next",
5278
- "onSubmit": {
5279
- "actionId": "tier5.spacesCreatePost.focusBody"
5280
- },
5281
- "onChange": {
5282
- "actionId": "tier5.spacesCreatePost.setTitle"
5283
- }
5400
+ "spacing": 10,
5401
+ "alignment": "center",
5402
+ "children": [
5403
+ {
5404
+ "id": "",
5405
+ "body": {
5406
+ "package": "himalaya",
5407
+ "type": "Icon",
5408
+ "props": {
5409
+ "name": "person",
5410
+ "size": 28,
5411
+ "tint": "accentPrimary"
5412
+ }
5413
+ },
5414
+ "modifiers": {
5415
+ "accessibilityRole": "decorative"
5416
+ }
5417
+ },
5418
+ {
5419
+ "id": "himi_create_post.member_gate_title",
5420
+ "body": {
5421
+ "package": "himalaya",
5422
+ "type": "Text",
5423
+ "props": {
5424
+ "text": "Sign in to post",
5425
+ "style": "bodyEmphasis"
5426
+ }
5427
+ }
5428
+ },
5429
+ {
5430
+ "id": "",
5431
+ "body": {
5432
+ "package": "himalaya",
5433
+ "type": "Text",
5434
+ "props": {
5435
+ "text": "Sign in to share something with the community.",
5436
+ "style": "footnote",
5437
+ "color": "textSecondary"
5438
+ }
5439
+ }
5440
+ },
5441
+ {
5442
+ "id": "himi_create_post.member_gate_signin",
5443
+ "body": {
5444
+ "package": "himalaya",
5445
+ "type": "Button",
5446
+ "props": {
5447
+ "title": "Sign in",
5448
+ "style": "primary",
5449
+ "fullWidth": true,
5450
+ "action": {
5451
+ "actionId": "himi.open",
5452
+ "params": {
5453
+ "screenId": "himi_account",
5454
+ "intent": "post",
5455
+ "returnScreenId": "himi_create_post"
5456
+ }
5457
+ }
5458
+ }
5459
+ }
5460
+ }
5461
+ ]
5284
5462
  }
5285
- }
5286
- },
5287
- {
5288
- "id": "himi_create_post.body_field",
5289
- "body": {
5290
- "package": "himalaya",
5291
- "type": "TextField",
5292
- "props": {
5293
- "placeholder": "Write something…",
5294
- "value": "",
5295
- "multilineMaxLines": 6,
5296
- "focusPath": "$service:spacesCreatePost.bodyFocus",
5297
- "onChange": {
5298
- "actionId": "tier5.spacesCreatePost.setBody"
5463
+ },
5464
+ "modifiers": {
5465
+ "background": {
5466
+ "token": "surfaceElevated"
5467
+ },
5468
+ "border": {
5469
+ "width": 1,
5470
+ "color": {
5471
+ "token": "border"
5299
5472
  }
5300
- }
5473
+ },
5474
+ "cornerRadius": 16,
5475
+ "padding": {
5476
+ "leading": 16,
5477
+ "trailing": 16,
5478
+ "top": 20,
5479
+ "bottom": 20
5480
+ },
5481
+ "fillMaxWidth": true,
5482
+ "hiddenPath": "$service:spacesCreatePost.noSignIn"
5301
5483
  }
5302
5484
  },
5303
5485
  {
5304
- "id": "himi_create_post.submit",
5486
+ "id": "",
5305
5487
  "body": {
5306
5488
  "package": "himalaya",
5307
- "type": "Button",
5489
+ "type": "VStack",
5308
5490
  "props": {
5309
- "title": "Post",
5310
- "style": "primary",
5311
- "fullWidth": true,
5312
- "action": {
5313
- "actionId": "tier5.spacesCreatePost.submit"
5314
- }
5491
+ "spacing": 16,
5492
+ "alignment": "leading",
5493
+ "children": [
5494
+ {
5495
+ "id": "",
5496
+ "body": {
5497
+ "package": "himalaya",
5498
+ "type": "Text",
5499
+ "props": {
5500
+ "text": "$service:spacesCreatePost.hint",
5501
+ "style": "footnote",
5502
+ "color": "textSecondary",
5503
+ "collapseWhenEmpty": true
5504
+ }
5505
+ }
5506
+ },
5507
+ {
5508
+ "id": "himi_create_post.title_field",
5509
+ "body": {
5510
+ "package": "himalaya",
5511
+ "type": "TextField",
5512
+ "props": {
5513
+ "placeholder": "Title",
5514
+ "value": "",
5515
+ "submitLabel": "next",
5516
+ "onSubmit": {
5517
+ "actionId": "tier5.spacesCreatePost.focusBody"
5518
+ },
5519
+ "onChange": {
5520
+ "actionId": "tier5.spacesCreatePost.setTitle"
5521
+ }
5522
+ }
5523
+ }
5524
+ },
5525
+ {
5526
+ "id": "himi_create_post.body_field",
5527
+ "body": {
5528
+ "package": "himalaya",
5529
+ "type": "TextField",
5530
+ "props": {
5531
+ "placeholder": "Write something…",
5532
+ "value": "",
5533
+ "multilineMaxLines": 6,
5534
+ "focusPath": "$service:spacesCreatePost.bodyFocus",
5535
+ "onChange": {
5536
+ "actionId": "tier5.spacesCreatePost.setBody"
5537
+ }
5538
+ }
5539
+ }
5540
+ },
5541
+ {
5542
+ "id": "himi_create_post.submit",
5543
+ "body": {
5544
+ "package": "himalaya",
5545
+ "type": "Button",
5546
+ "props": {
5547
+ "title": "Post",
5548
+ "style": "primary",
5549
+ "fullWidth": true,
5550
+ "action": {
5551
+ "actionId": "tier5.spacesCreatePost.submit"
5552
+ }
5553
+ }
5554
+ }
5555
+ }
5556
+ ]
5315
5557
  }
5558
+ },
5559
+ "modifiers": {
5560
+ "hiddenPath": "$service:spacesCreatePost.hideForm"
5316
5561
  }
5317
5562
  }
5318
5563
  ]
@@ -5327,6 +5572,9 @@ const SCREENS = runtimeSetting({
5327
5572
  "trailing": 16,
5328
5573
  "top": 18,
5329
5574
  "bottom": 28
5575
+ },
5576
+ "onAppear": {
5577
+ "actionId": "tier5.spacesCreatePost.load"
5330
5578
  }
5331
5579
  }
5332
5580
  },
@@ -4,6 +4,7 @@
4
4
  // create endpoint in the SDK; blog is the available authored-content write.)
5
5
  import { defineService } from "@himalaya/state";
6
6
  import { createDraftPost, publishDraftPost } from "@himalaya/wix-blog";
7
+ import { memberAccess } from "../_shared/member-access.js";
7
8
 
8
9
  // `bodyFocus` is a focus TOKEN, not a flag: the body field focuses whenever this
9
10
  // becomes a new non-empty value (`TextFieldProps.focus_path`). A monotonic counter
@@ -14,7 +15,11 @@ import { createDraftPost, publishDraftPost } from "@himalaya/wix-blog";
14
15
  // the new state), so there is no way to observe an intermediate value while `submit`
15
16
  // is awaiting the network. A type that promises a state nobody can ever see reads as
16
17
  // an in-flight guard that exists — see `posting` below for the one that works.
17
- interface S { status: "ready"; title: string; body: string; hint: string; bodyFocus: string }
18
+ // `needsSignIn` / `noSignIn` / `hideForm` drive the member gate: a guest sees the
19
+ // gate and NO form, a member sees the form, and until `load` has asked the
20
+ // host who is signed in, neither shows — so a guest can never start typing a post
21
+ // that would only fail on submit.
22
+ interface S { status: "ready"; title: string; body: string; hint: string; bodyFocus: string; needsSignIn: boolean; noSignIn: boolean; hideForm: boolean }
18
23
 
19
24
  // The real double-press guard, module-local because it must live OUTSIDE published
20
25
  // state for the reason above. One worker instance owns one composer, so a module flag
@@ -33,15 +38,20 @@ function val(arg?: unknown): string {
33
38
  return typeof arg === "string" ? arg : ((arg as { value?: string; text?: string })?.value ?? (arg as { text?: string })?.text ?? "");
34
39
  }
35
40
 
36
- const svc = defineService<S, { setTitle: (s: S, a?: unknown) => S; setBody: (s: S, a?: unknown) => S; focusBody: (s: S) => S; submit: (s: S) => Promise<S> }>("spacesCreatePost", {
37
- state: { status: "ready", title: "", body: "", hint: "Share something with the community.", bodyFocus: "" },
41
+ const svc = defineService<S, { load: (s: S) => Promise<S>; setTitle: (s: S, a?: unknown) => S; setBody: (s: S, a?: unknown) => S; focusBody: (s: S) => S; submit: (s: S) => Promise<S> }>("spacesCreatePost", {
42
+ state: { status: "ready", title: "", body: "", hint: "Share something with the community.", bodyFocus: "", needsSignIn: false, noSignIn: true, hideForm: true },
38
43
  actions: {
44
+ load: async (s) => {
45
+ const { email } = await memberAccess();
46
+ return { ...s, needsSignIn: !email, noSignIn: !!email, hideForm: !email };
47
+ },
39
48
  setTitle: (s, a) => ({ ...s, title: val(a) }),
40
49
  setBody: (s, a) => ({ ...s, body: val(a) }),
41
50
  // Title's Next key lands here. The keyboard stays up (that is the "next"
42
51
  // contract) and the body field takes focus.
43
52
  focusBody: (s) => ({ ...s, bodyFocus: bumpToken(s.bodyFocus) }),
44
53
  submit: async (s) => {
54
+ if (s.needsSignIn) return { ...s, hint: "Sign in to post." };
45
55
  const title = s.title.trim();
46
56
  if (!title) return { ...s, hint: "Please enter a title." };
47
57
  // A second press while the first is still in flight is a NO-OP, not a second
@@ -63,7 +73,7 @@ const svc = defineService<S, { setTitle: (s: S, a?: unknown) => S; setBody: (s:
63
73
  // steals focus, so those transitions must clear it (see himi-store-account's
64
74
  // NO_FOCUS on its mode switches). Same token, opposite fix; the deciding
65
75
  // question is always whether the field stays mounted.
66
- return { status: "ready", title: "", body: "", bodyFocus: s.bodyFocus, hint: `Posted “${title}” ✓ — it now appears in Blog.` };
76
+ return { ...s, status: "ready", title: "", body: "", bodyFocus: s.bodyFocus, hint: `Posted “${title}” ✓ — it now appears in Blog.` };
67
77
  } catch {
68
78
  return { ...s, status: "ready", hint: "Couldn't post — check your connection and try again." };
69
79
  } finally {