@flow-industries/id 0.23.3 → 0.24.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.
@@ -74,17 +74,26 @@ export function createProfileButton(options) {
74
74
  fallback.rel = "noopener noreferrer";
75
75
  fallback.textContent = "Open Flow ID";
76
76
  fallback.hidden = true;
77
- let lastUsername;
77
+ let lastUser = null;
78
+ let imageUrl = null;
79
+ avatar.textContent = "F";
78
80
  let profileRequest = null;
79
- function update() {
81
+ function update(refresh = false) {
80
82
  const user = flow.getState().user;
81
83
  name.textContent = user?.username ?? "Flow ID";
82
84
  button.setAttribute("aria-label", `Open ${user?.username ?? "your"} Flow ID account in a new tab`);
83
- if (user?.username === lastUsername)
85
+ const identityChanged = user?.id !== lastUser?.id ||
86
+ user?.username !== lastUser?.username ||
87
+ user?.isGuest !== lastUser?.isGuest;
88
+ if (identityChanged) {
89
+ profileRequest?.abort();
90
+ profileRequest = null;
91
+ imageUrl = null;
92
+ avatar.textContent = user?.username.slice(0, 1).toUpperCase() ?? "F";
93
+ lastUser = user;
94
+ }
95
+ else if (!refresh || profileRequest)
84
96
  return;
85
- lastUsername = user?.username;
86
- profileRequest?.abort();
87
- avatar.textContent = user?.username.slice(0, 1).toUpperCase() ?? "F";
88
97
  if (!user || user.isGuest)
89
98
  return;
90
99
  const controller = new AbortController();
@@ -98,7 +107,14 @@ export function createProfileButton(options) {
98
107
  return;
99
108
  // SAFETY: Flow ID owns the public profile endpoint and its optional image field.
100
109
  const profile = (await response.json());
101
- if (controller.signal.aborted || !profile.image)
110
+ if (controller.signal.aborted)
111
+ return;
112
+ if (profile.image === null) {
113
+ imageUrl = null;
114
+ avatar.textContent = user.username.slice(0, 1).toUpperCase();
115
+ return;
116
+ }
117
+ if (!profile.image || profile.image === imageUrl)
102
118
  return;
103
119
  const image = document.createElement("img");
104
120
  image.src = profile.image;
@@ -108,9 +124,17 @@ export function createProfileButton(options) {
108
124
  height: "100%",
109
125
  objectFit: "cover",
110
126
  });
127
+ await image.decode();
128
+ if (controller.signal.aborted)
129
+ return;
130
+ imageUrl = profile.image;
111
131
  avatar.replaceChildren(image);
112
132
  })
113
- .catch(() => { });
133
+ .catch(() => { })
134
+ .finally(() => {
135
+ if (profileRequest === controller)
136
+ profileRequest = null;
137
+ });
114
138
  }
115
139
  button.addEventListener("click", () => {
116
140
  const opened = flow.openAccount();
@@ -120,8 +144,7 @@ export function createProfileButton(options) {
120
144
  fallback.hidden = opened;
121
145
  });
122
146
  function refresh() {
123
- lastUsername = undefined;
124
- update();
147
+ update(true);
125
148
  }
126
149
  window.addEventListener("focus", refresh);
127
150
  function setTheme(theme) {
@@ -130,7 +153,7 @@ export function createProfileButton(options) {
130
153
  setTheme(options.theme ?? "light dark");
131
154
  container.append(button, notice, fallback);
132
155
  update();
133
- const unsubscribe = flow.subscribe(update);
156
+ const unsubscribe = flow.subscribe(() => update());
134
157
  return {
135
158
  setTheme,
136
159
  destroy() {
@@ -81,12 +81,13 @@ export type CosmeticMaterial = (typeof COSMETIC_MATERIALS)[number];
81
81
  * that shrank whenever a slot emptied would churn the contract every time
82
82
  * content lands.
83
83
  */
84
- export type CosmeticSlot = "hat" | "face" | "top" | "bottom" | "feet";
84
+ export type CosmeticSlot = "hat" | "hair" | "face" | "top" | "bottom" | "feet";
85
85
  /** One item in the catalog. */
86
86
  export interface CosmeticItem {
87
87
  id: string;
88
88
  slot: CosmeticSlot;
89
89
  name: string;
90
+ description?: string;
90
91
  /**
91
92
  * ALWAYS plural, even for a single region, and the only shape there is: TF2
92
93
  * carried a singular `equip_region` key alongside the plural one, and items
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flow-industries/id",
3
- "version": "0.23.3",
3
+ "version": "0.24.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -140,7 +140,7 @@
140
140
  ],
141
141
  "dependencies": {
142
142
  "@aws-sdk/client-s3": "^3.1073.0",
143
- "@flow-industries/ui": "^0.21.1",
143
+ "@flow-industries/ui": "^0.23.3",
144
144
  "@hono/otel": "^1.1.2",
145
145
  "@openobserve/browser-logs": "^0.3.1",
146
146
  "@openobserve/browser-rum": "^0.3.1",