@dcl-regenesislabs/web-editor 0.0.0-stage → 0.1.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.
- package/editor.css +1 -0
- package/editor.js +33891 -0
- package/package.json +8 -4
- package/prefabs/admin-tools/composite.json +77 -0
- package/prefabs/admin-tools/data.json +28 -0
- package/prefabs/admin-tools/icons/admin-panel-control-button.png +0 -0
- package/prefabs/admin-tools/icons/admin-panel-moderation-control-button.png +0 -0
- package/prefabs/admin-tools/icons/admin-panel-rewards-control-button.png +0 -0
- package/prefabs/admin-tools/icons/admin-panel-smart-item-control-button.png +0 -0
- package/prefabs/admin-tools/icons/admin-panel-text-announcement-control-button.png +0 -0
- package/prefabs/admin-tools/icons/admin-panel-video-control-button.png +0 -0
- package/prefabs/admin-tools/icons/admin-tool-background.png +0 -0
- package/prefabs/admin-tools/icons/announcements/text-announcement-chat-message.png +0 -0
- package/prefabs/admin-tools/icons/announcements/text-announcement-check.png +0 -0
- package/prefabs/admin-tools/icons/announcements/text-announcement-close-button.png +0 -0
- package/prefabs/admin-tools/icons/moderation/admin-panel-verified-user.png +0 -0
- package/prefabs/admin-tools/icons/moderation/ban.png +0 -0
- package/prefabs/admin-tools/icons/moderation/chevron-back.png +0 -0
- package/prefabs/admin-tools/icons/moderation/chevron-forward.png +0 -0
- package/prefabs/admin-tools/icons/moderation/close.png +0 -0
- package/prefabs/admin-tools/icons/moderation/error.png +0 -0
- package/prefabs/admin-tools/icons/moderation/person-outline.png +0 -0
- package/prefabs/admin-tools/icons/moderation-control-icon.png +0 -0
- package/prefabs/admin-tools/icons/rewards/close.png +0 -0
- package/prefabs/admin-tools/icons/rewards/send.png +0 -0
- package/prefabs/admin-tools/icons/rewards-control.png +0 -0
- package/prefabs/admin-tools/icons/smart-item-control.png +0 -0
- package/prefabs/admin-tools/icons/text-announcement-control.png +0 -0
- package/prefabs/admin-tools/icons/video/chevron-down.png +0 -0
- package/prefabs/admin-tools/icons/video/chevron-up.png +0 -0
- package/prefabs/admin-tools/icons/video/copy-to-clipboard.png +0 -0
- package/prefabs/admin-tools/icons/video/error.png +0 -0
- package/prefabs/admin-tools/icons/video/eye-off.png +0 -0
- package/prefabs/admin-tools/icons/video/eye.png +0 -0
- package/prefabs/admin-tools/icons/video/help.png +0 -0
- package/prefabs/admin-tools/icons/video/info.png +0 -0
- package/prefabs/admin-tools/icons/video/person-outline.png +0 -0
- package/prefabs/admin-tools/icons/video/star.png +0 -0
- package/prefabs/admin-tools/icons/video/video-control-dcl-cast.png +0 -0
- package/prefabs/admin-tools/icons/video/video-control-live.png +0 -0
- package/prefabs/admin-tools/icons/video/video-control-loop.png +0 -0
- package/prefabs/admin-tools/icons/video/video-control-mute.png +0 -0
- package/prefabs/admin-tools/icons/video/video-control-pause-button.png +0 -0
- package/prefabs/admin-tools/icons/video/video-control-play-button.png +0 -0
- package/prefabs/admin-tools/icons/video/video-control-video-icon.png +0 -0
- package/prefabs/admin-tools/icons/video/video-control-volume-minus-button.png +0 -0
- package/prefabs/admin-tools/icons/video/video-control-volume-plus-button.png +0 -0
- package/prefabs/admin-tools/icons/video-control.png +0 -0
- package/prefabs/admin-tools/scripts/actions.ts +206 -0
- package/prefabs/admin-tools/scripts/admin.tsx +253 -0
- package/prefabs/admin-tools/scripts/api.ts +125 -0
- package/prefabs/admin-tools/scripts/components.ts +190 -0
- package/prefabs/admin-tools/scripts/icons.ts +53 -0
- package/prefabs/admin-tools/scripts/message-bus.ts +297 -0
- package/prefabs/admin-tools/scripts/state.ts +69 -0
- package/prefabs/admin-tools/scripts/tabs/Moderation.tsx +155 -0
- package/prefabs/admin-tools/scripts/tabs/Rewards.tsx +136 -0
- package/prefabs/admin-tools/scripts/tabs/SmartItemActions.tsx +167 -0
- package/prefabs/admin-tools/scripts/tabs/TextAnnouncements.tsx +195 -0
- package/prefabs/admin-tools/scripts/tabs/VideoControl.tsx +156 -0
- package/prefabs/admin-tools/scripts/tabs/announcements/Overlay.tsx +94 -0
- package/prefabs/admin-tools/scripts/tabs/announcements/icons.ts +15 -0
- package/prefabs/admin-tools/scripts/tabs/announcements/state.ts +71 -0
- package/prefabs/admin-tools/scripts/tabs/announcements/ui.tsx +37 -0
- package/prefabs/admin-tools/scripts/tabs/moderation/AddUserInput.tsx +184 -0
- package/prefabs/admin-tools/scripts/tabs/moderation/RemoveAdminConfirmation.tsx +93 -0
- package/prefabs/admin-tools/scripts/tabs/moderation/UsersList.tsx +228 -0
- package/prefabs/admin-tools/scripts/tabs/moderation/api.ts +35 -0
- package/prefabs/admin-tools/scripts/tabs/moderation/icons.ts +23 -0
- package/prefabs/admin-tools/scripts/tabs/moderation/kick.ts +95 -0
- package/prefabs/admin-tools/scripts/tabs/moderation/state.ts +69 -0
- package/prefabs/admin-tools/scripts/tabs/moderation/ui.tsx +172 -0
- package/prefabs/admin-tools/scripts/tabs/moderation/utils.ts +88 -0
- package/prefabs/admin-tools/scripts/tabs/rewards/Button.tsx +52 -0
- package/prefabs/admin-tools/scripts/tabs/rewards/Captcha.tsx +88 -0
- package/prefabs/admin-tools/scripts/tabs/rewards/api.ts +138 -0
- package/prefabs/admin-tools/scripts/tabs/rewards/claim.ts +99 -0
- package/prefabs/admin-tools/scripts/tabs/rewards/controller.ts +103 -0
- package/prefabs/admin-tools/scripts/tabs/rewards/icons.ts +13 -0
- package/prefabs/admin-tools/scripts/tabs/rewards/state.ts +32 -0
- package/prefabs/admin-tools/scripts/tabs/smart-items/selection.ts +90 -0
- package/prefabs/admin-tools/scripts/tabs/video/DclCast.tsx +226 -0
- package/prefabs/admin-tools/scripts/tabs/video/LiveStream.tsx +284 -0
- package/prefabs/admin-tools/scripts/tabs/video/SpeakerShowcase.tsx +112 -0
- package/prefabs/admin-tools/scripts/tabs/video/VideoUrl.tsx +107 -0
- package/prefabs/admin-tools/scripts/tabs/video/Volume.tsx +69 -0
- package/prefabs/admin-tools/scripts/tabs/video/api.ts +172 -0
- package/prefabs/admin-tools/scripts/tabs/video/controls.ts +108 -0
- package/prefabs/admin-tools/scripts/tabs/video/icons.ts +59 -0
- package/prefabs/admin-tools/scripts/tabs/video/speakers.ts +43 -0
- package/prefabs/admin-tools/scripts/tabs/video/state.ts +82 -0
- package/prefabs/admin-tools/scripts/tabs/video/ui.tsx +243 -0
- package/prefabs/admin-tools/scripts/types.ts +46 -0
- package/prefabs/admin-tools/scripts/ui.tsx +122 -0
- package/prefabs/admin-tools/thumbnail.png +0 -0
- package/prefabs/announcer/ai.md +76 -0
- package/prefabs/announcer/composite.json +31 -0
- package/prefabs/announcer/data.json +35 -0
- package/prefabs/announcer/scripts/announcer.tsx +79 -0
- package/prefabs/announcer/scripts/pure/toast.ts +30 -0
- package/prefabs/announcer/thumbnail.png +0 -0
- package/prefabs/black-chair/HighBackChair.glb +0 -0
- package/prefabs/black-chair/composite.json +74 -0
- package/prefabs/black-chair/data.json +30 -0
- package/prefabs/black-chair/scripts/seat.ts +191 -0
- package/prefabs/black-chair/thumbnail.png +0 -0
- package/prefabs/catalog.json +1 -0
- package/prefabs/classic-bench/Bench_01.glb +0 -0
- package/prefabs/classic-bench/composite.json +100 -0
- package/prefabs/classic-bench/data.json +34 -0
- package/prefabs/classic-bench/file1.png +0 -0
- package/prefabs/classic-bench/scripts/seat.ts +191 -0
- package/prefabs/classic-bench/thumbnail.png +0 -0
- package/prefabs/classic-bench-armrests/Bench 1.glb +0 -0
- package/prefabs/classic-bench-armrests/composite.json +100 -0
- package/prefabs/classic-bench-armrests/data.json +30 -0
- package/prefabs/classic-bench-armrests/scripts/seat.ts +191 -0
- package/prefabs/classic-bench-armrests/thumbnail.png +0 -0
- package/prefabs/classroom-chair/Chair_02.glb +0 -0
- package/prefabs/classroom-chair/composite.json +74 -0
- package/prefabs/classroom-chair/data.json +34 -0
- package/prefabs/classroom-chair/file1.png +0 -0
- package/prefabs/classroom-chair/scripts/seat.ts +191 -0
- package/prefabs/classroom-chair/thumbnail.png +0 -0
- package/prefabs/court-chair/Chair_Wood_01.glb +0 -0
- package/prefabs/court-chair/FanstasyPack_TX.png +0 -0
- package/prefabs/court-chair/composite.json +74 -0
- package/prefabs/court-chair/data.json +34 -0
- package/prefabs/court-chair/scripts/seat.ts +191 -0
- package/prefabs/court-chair/thumbnail.png +0 -0
- package/prefabs/curved-couch/CurvedCouch.glb +0 -0
- package/prefabs/curved-couch/composite.json +100 -0
- package/prefabs/curved-couch/data.json +30 -0
- package/prefabs/curved-couch/scripts/seat.ts +191 -0
- package/prefabs/curved-couch/thumbnail.png +0 -0
- package/prefabs/game-flow/ai.md +94 -0
- package/prefabs/game-flow/composite.json +45 -0
- package/prefabs/game-flow/data.json +27 -0
- package/prefabs/game-flow/scripts/game-flow.ts +168 -0
- package/prefabs/game-flow/scripts/pure/flow.ts +185 -0
- package/prefabs/game-flow/thumbnail.png +0 -0
- package/prefabs/health-respawn/ai.md +76 -0
- package/prefabs/health-respawn/composite.json +31 -0
- package/prefabs/health-respawn/data.json +38 -0
- package/prefabs/health-respawn/scripts/health-respawn.ts +98 -0
- package/prefabs/health-respawn/scripts/health.ts +22 -0
- package/prefabs/health-respawn/scripts/pure/health.ts +60 -0
- package/prefabs/health-respawn/thumbnail.png +0 -0
- package/prefabs/high-stool/HighStool.glb +0 -0
- package/prefabs/high-stool/composite.json +74 -0
- package/prefabs/high-stool/data.json +30 -0
- package/prefabs/high-stool/scripts/seat.ts +191 -0
- package/prefabs/high-stool/thumbnail.png +0 -0
- package/prefabs/large-couch/ThreeSeater_Couch.glb +0 -0
- package/prefabs/large-couch/composite.json +100 -0
- package/prefabs/large-couch/data.json +30 -0
- package/prefabs/large-couch/scripts/seat.ts +191 -0
- package/prefabs/large-couch/thumbnail.png +0 -0
- package/prefabs/leaderboard/ai.md +75 -0
- package/prefabs/leaderboard/board-panel.glb +0 -0
- package/prefabs/leaderboard/composite.json +82 -0
- package/prefabs/leaderboard/data.json +35 -0
- package/prefabs/leaderboard/scripts/leaderboard.ts +66 -0
- package/prefabs/leaderboard/scripts/pure/board.ts +71 -0
- package/prefabs/leaderboard/thumbnail.png +0 -0
- package/prefabs/loveseat/Couch_01.glb +0 -0
- package/prefabs/loveseat/composite.json +100 -0
- package/prefabs/loveseat/data.json +34 -0
- package/prefabs/loveseat/file1.png +0 -0
- package/prefabs/loveseat/scripts/seat.ts +191 -0
- package/prefabs/loveseat/thumbnail.png +0 -0
- package/prefabs/modern-sofa/ModernSofa.glb +0 -0
- package/prefabs/modern-sofa/composite.json +100 -0
- package/prefabs/modern-sofa/data.json +30 -0
- package/prefabs/modern-sofa/scripts/seat.ts +191 -0
- package/prefabs/modern-sofa/thumbnail.png +0 -0
- package/prefabs/outdoor-chair/Chair_07.glb +0 -0
- package/prefabs/outdoor-chair/composite.json +74 -0
- package/prefabs/outdoor-chair/data.json +34 -0
- package/prefabs/outdoor-chair/file1.png +0 -0
- package/prefabs/outdoor-chair/scripts/seat.ts +191 -0
- package/prefabs/outdoor-chair/thumbnail.png +0 -0
- package/prefabs/rustic-bench/Bench_01.glb +0 -0
- package/prefabs/rustic-bench/PiratesPack_TX.png.png +0 -0
- package/prefabs/rustic-bench/composite.json +100 -0
- package/prefabs/rustic-bench/data.json +34 -0
- package/prefabs/rustic-bench/scripts/seat.ts +191 -0
- package/prefabs/rustic-bench/thumbnail.png +0 -0
- package/prefabs/rustic-round-stool/Chairwood_01.glb +0 -0
- package/prefabs/rustic-round-stool/PiratesPack_TX.png.png +0 -0
- package/prefabs/rustic-round-stool/composite.json +74 -0
- package/prefabs/rustic-round-stool/data.json +34 -0
- package/prefabs/rustic-round-stool/scripts/seat.ts +191 -0
- package/prefabs/rustic-round-stool/thumbnail.png +0 -0
- package/prefabs/rustic-square-stool/Chairwood_02.glb +0 -0
- package/prefabs/rustic-square-stool/PiratesPack_TX.png.png +0 -0
- package/prefabs/rustic-square-stool/composite.json +74 -0
- package/prefabs/rustic-square-stool/data.json +34 -0
- package/prefabs/rustic-square-stool/scripts/seat.ts +191 -0
- package/prefabs/rustic-square-stool/thumbnail.png +0 -0
- package/prefabs/server-clock/ai.md +67 -0
- package/prefabs/server-clock/composite.json +45 -0
- package/prefabs/server-clock/data.json +24 -0
- package/prefabs/server-clock/scripts/clock-hud.tsx +55 -0
- package/prefabs/server-clock/scripts/server-clock.ts +71 -0
- package/prefabs/server-clock/thumbnail.png +0 -0
- package/prefabs/simple-chair/Chair_03.glb +0 -0
- package/prefabs/simple-chair/composite.json +74 -0
- package/prefabs/simple-chair/data.json +34 -0
- package/prefabs/simple-chair/file1.png +0 -0
- package/prefabs/simple-chair/scripts/seat.ts +191 -0
- package/prefabs/simple-chair/thumbnail.png +0 -0
- package/prefabs/sit-spot/composite.json +45 -0
- package/prefabs/sit-spot/data.json +30 -0
- package/prefabs/sit-spot/scripts/seat.ts +191 -0
- package/prefabs/sit-spot/thumbnail.png +0 -0
- package/prefabs/sit-spot-edge/composite.json +42 -0
- package/prefabs/sit-spot-edge/data.json +32 -0
- package/prefabs/sit-spot-edge/emotes/Sit_Edge1_emote.glb +0 -0
- package/prefabs/sit-spot-edge/emotes/Sit_Edge2_emote.glb +0 -0
- package/prefabs/sit-spot-edge/scripts/seat.ts +191 -0
- package/prefabs/sit-spot-edge/thumbnail.png +0 -0
- package/prefabs/small-couch/TwoSeater_Couch.glb +0 -0
- package/prefabs/small-couch/composite.json +100 -0
- package/prefabs/small-couch/data.json +30 -0
- package/prefabs/small-couch/scripts/seat.ts +191 -0
- package/prefabs/small-couch/thumbnail.png +0 -0
- package/prefabs/spawner/ai.md +109 -0
- package/prefabs/spawner/composite.json +73 -0
- package/prefabs/spawner/data.json +22 -0
- package/prefabs/spawner/scripts/spawner.ts +380 -0
- package/prefabs/spawner/thumbnail.png +0 -0
- package/prefabs/steampunk-bench/Steampunk Bench.glb +0 -0
- package/prefabs/steampunk-bench/composite.json +100 -0
- package/prefabs/steampunk-bench/data.json +30 -0
- package/prefabs/steampunk-bench/scripts/seat.ts +191 -0
- package/prefabs/steampunk-bench/thumbnail.png +0 -0
- package/prefabs/steampunk-chair/Steampunk Chair.glb +0 -0
- package/prefabs/steampunk-chair/composite.json +74 -0
- package/prefabs/steampunk-chair/data.json +30 -0
- package/prefabs/steampunk-chair/scripts/seat.ts +191 -0
- package/prefabs/steampunk-chair/thumbnail.png +0 -0
- package/prefabs/tall-stool/Stool_D.glb +0 -0
- package/prefabs/tall-stool/composite.json +74 -0
- package/prefabs/tall-stool/data.json +30 -0
- package/prefabs/tall-stool/scripts/seat.ts +191 -0
- package/prefabs/tall-stool/thumbnail.png +0 -0
- package/prefabs/trigger-zone/ai.md +115 -0
- package/prefabs/trigger-zone/composite.json +52 -0
- package/prefabs/trigger-zone/data.json +31 -0
- package/prefabs/trigger-zone/scripts/trigger-zone.ts +89 -0
- package/prefabs/trigger-zone/thumbnail.png +0 -0
- package/prefabs/video-screen/composite.json +98 -0
- package/prefabs/video-screen/data.json +21 -0
- package/prefabs/video-screen/thumbnail.png +0 -0
- package/prefabs/video-screen/video_player.glb +0 -0
- package/prefabs/wooden-chair/Chair 1.glb +0 -0
- package/prefabs/wooden-chair/composite.json +74 -0
- package/prefabs/wooden-chair/data.json +30 -0
- package/prefabs/wooden-chair/scripts/seat.ts +191 -0
- package/prefabs/wooden-chair/thumbnail.png +0 -0
- package/prefabs/wooden-chair-armrests/Chair 3.glb +0 -0
- package/prefabs/wooden-chair-armrests/composite.json +74 -0
- package/prefabs/wooden-chair-armrests/data.json +30 -0
- package/prefabs/wooden-chair-armrests/scripts/seat.ts +191 -0
- package/prefabs/wooden-chair-armrests/thumbnail.png +0 -0
- package/scene/about +17 -0
- package/scene/bafkreias2w4xuqaic6sdcvblrisayi6xg7drfowzpt5ubjkmrk3mx6czc4 +1 -0
- package/scene/bafkreiby2gmthtauf7cckttphmhpecsueyypi2hzlke4lcodg7fm4cm2sq +23 -0
- package/scene/bafkreidhwqwajqk4nsuffef5fkdrgmp5ql6vnn2knpqsuqalyosv5o6lk4 +1 -0
- package/scene/bafkreihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku +0 -0
- package/scene/bafybeihw3xlbff4sf5sc3kvmlmlwf2visnty5lfymawlunzt5vrkp5fd7u +90 -0
- package/web-build/esbuild.wasm +0 -0
- package/web-build/index.js +3 -0
- package/web-build/sdk-snapshot.json +1 -0
- package/web-build/worker.js +701 -0
- package/README.md +0 -3
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// The CLAIM_AIRDROP flow from @dcl/asset-packs src/actions.ts (~line 1544), as a
|
|
2
|
+
// standalone module: read the dispenser's asset-packs::Rewards config, look the
|
|
3
|
+
// campaign up by dispenser key, and either claim straight away or hand the caller
|
|
4
|
+
// a captcha to solve first. Test mode short-circuits, like the Hub's interpreter.
|
|
5
|
+
import type { Entity } from '@dcl/sdk/ecs'
|
|
6
|
+
import { getPlayer } from '@dcl/sdk/players'
|
|
7
|
+
import { loadRealm } from '../../api'
|
|
8
|
+
import { Rewards } from '../../components'
|
|
9
|
+
import {
|
|
10
|
+
claimReward,
|
|
11
|
+
fetchCampaigns,
|
|
12
|
+
fetchCaptcha,
|
|
13
|
+
isCaptchaRequired,
|
|
14
|
+
type RewardCaptcha
|
|
15
|
+
} from './api'
|
|
16
|
+
|
|
17
|
+
export const CLAIM_AIRDROP_ACTION = 'claim_airdrop'
|
|
18
|
+
|
|
19
|
+
export interface PendingCaptcha {
|
|
20
|
+
campaignId: string
|
|
21
|
+
dispenserKey: string
|
|
22
|
+
captcha: RewardCaptcha
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type ClaimOutcome =
|
|
26
|
+
| { kind: 'claimed'; message: string }
|
|
27
|
+
| { kind: 'captcha'; pending: PendingCaptcha }
|
|
28
|
+
| { kind: 'test-mode' }
|
|
29
|
+
| { kind: 'error'; message: string }
|
|
30
|
+
|
|
31
|
+
function failed(message: string): ClaimOutcome {
|
|
32
|
+
return { kind: 'error', message }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function beneficiary(): string {
|
|
36
|
+
const player = getPlayer()
|
|
37
|
+
if (player === null || player.isGuest) return ''
|
|
38
|
+
return player.userId.toLowerCase()
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function catalyst(): Promise<string> {
|
|
42
|
+
const realm = await loadRealm()
|
|
43
|
+
return realm?.baseUrl ?? ''
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function promptCaptcha(campaignId: string, dispenserKey: string): Promise<ClaimOutcome> {
|
|
47
|
+
const [error, captcha] = await fetchCaptcha()
|
|
48
|
+
if (error !== null) return failed(error)
|
|
49
|
+
return { kind: 'captcha', pending: { campaignId, dispenserKey, captcha } }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function claim(
|
|
53
|
+
campaignId: string,
|
|
54
|
+
dispenserKey: string,
|
|
55
|
+
captcha?: { id: string; value: string }
|
|
56
|
+
): Promise<ClaimOutcome> {
|
|
57
|
+
const [error, summary] = await claimReward({
|
|
58
|
+
dispenserKey,
|
|
59
|
+
beneficiary: beneficiary(),
|
|
60
|
+
catalyst: await catalyst(),
|
|
61
|
+
captchaId: captcha?.id,
|
|
62
|
+
captchaValue: captcha?.value
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
if (error !== null) {
|
|
66
|
+
// The dispenser may demand a captcha even when the campaign flag says
|
|
67
|
+
// otherwise — the newer Hub dispenser script reads the same signal.
|
|
68
|
+
if (captcha === undefined && isCaptchaRequired(error)) return promptCaptcha(campaignId, dispenserKey)
|
|
69
|
+
return failed(error)
|
|
70
|
+
}
|
|
71
|
+
const reward = summary.count === 1 ? 'reward' : 'rewards'
|
|
72
|
+
return { kind: 'claimed', message: `Airdrop released — ${summary.count} ${reward} ${summary.status}.` }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Runs the airdrop claim for a dispenser entity carrying asset-packs::Rewards. */
|
|
76
|
+
export async function releaseAirdrop(entity: Entity): Promise<ClaimOutcome> {
|
|
77
|
+
const rewards = Rewards.getOrNull(entity)
|
|
78
|
+
if (rewards === null) return failed('This item has no Rewards configuration.')
|
|
79
|
+
|
|
80
|
+
const { campaignId, dispenserKey, testMode } = rewards
|
|
81
|
+
if (testMode) return { kind: 'test-mode' }
|
|
82
|
+
if (dispenserKey === '') return failed('This item has no dispenser key set.')
|
|
83
|
+
|
|
84
|
+
const [error, campaigns] = await fetchCampaigns(dispenserKey)
|
|
85
|
+
if (error !== null) return failed(error)
|
|
86
|
+
|
|
87
|
+
const campaign = campaigns.find((entry) => entry.campaignId === campaignId)
|
|
88
|
+
if (campaign === undefined) return failed('No campaign matches this dispenser key.')
|
|
89
|
+
if (!campaign.enabled) return failed('This campaign is disabled on rewards.decentraland.org.')
|
|
90
|
+
if (campaign.requiresCaptcha) return promptCaptcha(campaignId, dispenserKey)
|
|
91
|
+
|
|
92
|
+
return claim(campaignId, dispenserKey)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export async function submitCaptcha(pending: PendingCaptcha, value: string): Promise<ClaimOutcome> {
|
|
96
|
+
const answer = value.trim()
|
|
97
|
+
if (answer === '') return failed('Enter the captcha to continue.')
|
|
98
|
+
return claim(pending.campaignId, pending.dispenserKey, { id: pending.captcha.id, value: answer })
|
|
99
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// What the Release / Clear / Submit buttons do. Same contract as the Creator Hub's
|
|
2
|
+
// RewardsControl: Release runs the dispenser's "Airdrop" action and Clear runs its
|
|
3
|
+
// "Invisible" one. The names are the Hub's convention, so an action of the matching
|
|
4
|
+
// *type* is accepted as a fallback for items authored with different labels.
|
|
5
|
+
import type { Entity } from '@dcl/sdk/ecs'
|
|
6
|
+
import { ActionType, getActions, runAction } from '../../actions'
|
|
7
|
+
import type { ActionEntry, RewardItemRef } from '../../components'
|
|
8
|
+
import { Rewards } from '../../components'
|
|
9
|
+
import { CLAIM_AIRDROP_ACTION, releaseAirdrop, submitCaptcha, type ClaimOutcome } from './claim'
|
|
10
|
+
import { clearCaptcha, rewardsTabState, setStatus } from './state'
|
|
11
|
+
|
|
12
|
+
export const RELEASE_ACTION = 'Airdrop'
|
|
13
|
+
export const CLEAR_ACTION = 'Invisible'
|
|
14
|
+
|
|
15
|
+
export function selectedEntity(
|
|
16
|
+
items: readonly RewardItemRef[],
|
|
17
|
+
index: number | undefined
|
|
18
|
+
): Entity | null {
|
|
19
|
+
if (index === undefined || index < 0 || index >= items.length) return null
|
|
20
|
+
return items[index].entity as Entity
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function findAction(entity: Entity, name: string, type: string): ActionEntry | undefined {
|
|
24
|
+
const actions = getActions(entity)
|
|
25
|
+
return actions.find((action) => action.name === name) ?? actions.find((action) => action.type === type)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function describe(error: unknown): string {
|
|
29
|
+
return error instanceof Error ? error.message : String(error)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function applyOutcome(outcome: ClaimOutcome): void {
|
|
33
|
+
switch (outcome.kind) {
|
|
34
|
+
case 'claimed':
|
|
35
|
+
clearCaptcha()
|
|
36
|
+
setStatus(outcome.message, 'success')
|
|
37
|
+
break
|
|
38
|
+
case 'captcha':
|
|
39
|
+
rewardsTabState.pending = outcome.pending
|
|
40
|
+
rewardsTabState.answer = ''
|
|
41
|
+
setStatus('Solve the captcha to release this airdrop.')
|
|
42
|
+
break
|
|
43
|
+
case 'test-mode':
|
|
44
|
+
clearCaptcha()
|
|
45
|
+
setStatus('Test mode is on for this item — nothing was assigned.')
|
|
46
|
+
break
|
|
47
|
+
case 'error':
|
|
48
|
+
setStatus(outcome.message, 'error')
|
|
49
|
+
break
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function run(task: Promise<ClaimOutcome>): Promise<void> {
|
|
54
|
+
rewardsTabState.busy = true
|
|
55
|
+
try {
|
|
56
|
+
applyOutcome(await task)
|
|
57
|
+
} catch (error) {
|
|
58
|
+
setStatus(describe(error), 'error')
|
|
59
|
+
} finally {
|
|
60
|
+
rewardsTabState.busy = false
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function release(entity: Entity): void {
|
|
65
|
+
if (rewardsTabState.busy) return
|
|
66
|
+
const action = findAction(entity, RELEASE_ACTION, CLAIM_AIRDROP_ACTION)
|
|
67
|
+
|
|
68
|
+
if (action !== undefined && action.type !== CLAIM_AIRDROP_ACTION) {
|
|
69
|
+
runAction(entity, action)
|
|
70
|
+
clearCaptcha()
|
|
71
|
+
setStatus(`Ran "${action.name}" on this item.`, 'success')
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
clearCaptcha()
|
|
76
|
+
setStatus('Releasing airdrop…')
|
|
77
|
+
void run(releaseAirdrop(entity))
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function clear(entity: Entity): void {
|
|
81
|
+
if (rewardsTabState.busy) return
|
|
82
|
+
if (Rewards.getOrNull(entity) === null) {
|
|
83
|
+
setStatus('This item has no Rewards configuration.', 'error')
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const action = findAction(entity, CLEAR_ACTION, ActionType.SET_VISIBILITY)
|
|
88
|
+
if (action === undefined) {
|
|
89
|
+
setStatus(`This item has no "${CLEAR_ACTION}" action to clear it with.`, 'error')
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
runAction(entity, action)
|
|
94
|
+
clearCaptcha()
|
|
95
|
+
setStatus('Airdrop cleared.', 'success')
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function submit(): void {
|
|
99
|
+
const pending = rewardsTabState.pending
|
|
100
|
+
if (pending === null || rewardsTabState.busy) return
|
|
101
|
+
setStatus('Checking the captcha…')
|
|
102
|
+
void run(submitCaptcha(pending, rewardsTabState.answer))
|
|
103
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { iconPath } from '../../icons'
|
|
2
|
+
|
|
3
|
+
export interface RewardsIcons {
|
|
4
|
+
send: string
|
|
5
|
+
close: string
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function rewardsIcons(base: string): RewardsIcons {
|
|
9
|
+
return {
|
|
10
|
+
send: iconPath(base, 'rewards/send.png'),
|
|
11
|
+
close: iconPath(base, 'rewards/close.png')
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Per-tab UI state. The shared AdminState only carries the selected airdrop, so
|
|
2
|
+
// the in-flight claim (busy flag, feedback line, captcha challenge) lives here —
|
|
3
|
+
// one panel per scene, so a module singleton is enough.
|
|
4
|
+
import type { PendingCaptcha } from './claim'
|
|
5
|
+
|
|
6
|
+
export type StatusTone = 'info' | 'error' | 'success'
|
|
7
|
+
|
|
8
|
+
export interface RewardsTabState {
|
|
9
|
+
busy: boolean
|
|
10
|
+
status: string
|
|
11
|
+
tone: StatusTone
|
|
12
|
+
pending: PendingCaptcha | null
|
|
13
|
+
answer: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const rewardsTabState: RewardsTabState = {
|
|
17
|
+
busy: false,
|
|
18
|
+
status: '',
|
|
19
|
+
tone: 'info',
|
|
20
|
+
pending: null,
|
|
21
|
+
answer: ''
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function setStatus(status: string, tone: StatusTone = 'info'): void {
|
|
25
|
+
rewardsTabState.status = status
|
|
26
|
+
rewardsTabState.tone = tone
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function clearCaptcha(): void {
|
|
30
|
+
rewardsTabState.pending = null
|
|
31
|
+
rewardsTabState.answer = ''
|
|
32
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// Smart-item list building and per-item panel state.
|
|
2
|
+
//
|
|
3
|
+
// `linkAllSmartItems` is a plain flag in the inspector — nothing populates the
|
|
4
|
+
// list from it — so it is honoured here: with it on, every entity carrying
|
|
5
|
+
// asset-packs::Actions joins the dropdown, named after core-schema::Name.
|
|
6
|
+
import { engine, Name, VisibilityComponent, type Entity } from '@dcl/sdk/ecs'
|
|
7
|
+
import { getActions } from '../../actions'
|
|
8
|
+
import { Actions, type AdminToolsValue } from '../../components'
|
|
9
|
+
import type { AdminState, SmartItemUiState } from '../../state'
|
|
10
|
+
|
|
11
|
+
export interface SmartItemEntry {
|
|
12
|
+
entity: Entity
|
|
13
|
+
customName: string
|
|
14
|
+
defaultAction: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface SmartItemsUiState {
|
|
18
|
+
lastPlayed: string
|
|
19
|
+
lastPlayedFailed: boolean
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const smartItemsUi: SmartItemsUiState = { lastPlayed: '', lastPlayedFailed: false }
|
|
23
|
+
|
|
24
|
+
function labelFor(entity: Entity): string {
|
|
25
|
+
const name = Name.getOrNull(entity)
|
|
26
|
+
return name !== null && name.value !== '' ? name.value : `Entity ${entity as number}`
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function smartItemList(config: AdminToolsValue, self: Entity): SmartItemEntry[] {
|
|
30
|
+
const entries: SmartItemEntry[] = []
|
|
31
|
+
const seen = new Set<number>()
|
|
32
|
+
|
|
33
|
+
for (const item of config.smartItemsControl.smartItems ?? []) {
|
|
34
|
+
if (item.entity === 0 || seen.has(item.entity)) continue
|
|
35
|
+
seen.add(item.entity)
|
|
36
|
+
const entity = item.entity as Entity
|
|
37
|
+
entries.push({
|
|
38
|
+
entity,
|
|
39
|
+
customName: item.customName === '' ? labelFor(entity) : item.customName,
|
|
40
|
+
defaultAction: item.defaultAction
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (!config.smartItemsControl.linkAllSmartItems) return entries
|
|
45
|
+
|
|
46
|
+
const discovered: SmartItemEntry[] = []
|
|
47
|
+
for (const [entity] of engine.getEntitiesWith(Actions)) {
|
|
48
|
+
if (entity === self || seen.has(entity as number)) continue
|
|
49
|
+
seen.add(entity as number)
|
|
50
|
+
discovered.push({
|
|
51
|
+
entity,
|
|
52
|
+
customName: labelFor(entity),
|
|
53
|
+
defaultAction: getActions(entity)[0]?.name ?? ''
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
discovered.sort((a, b) => (a.entity as number) - (b.entity as number))
|
|
57
|
+
return [...entries, ...discovered]
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function uiStateFor(state: AdminState, item: SmartItemEntry): SmartItemUiState {
|
|
61
|
+
const existing = state.smartItems.byEntity.get(item.entity)
|
|
62
|
+
if (existing !== undefined) return existing
|
|
63
|
+
const created: SmartItemUiState = {
|
|
64
|
+
visible: VisibilityComponent.getOrNull(item.entity)?.visible ?? true,
|
|
65
|
+
selectedAction: item.defaultAction
|
|
66
|
+
}
|
|
67
|
+
state.smartItems.byEntity.set(item.entity, created)
|
|
68
|
+
return created
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function selectedItem(state: AdminState, items: SmartItemEntry[]): SmartItemEntry | undefined {
|
|
72
|
+
const index = state.smartItems.selectedIndex
|
|
73
|
+
if (index === undefined || index < 0 || index >= items.length) return undefined
|
|
74
|
+
return items[index]
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function selectSmartItem(state: AdminState, items: SmartItemEntry[], index: number): void {
|
|
78
|
+
if (index < 0 || index >= items.length) return
|
|
79
|
+
state.smartItems.selectedIndex = index
|
|
80
|
+
uiStateFor(state, items[index])
|
|
81
|
+
smartItemsUi.lastPlayed = ''
|
|
82
|
+
smartItemsUi.lastPlayedFailed = false
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function setVisible(state: AdminState, item: SmartItemEntry, visible: boolean): void {
|
|
86
|
+
uiStateFor(state, item).visible = visible
|
|
87
|
+
const existing = VisibilityComponent.getMutableOrNull(item.entity)
|
|
88
|
+
if (existing === null) VisibilityComponent.create(item.entity, { visible })
|
|
89
|
+
else existing.visible = visible
|
|
90
|
+
}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import ReactEcs, { Label, UiEntity } from '@dcl/sdk/react-ecs'
|
|
2
|
+
import { VideoPlayer, type Entity } from '@dcl/sdk/ecs'
|
|
3
|
+
import { copyToClipboard } from '~system/RestrictedActions'
|
|
4
|
+
import { VideoControlState } from '../../components'
|
|
5
|
+
import { ensurePresenterRole, getCastRoom, resetStreamKey } from './api'
|
|
6
|
+
import { LIVEKIT_STREAM_SRC, type VideoControls } from './controls'
|
|
7
|
+
import type { VideoIcons } from './icons'
|
|
8
|
+
import { ensureSpeakerPolling, refreshSpeakers } from './speakers'
|
|
9
|
+
import { markCopied, videoTab, wasCopied } from './state'
|
|
10
|
+
import { SpeakerShowcase } from './SpeakerShowcase'
|
|
11
|
+
import { Volume } from './Volume'
|
|
12
|
+
import { Button, COLORS, Column, Divider, ErrorNote, Hint, Row, SubHeader } from './ui'
|
|
13
|
+
|
|
14
|
+
export function DclCast(props: {
|
|
15
|
+
self: Entity
|
|
16
|
+
entity: Entity
|
|
17
|
+
icons: VideoIcons
|
|
18
|
+
controls: VideoControls
|
|
19
|
+
soundDisabled: boolean
|
|
20
|
+
playerAddress: string | undefined
|
|
21
|
+
active: boolean
|
|
22
|
+
}): ReactEcs.JSX.Element {
|
|
23
|
+
const [loading, setLoading] = ReactEcs.useState(videoTab.castRoom === undefined)
|
|
24
|
+
|
|
25
|
+
const fetchRoom = (): void => {
|
|
26
|
+
setLoading(true)
|
|
27
|
+
videoTab.castError = ''
|
|
28
|
+
void (async () => {
|
|
29
|
+
const [error, room] = await getCastRoom()
|
|
30
|
+
if (error !== null) videoTab.castError = error
|
|
31
|
+
else videoTab.castRoom = room
|
|
32
|
+
setLoading(false)
|
|
33
|
+
})()
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
ReactEcs.useEffect(() => {
|
|
37
|
+
ensureSpeakerPolling()
|
|
38
|
+
if (videoTab.castRoom === undefined) fetchRoom()
|
|
39
|
+
else setLoading(false)
|
|
40
|
+
if (props.playerAddress !== undefined) void ensurePresenterRole(props.playerAddress)
|
|
41
|
+
}, [])
|
|
42
|
+
|
|
43
|
+
const room = videoTab.castRoom
|
|
44
|
+
const showSpeakers = videoTab.showSpeakers
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<Column>
|
|
48
|
+
<Row uiTransform={{ justifyContent: 'space-between' }}>
|
|
49
|
+
<SubHeader icon={props.icons.sourceCast} title="DCL Cast" />
|
|
50
|
+
<UiEntity
|
|
51
|
+
uiTransform={{ width: 20, height: 20, display: props.active ? 'flex' : 'none' }}
|
|
52
|
+
uiBackground={{
|
|
53
|
+
textureMode: 'stretch',
|
|
54
|
+
texture: { src: props.icons.star },
|
|
55
|
+
color: COLORS.success
|
|
56
|
+
}}
|
|
57
|
+
/>
|
|
58
|
+
</Row>
|
|
59
|
+
<Hint text="Use a browser-based DCL Cast room to stream camera and screen feeds onto this screen." />
|
|
60
|
+
|
|
61
|
+
{loading ? <Hint text="Loading room…" uiTransform={{ margin: { top: 16 } }} /> : null}
|
|
62
|
+
|
|
63
|
+
{!loading && videoTab.castError !== '' ? (
|
|
64
|
+
<Column uiTransform={{ margin: { top: 12 } }}>
|
|
65
|
+
<ErrorNote icon={props.icons.error} text={videoTab.castError} />
|
|
66
|
+
<Row uiTransform={{ margin: { top: 8 } }}>
|
|
67
|
+
<Button label="Retry" uiTransform={{ width: 'auto' }} onClick={fetchRoom} />
|
|
68
|
+
</Row>
|
|
69
|
+
</Column>
|
|
70
|
+
) : null}
|
|
71
|
+
|
|
72
|
+
{!loading && room !== undefined ? (
|
|
73
|
+
<Column uiTransform={{ margin: { top: 12 } }}>
|
|
74
|
+
<Row uiTransform={{ justifyContent: 'space-between' }}>
|
|
75
|
+
<Column uiTransform={{ width: 'auto' }}>
|
|
76
|
+
<Label
|
|
77
|
+
value="<b>Room ID</b>"
|
|
78
|
+
fontSize={20}
|
|
79
|
+
color={COLORS.white}
|
|
80
|
+
textAlign="middle-left"
|
|
81
|
+
uiTransform={{ height: 26 }}
|
|
82
|
+
/>
|
|
83
|
+
<Hint text={`Expires in ${room.expiresInDays} days`} />
|
|
84
|
+
</Column>
|
|
85
|
+
{props.active ? (
|
|
86
|
+
<Button
|
|
87
|
+
label="Deactivate"
|
|
88
|
+
variant="text"
|
|
89
|
+
uiTransform={{ width: 'auto' }}
|
|
90
|
+
onClick={() => {
|
|
91
|
+
props.controls.setSource('')
|
|
92
|
+
videoTab.activeTrackSid = undefined
|
|
93
|
+
}}
|
|
94
|
+
/>
|
|
95
|
+
) : (
|
|
96
|
+
<Button
|
|
97
|
+
label="Activate"
|
|
98
|
+
variant="success"
|
|
99
|
+
uiTransform={{ width: 'auto' }}
|
|
100
|
+
onClick={() => {
|
|
101
|
+
props.controls.setSource(LIVEKIT_STREAM_SRC)
|
|
102
|
+
videoTab.activeTrackSid = undefined
|
|
103
|
+
videoTab.source = 'dcl-cast'
|
|
104
|
+
}}
|
|
105
|
+
/>
|
|
106
|
+
)}
|
|
107
|
+
</Row>
|
|
108
|
+
|
|
109
|
+
<Divider />
|
|
110
|
+
|
|
111
|
+
<Row uiTransform={{ justifyContent: 'space-between' }}>
|
|
112
|
+
<Column uiTransform={{ width: '62%' }}>
|
|
113
|
+
<Label
|
|
114
|
+
value="<b>Cast speakers</b>"
|
|
115
|
+
fontSize={16}
|
|
116
|
+
color={COLORS.white}
|
|
117
|
+
textAlign="middle-left"
|
|
118
|
+
uiTransform={{ height: 22 }}
|
|
119
|
+
/>
|
|
120
|
+
<Hint text="This link grants streaming access." />
|
|
121
|
+
</Column>
|
|
122
|
+
<Button
|
|
123
|
+
label={wasCopied('stream-link') ? 'Copied!' : 'Copy Link'}
|
|
124
|
+
variant="text"
|
|
125
|
+
iconRight={props.icons.copy}
|
|
126
|
+
uiTransform={{ width: 'auto' }}
|
|
127
|
+
onClick={() => {
|
|
128
|
+
if (room.streamLink === '') return
|
|
129
|
+
void copyToClipboard({ text: room.streamLink })
|
|
130
|
+
markCopied('stream-link')
|
|
131
|
+
}}
|
|
132
|
+
/>
|
|
133
|
+
</Row>
|
|
134
|
+
|
|
135
|
+
<Divider />
|
|
136
|
+
|
|
137
|
+
<Row uiTransform={{ justifyContent: 'space-between' }}>
|
|
138
|
+
<Column uiTransform={{ width: '62%' }}>
|
|
139
|
+
<Label
|
|
140
|
+
value="<b>Viewers</b>"
|
|
141
|
+
fontSize={16}
|
|
142
|
+
color={COLORS.white}
|
|
143
|
+
textAlign="middle-left"
|
|
144
|
+
uiTransform={{ height: 22 }}
|
|
145
|
+
/>
|
|
146
|
+
<Hint text="This link grants viewing access." />
|
|
147
|
+
</Column>
|
|
148
|
+
<Button
|
|
149
|
+
label={wasCopied('watcher-link') ? 'Copied!' : 'Copy Link'}
|
|
150
|
+
variant="text"
|
|
151
|
+
iconRight={props.icons.copy}
|
|
152
|
+
uiTransform={{ width: 'auto' }}
|
|
153
|
+
onClick={() => {
|
|
154
|
+
if (room.watcherLink === '') return
|
|
155
|
+
void copyToClipboard({ text: room.watcherLink })
|
|
156
|
+
markCopied('watcher-link')
|
|
157
|
+
}}
|
|
158
|
+
/>
|
|
159
|
+
</Row>
|
|
160
|
+
|
|
161
|
+
<Volume
|
|
162
|
+
label="Cast controls"
|
|
163
|
+
entity={props.entity}
|
|
164
|
+
icons={props.icons}
|
|
165
|
+
controls={props.controls}
|
|
166
|
+
soundDisabled={props.soundDisabled}
|
|
167
|
+
/>
|
|
168
|
+
|
|
169
|
+
<Row uiTransform={{ margin: { top: 16 }, justifyContent: 'space-between' }}>
|
|
170
|
+
<Button
|
|
171
|
+
label={showSpeakers ? 'Hide Speakers' : 'Speakers'}
|
|
172
|
+
icon={props.icons.star}
|
|
173
|
+
iconRight={showSpeakers ? props.icons.chevronUp : props.icons.chevronDown}
|
|
174
|
+
disabled={!props.active}
|
|
175
|
+
uiTransform={{ width: 'auto' }}
|
|
176
|
+
onClick={() => {
|
|
177
|
+
videoTab.showSpeakers = !showSpeakers
|
|
178
|
+
if (!showSpeakers) refreshSpeakers()
|
|
179
|
+
}}
|
|
180
|
+
/>
|
|
181
|
+
<Button
|
|
182
|
+
label="Reset Room"
|
|
183
|
+
variant="danger"
|
|
184
|
+
uiTransform={{ width: 'auto' }}
|
|
185
|
+
onClick={() => {
|
|
186
|
+
setLoading(true)
|
|
187
|
+
void (async () => {
|
|
188
|
+
const [error, data] = await resetStreamKey()
|
|
189
|
+
if (error !== null) {
|
|
190
|
+
videoTab.castError = error
|
|
191
|
+
setLoading(false)
|
|
192
|
+
return
|
|
193
|
+
}
|
|
194
|
+
const state = VideoControlState.getMutableOrNull(props.self)
|
|
195
|
+
if (state !== null) state.endsAt = data.endsAt
|
|
196
|
+
videoTab.castRoom = undefined
|
|
197
|
+
videoTab.activeTrackSid = undefined
|
|
198
|
+
fetchRoom()
|
|
199
|
+
})()
|
|
200
|
+
}}
|
|
201
|
+
/>
|
|
202
|
+
</Row>
|
|
203
|
+
|
|
204
|
+
{showSpeakers && props.active ? (
|
|
205
|
+
<SpeakerShowcase
|
|
206
|
+
icons={props.icons}
|
|
207
|
+
onSelectTrack={(track) => {
|
|
208
|
+
props.controls.setSource(track.sid)
|
|
209
|
+
videoTab.activeTrackSid = track.sid
|
|
210
|
+
videoTab.source = 'dcl-cast'
|
|
211
|
+
}}
|
|
212
|
+
onAutomatic={() => {
|
|
213
|
+
props.controls.setSource(LIVEKIT_STREAM_SRC)
|
|
214
|
+
videoTab.activeTrackSid = undefined
|
|
215
|
+
}}
|
|
216
|
+
/>
|
|
217
|
+
) : null}
|
|
218
|
+
</Column>
|
|
219
|
+
) : null}
|
|
220
|
+
|
|
221
|
+
{VideoPlayer.getOrNull(props.entity) === null ? (
|
|
222
|
+
<ErrorNote icon={props.icons.error} text="This screen has no VideoPlayer component." />
|
|
223
|
+
) : null}
|
|
224
|
+
</Column>
|
|
225
|
+
)
|
|
226
|
+
}
|