|
35 | 35 |
|
36 | 36 | export let myState: MyMessageState |
37 | 37 | export let myInfo: Readable<UserInfo> |
38 | | - export let channelInfo: ChannelInfoEx |
| 38 | + export let channelInfo: Readable<ChannelInfoEx> |
39 | 39 |
|
40 | 40 | const toastStore = getToastStore() |
41 | 41 | const modalStore = getModalStore() |
42 | 42 | const members: Writable<DisplayUserInfoEx[]> = writable([]) |
43 | 43 | const myID = $myInfo.id.toText() |
44 | | - const { canister, id } = channelInfo |
| 44 | + const { canister, id } = $channelInfo |
45 | 45 |
|
46 | | - let mute = channelInfo.my_setting.mute |
| 46 | + let mute = $channelInfo.my_setting.mute |
47 | 47 | let isManager = false |
48 | | - let kekStatus = 0 |
49 | | -
|
50 | | - $: { |
51 | | - if (channelInfo.my_setting.ecdh_remote.length > 0) { |
52 | | - kekStatus = 1 // should accept the key |
53 | | - } else if (channelInfo.my_setting.ecdh_pub.length > 0) { |
54 | | - kekStatus = 2 // should wait for the key |
55 | | - } else if (channelInfo._kek && !channelInfo._invalidKEK) { |
56 | | - kekStatus = 3 // key exists |
57 | | - } else { |
58 | | - kekStatus = 0 |
59 | | - } |
60 | | - } |
| 48 | + let validKEK = true |
61 | 49 |
|
62 | 50 | $: hasExchangeKeys = |
63 | | - channelInfo.ecdh_request.filter( |
| 51 | + $channelInfo.ecdh_request.filter( |
64 | 52 | (r) => r[0].toText() !== myID && r[1][1].length === 0 |
65 | 53 | ).length > 0 |
66 | 54 |
|
67 | 55 | async function loadMembers() { |
68 | | - const res = await myState.channelMembers(channelInfo, $myInfo) |
69 | | - isManager = channelInfo._managers.includes(myID) |
| 56 | + const res = await myState.channelMembers($channelInfo, $myInfo) |
| 57 | + isManager = $channelInfo._managers.includes(myID) |
70 | 58 | members.set(res) |
71 | 59 | } |
72 | 60 |
|
|
76 | 64 | component: { |
77 | 65 | ref: ChannelEditModal, |
78 | 66 | props: { |
79 | | - channel: channelInfo, |
| 67 | + channel: $channelInfo, |
80 | 68 | onSave: (input: UpdateChannelInput) => { |
81 | 69 | return toastRun(async (signal: AbortSignal) => { |
82 | 70 | const api = myState.api.channelAPI(canister) |
83 | 71 | await api.update_channel(input) |
84 | | - channelInfo = await myState.refreshChannel(channelInfo) |
| 72 | + await myState.refreshChannel($channelInfo) |
85 | 73 | }, toastStore).finally() |
86 | 74 | } |
87 | 75 | } |
|
115 | 103 | mute: [mute], |
116 | 104 | last_read: [] |
117 | 105 | }) |
118 | | - channelInfo = await myState.refreshChannel(channelInfo) |
| 106 | + await myState.refreshChannel($channelInfo) |
119 | 107 | }, toastStore).finally(() => { |
120 | 108 | muteSubmitting = false |
121 | 109 | }) |
|
126 | 114 | myECDHSubmitting = true |
127 | 115 |
|
128 | 116 | toastRun(async (signal: AbortSignal) => { |
129 | | - if (channelInfo.my_setting.ecdh_remote.length === 1) { |
| 117 | + if ($channelInfo.my_setting.ecdh_remote.length === 1) { |
130 | 118 | try { |
131 | | - await myState.acceptKEK(channelInfo) |
132 | | - await myState.decryptChannelDEK(channelInfo) |
| 119 | + await myState.acceptKEK($channelInfo) |
| 120 | + await myState.decryptChannelDEK($channelInfo) |
| 121 | + validKEK = true |
133 | 122 | } catch (err: any) { |
| 123 | + validKEK = false |
134 | 124 | toastStore.trigger({ |
135 | 125 | timeout: 10000, |
136 | 126 | hideDismiss: false, |
137 | 127 | background: 'variant-soft-error', |
138 | 128 | message: `Failed to receive the key. A new key has been requested.\n<br />Error: ${errMessage(err)}` |
139 | 129 | }) |
140 | | - const my_setting = { ...channelInfo.my_setting } |
| 130 | + const my_setting = { ...$channelInfo.my_setting } |
141 | 131 | my_setting.ecdh_remote = [] |
142 | 132 | my_setting.ecdh_pub = [] |
143 | | - await myState.requestKEK({ ...channelInfo, my_setting }) |
| 133 | + await myState.requestKEK({ ...$channelInfo, my_setting }) |
144 | 134 | } |
145 | 135 | } else { |
146 | | - await myState.requestKEK(channelInfo) |
| 136 | + await myState.requestKEK($channelInfo) |
147 | 137 | } |
148 | 138 |
|
149 | | - channelInfo = await myState.refreshChannel(channelInfo) |
| 139 | + await myState.refreshChannel($channelInfo) |
150 | 140 | }, toastStore).finally(() => { |
151 | 141 | myECDHSubmitting = false |
152 | 142 | }) |
|
155 | 145 | let leavingWord = '' |
156 | 146 | let myLeavingSubmitting = false |
157 | 147 | function onClickMyLeaving() { |
158 | | - if (leavingWord.trim() != channelInfo.name) { |
| 148 | + if (leavingWord.trim() != $channelInfo.name) { |
159 | 149 | return |
160 | 150 | } |
161 | 151 |
|
|
176 | 166 | adminExchangeKeysSubmitting = true |
177 | 167 | toastRun(async (signal: AbortSignal) => { |
178 | 168 | // fetch the latest ECDH request |
179 | | - channelInfo = await myState.refreshChannel(channelInfo) |
180 | | - await myState.adminExchangeKEK(channelInfo) |
181 | | - channelInfo = await myState.refreshChannel(channelInfo) |
| 169 | + await myState.refreshChannel($channelInfo) |
| 170 | + await myState.adminExchangeKEK($channelInfo) |
| 171 | + await myState.refreshChannel($channelInfo) |
182 | 172 | await loadMembers() |
183 | 173 | }, toastStore).finally(() => { |
184 | 174 | adminExchangeKeysSubmitting = false |
|
187 | 177 |
|
188 | 178 | let adminAddManagersSubmitting = false |
189 | 179 | function onClickAdminAddManagers() { |
190 | | - const existsMembers = channelInfo.members.map((m) => m.toText()) |
| 180 | + const existsMembers = $channelInfo.members.map((m) => m.toText()) |
191 | 181 | modalStore.trigger({ |
192 | 182 | type: 'component', |
193 | 183 | component: { |
194 | 184 | ref: UserSelectModal, |
195 | 185 | props: { |
196 | 186 | isAddManager: true, |
197 | | - existsManagers: channelInfo._managers, |
| 187 | + existsManagers: $channelInfo._managers, |
198 | 188 | existsMembers, |
199 | 189 | myState: myState, |
200 | 190 | onSave: (members: Array<[Principal, Uint8Array | null]>) => { |
|
207 | 197 | members.length === 1 && |
208 | 198 | existsMembers.includes(member[0].toText()) |
209 | 199 | ) { |
210 | | - await myState.adminAddManager(channelInfo, member[0]) |
| 200 | + await myState.adminAddManager($channelInfo, member[0]) |
211 | 201 | } else { |
212 | | - await myState.adminAddMembers(channelInfo, 'Manager', members) |
| 202 | + await myState.adminAddMembers($channelInfo, 'Manager', members) |
213 | 203 | } |
214 | | - channelInfo = await myState.refreshChannel(channelInfo) |
| 204 | + await myState.refreshChannel($channelInfo) |
215 | 205 | await loadMembers() |
216 | 206 | }, toastStore).finally(() => { |
217 | 207 | adminAddManagersSubmitting = false |
|
230 | 220 | ref: UserSelectModal, |
231 | 221 | props: { |
232 | 222 | isAddManager: false, |
233 | | - existsManagers: channelInfo._managers, |
234 | | - existsMembers: channelInfo.members.map((m) => m.toText()), |
| 223 | + existsManagers: $channelInfo._managers, |
| 224 | + existsMembers: $channelInfo.members.map((m) => m.toText()), |
235 | 225 | myState: myState, |
236 | 226 | onSave: (members: Array<[Principal, Uint8Array | null]>) => { |
237 | 227 | adminAddMembersSubmitting = true |
238 | 228 | toastRun(async (signal: AbortSignal) => { |
239 | | - await myState.adminAddMembers(channelInfo, 'Member', members) |
240 | | - channelInfo = await myState.refreshChannel(channelInfo) |
| 229 | + await myState.adminAddMembers($channelInfo, 'Member', members) |
| 230 | + await myState.refreshChannel($channelInfo) |
241 | 231 | await loadMembers() |
242 | 232 | }, toastStore).finally(() => { |
243 | 233 | adminAddMembersSubmitting = false |
|
261 | 251 | } as ChannelECDHInput |
262 | 252 | }) |
263 | 253 |
|
264 | | - channelInfo = await myState.refreshChannel(channelInfo) |
| 254 | + await myState.refreshChannel($channelInfo) |
265 | 255 | await loadMembers() |
266 | 256 | }, toastStore).finally(() => { |
267 | 257 | adminRemoveMembersSubmitting = '' |
|
270 | 260 |
|
271 | 261 | onMount(() => { |
272 | 262 | const { abort } = toastRun(async (signal: AbortSignal) => { |
273 | | - channelInfo = await myState.refreshChannel(channelInfo, true) |
| 263 | + await myState.refreshChannel($channelInfo, true) |
| 264 | + try { |
| 265 | + await myState.decryptChannelDEK($channelInfo) |
| 266 | + validKEK = true |
| 267 | + } catch (_e) { |
| 268 | + validKEK = false |
| 269 | + } |
274 | 270 | await loadMembers() |
275 | 271 | }, toastStore) |
276 | 272 |
|
|
283 | 279 | > |
284 | 280 | <section class="mt-4 flex w-full flex-row items-center gap-4 self-start px-4"> |
285 | 281 | <Avatar |
286 | | - initials={channelInfo.name} |
287 | | - src={channelInfo.image} |
| 282 | + initials={$channelInfo.name} |
| 283 | + src={$channelInfo.image} |
288 | 284 | border="border-4 border-white" |
289 | | - background={channelInfo.image ? '' : 'bg-panda'} |
| 285 | + background={$channelInfo.image ? '' : 'bg-panda'} |
290 | 286 | fill="fill-white" |
291 | 287 | width="w-16" |
292 | 288 | /> |
293 | 289 | <div class="flex-1"> |
294 | 290 | <p class="flex flex-row"> |
295 | | - <span>{channelInfo.name}</span> |
| 291 | + <span>{$channelInfo.name}</span> |
296 | 292 | {#if isManager} |
297 | 293 | <button |
298 | 294 | type="button" |
|
303 | 299 | </button> |
304 | 300 | {/if} |
305 | 301 | </p> |
306 | | - {#if channelInfo.description} |
| 302 | + {#if $channelInfo.description} |
307 | 303 | <div class="mt-2"> |
308 | | - {@html md.render(channelInfo.description)} |
| 304 | + {@html md.render($channelInfo.description)} |
309 | 305 | </div> |
310 | 306 | {/if} |
311 | 307 | </div> |
|
314 | 310 | <div class="flex flex-row items-center gap-1"> |
315 | 311 | <span class="text-sm font-normal text-neutral-500">Messages:</span> |
316 | 312 | <span class="font-bold text-panda" |
317 | | - >{channelInfo.latest_message_id - channelInfo.message_start + 1}</span |
| 313 | + >{$channelInfo.latest_message_id - $channelInfo.message_start + 1}</span |
318 | 314 | > |
319 | 315 | </div> |
320 | 316 | <div class="flex flex-row items-center gap-2"> |
321 | 317 | <span class="text-sm font-normal text-neutral-500">Gas Balance:</span> |
322 | | - <span class="font-bold text-panda">{channelInfo.gas}</span> |
| 318 | + <span class="font-bold text-panda">{$channelInfo.gas}</span> |
323 | 319 | </div> |
324 | 320 | <button |
325 | 321 | type="button" |
|
348 | 344 | </div> |
349 | 345 | <div class="flex flex-row items-center gap-4"> |
350 | 346 | <p>Request encryption key:</p> |
351 | | - {#if kekStatus === 1} |
| 347 | + {#if $channelInfo.my_setting.ecdh_remote.length > 0} |
352 | 348 | <button |
353 | 349 | type="button" |
354 | 350 | class="variant-filled-success btn btn-sm" |
355 | 351 | on:click={onClickMyECDH} |
356 | 352 | disabled={myECDHSubmitting} |
357 | 353 | ><span>Key received, accept it</span></button |
358 | 354 | > |
359 | | - {:else if kekStatus === 2} |
| 355 | + {:else if $channelInfo.my_setting.ecdh_pub.length > 0} |
360 | 356 | <span class="text-sm opacity-50" |
361 | 357 | >Request sent, waiting for a manager share key</span |
362 | 358 | > |
363 | | - {:else if kekStatus === 3} |
| 359 | + {:else if validKEK} |
364 | 360 | <span class="text-sm opacity-50" |
365 | 361 | >Key already exists, no action needed</span |
366 | 362 | > |
|
393 | 389 | class="variant-filled-warning !px-2 disabled:variant-filled-surface" |
394 | 390 | on:click={onClickMyLeaving} |
395 | 391 | disabled={myLeavingSubmitting || |
396 | | - leavingWord.trim() != channelInfo.name} |
| 392 | + leavingWord.trim() != $channelInfo.name} |
397 | 393 | ><span class="*:size-5"> |
398 | 394 | {#if myLeavingSubmitting} |
399 | 395 | <IconCircleSpin /> |
|
404 | 400 | > |
405 | 401 | </div> |
406 | 402 | </div> |
407 | | - {#if isManager && channelInfo._managers.length < 2} |
| 403 | + {#if isManager && $channelInfo._managers.length < 2} |
408 | 404 | <p |
409 | | - class="h-5 text-sm text-error-500 {leavingWord === channelInfo.name |
| 405 | + class="h-5 text-sm text-error-500 {leavingWord === $channelInfo.name |
410 | 406 | ? 'visible' |
411 | 407 | : 'invisible'}" |
412 | 408 | >You are the only manager. Leaving the channel will delete all its data.</p |
|
0 commit comments