-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathSharedShockerControlModule.svelte
More file actions
119 lines (105 loc) · 3.43 KB
/
SharedShockerControlModule.svelte
File metadata and controls
119 lines (105 loc) · 3.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
<script lang="ts">
import { ClockFading, Gauge, Pause } from '@lucide/svelte';
import type { SharedShocker } from '$lib/api/internal/v1';
import {
ControlDurationDefault,
ControlDurationProps,
ControlIntensityDefault,
ControlIntensityProps,
} from '$lib/constants/ControlConstants';
import { getConnection } from '$lib/signalr/index.svelte';
import { ControlType } from '$lib/signalr/models/ControlType';
import { serializeControlMessages } from '$lib/signalr/serializers/Control';
import ControlListener from './ControlListener.svelte';
import ActionButtons from './impl/ActionButtons.svelte';
import CircleSlider from './impl/CircleSlider.svelte';
interface Props {
shocker: SharedShocker;
disabled?: boolean;
}
let { shocker, disabled }: Props = $props();
// Limits from API (duration in ms, convert to seconds for display)
const maxIntensity = $derived(shocker.limits.intensity ?? 100);
const maxDurationSeconds = $derived(
shocker.limits.duration ? shocker.limits.duration / 1000 : ControlDurationProps.max
);
// Pause state
const isPaused = $derived(shocker.isPaused);
// Permissions
const permissions = $derived(shocker.permissions);
let intensity = $state(ControlIntensityDefault);
let duration = $state(ControlDurationDefault);
let active = $state<ControlType | null>(null);
// Clamp values to limits
const clampedIntensity = $derived(Math.min(intensity, maxIntensity));
const clampedDuration = $derived(Math.min(duration, maxDurationSeconds));
function ctrl(type: ControlType) {
const conn = getConnection();
if (!conn) return;
serializeControlMessages(conn, [
{
id: shocker.id,
type,
intensity: clampedIntensity,
duration: clampedDuration,
},
]);
}
// Permission check for each control type
const disabledControls = $derived({
[ControlType.Sound]: !permissions.sound,
[ControlType.Vibrate]: !permissions.vibrate,
[ControlType.Shock]: !permissions.shock,
});
</script>
<ControlListener shockerId={shocker.id} bind:active />
<div
class="border-surface-400-500-token flex flex-col items-center justify-center gap-2 overflow-hidden rounded-md border p-2"
class:opacity-50={isPaused}
>
<!-- Title -->
<h2 class="w-full truncate px-4 text-center text-lg font-bold">{shocker.name}</h2>
<div class="flex grow flex-col items-center justify-center">
<!-- Pause indicator -->
{#if isPaused}
<div class="text-destructive flex items-center gap-1 text-sm">
<Pause size={14} />
<span>Paused</span>
</div>
{/if}
<!-- Limits -->
<div class="text-muted-foreground flex gap-3 text-xs">
<span class="flex items-center gap-1" title="Max Intensity">
<Gauge size={14} />
{maxIntensity}%
</span>
<span class="flex items-center gap-1" title="Max Duration">
<ClockFading size={14} />
{maxDurationSeconds}s
</span>
</div>
</div>
<!-- Sliders -->
<div class="flex items-center gap-2">
<CircleSlider
name="Intensity"
bind:value={intensity}
{...ControlIntensityProps}
max={maxIntensity}
/>
<CircleSlider
name="Duration"
bind:value={duration}
{...ControlDurationProps}
max={maxDurationSeconds}
/>
</div>
<!-- Buttons -->
<ActionButtons
{ctrl}
duration={clampedDuration}
{active}
disabled={disabled || isPaused}
{disabledControls}
/>
</div>