Skip to content

Commit 9f1cf9a

Browse files
authored
feat: notification sound controls (#18)
* feat: notification sound controls * refactor: rewrite new components and utils in ts * refactor: don't use z-index for avatar group component also fixes the issue where the avatars show up in the settings dialog
1 parent 8625d8f commit 9f1cf9a

11 files changed

Lines changed: 377 additions & 214 deletions

frontend/components.d.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ declare module "vue" {
1212
"./src/components/AudioIndicator.vue",
1313
)["default"];
1414
BackgroundSettingsTab: typeof import(
15-
"./src/components/BackgroundSettingsTab.vue",
15+
"./src/components/settings/BackgroundSettingsTab.vue",
1616
)["default"];
1717
ChatNotification: typeof import(
1818
"./src/components/ChatNotification.vue",
@@ -25,7 +25,7 @@ declare module "vue" {
2525
"./src/components/ClickToCopyField.vue",
2626
)["default"];
2727
DeviceSettingsTab: typeof import(
28-
"./src/components/DeviceSettingsTab.vue",
28+
"./src/components/settings/DeviceSettingsTab.vue",
2929
)["default"];
3030
EmojiPicker: typeof import("./src/components/EmojiPicker.vue")["default"];
3131
FloatingControls: typeof import(
@@ -97,6 +97,9 @@ declare module "vue" {
9797
"./src/components/MeetingPreview.vue",
9898
)["default"];
9999
NamePill: typeof import("./src/components/NamePill.vue")["default"];
100+
NotificationSettingsTab: typeof import(
101+
"./src/components/settings/NotificationSettingsTab.vue",
102+
)["default"];
100103
ParticipantAvatarGroup: typeof import(
101104
"./src/components/ParticipantAvatarGroup.vue",
102105
)["default"];
@@ -128,7 +131,10 @@ declare module "vue" {
128131
"./src/components/ScreenShareSidebarParticipantTile.vue",
129132
)["default"];
130133
SettingsDialog: typeof import(
131-
"./src/components/SettingsDialog.vue",
134+
"./src/components/settings/SettingsDialog.vue",
135+
)["default"];
136+
SettingsLayoutBase: typeof import(
137+
"./src/components/settings/SettingsLayoutBase.vue",
132138
)["default"];
133139
SFUDashboard: typeof import("./src/components/SFUDashboard.vue")["default"];
134140
VideoGrid: typeof import("./src/components/VideoGrid.vue")["default"];

frontend/src/components/FloatingControls.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ import { computed, onMounted, onUnmounted, ref, toRefs } from "vue";
244244
import { canScreenShare } from "../utils/device";
245245
import MeetingInfoDialog from "./MeetingInfoDialog.vue";
246246
import ReactionPicker from "./ReactionPicker.vue";
247-
import SettingsDialog from "./SettingsDialog.vue";
247+
import SettingsDialog from "./settings/SettingsDialog.vue";
248248
249249
const props = defineProps({
250250
isChatOpen: {

frontend/src/components/ParticipantAvatarGroup.vue

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,11 @@
77
</p>
88
</div>
99
<div v-else-if="participants.length > 0" class="flex flex-col items-center">
10-
<div class="relative flex mx-auto">
10+
<div class="relative flex mx-auto -space-x-2">
1111
<div
12-
v-for="(participant, index) in displayedParticipants"
12+
v-for="participant in displayedParticipants"
1313
:key="participant.user_id"
14-
:class="[
15-
'relative',
16-
index > 0 ? '-ml-2' : ''
17-
]"
18-
:style="{ zIndex: displayedParticipants.length - index }"
14+
:style="{ zIndex: 0 }"
1915
>
2016
<div class="ring-2 ring-white rounded-full h-10">
2117
<Avatar
@@ -28,7 +24,6 @@
2824
</div>
2925
<div
3026
v-if="extraCount > 0"
31-
class="relative -ml-2"
3227
:style="{ zIndex: 0 }"
3328
>
3429
<div class="ring-2 ring-white rounded-full h-10 w-10 bg-surface-gray-2 flex items-center justify-center text-ink-gray-5 text-base font-semibold">

frontend/src/components/SettingsDialog.vue

Lines changed: 0 additions & 49 deletions
This file was deleted.

frontend/src/components/BackgroundSettingsTab.vue renamed to frontend/src/components/settings/BackgroundSettingsTab.vue

Lines changed: 95 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,115 +1,116 @@
11
<template>
2-
<div class="space-y-6 mt-6">
3-
<div class="">
4-
<p class="text-sm text-gray-600">
5-
Customize your video background with blur effects or virtual backgrounds
6-
</p>
7-
</div>
8-
9-
<!-- Video Preview -->
10-
<div class="flex justify-center mb-4">
11-
<div class="w-full h-auto aspect-video bg-gray-900 rounded-lg overflow-hidden shadow-sm relative">
12-
<video
13-
ref="videoPreviewRef"
14-
autoplay
15-
muted
16-
playsinline
17-
class="w-full h-full object-cover transform scale-x-[-1]"
18-
/>
19-
<div v-if="!previewStream"
20-
class="absolute inset-0 bg-black bg-opacity-20 flex items-center justify-center">
21-
<div v-if="isLoadingPreview" class="text-white text-center">
22-
<lucide-loader class="mx-auto mb-2 w-8 h-8 animate-spin" />
23-
<p class="text-sm">
24-
Loading preview...
25-
</p>
2+
<SettingsLayoutBase
3+
:description="'Customize your video background with blur effects or virtual backgrounds'"
4+
>
5+
<template #title>
6+
Background
7+
</template>
8+
<template #content>
9+
<!-- Video Preview -->
10+
<div class="flex justify-center mb-4">
11+
<div class="w-96 h-auto aspect-video bg-gray-900 rounded-lg overflow-hidden shadow-sm relative">
12+
<video
13+
ref="videoPreviewRef"
14+
autoplay
15+
muted
16+
playsinline
17+
class="w-full h-full object-cover transform scale-x-[-1]"
18+
/>
19+
<div v-if="!previewStream"
20+
class="absolute inset-0 bg-black bg-opacity-20 flex items-center justify-center">
21+
<div v-if="isLoadingPreview" class="text-white text-center">
22+
<lucide-loader class="mx-auto mb-2 w-8 h-8 animate-spin" />
23+
<p class="text-sm">
24+
Loading preview...
25+
</p>
26+
</div>
2627
</div>
2728
</div>
2829
</div>
29-
</div>
30-
31-
<div class="space-y-4">
32-
<!-- Image picker -->
33-
<input ref="fileInputRef" type="file" accept="image/*" class="hidden" @change="handleFileSelect" />
34-
35-
<!-- Background Options -->
36-
<div class="grid grid-cols-4 gap-3">
37-
<div v-for="option in allBackgroundOptions" :key="option.name"
38-
@click="handleBackgroundOptionClick(option)"
39-
class="relative cursor-pointer rounded-lg border overflow-hidden transition-all duration-200 hover:shadow-sm group"
40-
:class="[
41-
selectedBackgroundOption === option.name
42-
? 'border-gray-900 ring-gray-300'
43-
: 'border-gray-200 hover:border-gray-300',
44-
]">
45-
<div class="aspect-video bg-gray-100 relative">
46-
<!-- For blur option -->
47-
<div v-if="option.type === 'blur'" class="absolute inset-0 flex items-center justify-center"
48-
:class="option.name === 'blur-low'
49-
? 'bg-[radial-gradient(circle_at_center,theme(colors.blue.200),theme(colors.blue.300),theme(colors.blue.500))]'
50-
: 'bg-[radial-gradient(circle_at_center,theme(colors.blue.300),theme(colors.blue.400),theme(colors.blue.600))]'">
51-
<lucide-circle-user-round class="w-8 h-8 text-gray-50" />
52-
</div>
53-
54-
<!-- For add custom option -->
55-
<div v-else-if="option.isAddButton"
56-
class="absolute inset-0 bg-gray-100 flex items-center justify-center border-2 border-dashed border-gray-300 hover:border-gray-400 transition-colors">
57-
<lucide-plus class="w-8 h-8 text-gray-400" />
58-
</div>
5930

60-
<!-- For image options -->
61-
<img v-else-if="option.url" :src="option.url" :alt="option.label"
62-
class="w-full h-full object-cover" @error="handleImageError" />
63-
64-
<!-- For none option -->
65-
<div v-else class="absolute inset-0 bg-gray-50 flex items-center justify-center">
66-
<lucide-circle-user-round class="w-8 h-8 text-gray-400" />
67-
</div>
68-
69-
<!-- Selected indicator -->
70-
<div v-if="selectedBackgroundOption === option.name"
71-
class="absolute top-1 right-1 w-5 h-5 bg-gray-900 rounded-full flex items-center justify-center">
72-
<lucide-check class="w-3 h-3 text-white" />
31+
<div class="space-y-4">
32+
<!-- Image picker -->
33+
<input ref="fileInputRef" type="file" accept="image/*" class="hidden" @change="handleFileSelect" />
34+
35+
<!-- Background Options -->
36+
<div class="grid grid-cols-4 gap-3">
37+
<div v-for="option in allBackgroundOptions" :key="option.name"
38+
@click="handleBackgroundOptionClick(option)"
39+
class="relative cursor-pointer rounded-lg border overflow-hidden transition-all duration-200 hover:shadow-sm group"
40+
:class="[
41+
selectedBackgroundOption === option.name
42+
? 'border-gray-900 ring-gray-300'
43+
: 'border-gray-200 hover:border-gray-300',
44+
]">
45+
<div class="aspect-video bg-gray-100 relative">
46+
<!-- For blur option -->
47+
<div v-if="option.type === 'blur'" class="absolute inset-0 flex items-center justify-center"
48+
:class="option.name === 'blur-low'
49+
? 'bg-[radial-gradient(circle_at_center,theme(colors.blue.200),theme(colors.blue.300),theme(colors.blue.500))]'
50+
: 'bg-[radial-gradient(circle_at_center,theme(colors.blue.300),theme(colors.blue.400),theme(colors.blue.600))]'">
51+
<lucide-circle-user-round class="w-8 h-8 text-gray-50" />
52+
</div>
53+
54+
<!-- For add custom option -->
55+
<div v-else-if="option.isAddButton"
56+
class="absolute inset-0 bg-gray-100 flex items-center justify-center border-2 border-dashed border-gray-300 hover:border-gray-400 transition-colors">
57+
<lucide-plus class="w-8 h-8 text-gray-400" />
58+
</div>
59+
60+
<!-- For image options -->
61+
<img v-else-if="option.url" :src="option.url" :alt="option.label"
62+
class="w-full h-full object-cover" @error="handleImageError" />
63+
64+
<!-- For none option -->
65+
<div v-else class="absolute inset-0 bg-gray-50 flex items-center justify-center">
66+
<lucide-circle-user-round class="w-8 h-8 text-gray-400" />
67+
</div>
68+
69+
<!-- Selected indicator -->
70+
<div v-if="selectedBackgroundOption === option.name"
71+
class="absolute top-1 right-1 w-5 h-5 bg-gray-900 rounded-full flex items-center justify-center">
72+
<lucide-check class="w-3 h-3 text-white" />
73+
</div>
74+
75+
<!-- Delete button for custom images -->
76+
<div v-if="option.isCustom" @click.stop="handleDeleteCustomImage(option.name)"
77+
class="absolute top-1 right-1 w-5 h-5 bg-gray-500 rounded-full flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity cursor-pointer hover:bg-gray-600">
78+
<lucide-x class="w-3 h-3 text-white" />
79+
</div>
7380
</div>
7481

75-
<!-- Delete button for custom images -->
76-
<div v-if="option.isCustom" @click.stop="handleDeleteCustomImage(option.name)"
77-
class="absolute top-1 right-1 w-5 h-5 bg-gray-500 rounded-full flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity cursor-pointer hover:bg-gray-600">
78-
<lucide-x class="w-3 h-3 text-white" />
82+
<!-- Label -->
83+
<div class="p-2 bg-white">
84+
<p class="text-sm font-medium text-center text-gray-900 truncate">
85+
{{ option.label }}
86+
</p>
7987
</div>
8088
</div>
81-
82-
<!-- Label -->
83-
<div class="p-2 bg-white">
84-
<p class="text-sm font-medium text-center text-gray-900 truncate">
85-
{{ option.label }}
86-
</p>
87-
</div>
8889
</div>
8990
</div>
90-
</div>
9191

92-
<div class="bg-yellow-50 border border-yellow-200 rounded-lg p-3">
93-
<div class="flex">
94-
<div class="flex-shrink-0">
95-
<lucide-alert-triangle class="h-5 w-5 text-yellow-400" />
96-
</div>
97-
<div class="ml-3">
98-
<p class="text-sm text-yellow-800">
99-
<strong>Performance Warning:</strong> Enabling background effects may slow down your computer,
100-
especially on older devices.
92+
<div class="bg-yellow-50 border border-yellow-200 rounded-lg p-3 mt-4">
93+
<div class="flex">
94+
<div class="flex-shrink-0">
95+
<lucide-alert-triangle class="h-5 w-5 text-yellow-400" />
96+
</div>
97+
<div class="ml-3">
98+
<p class="text-sm text-yellow-800">
99+
<strong>Performance Warning:</strong> Enabling background effects may slow down your computer,
100+
especially on older devices.
101101
</p>
102102
</div>
103103
</div>
104104
</div>
105-
</div>
105+
</template>
106+
</SettingsLayoutBase>
106107
</template>
107108

108109
<script setup>
109110
import { toast } from "frappe-ui";
110111
import { computed, onMounted, onUnmounted, ref, watch } from "vue";
111-
import { useBackgroundEffects } from "../composables/useBackgroundEffects";
112-
import { useMeetingContext } from "../composables/useMeetingContext.js";
112+
import { useBackgroundEffects } from "../../composables/useBackgroundEffects";
113+
import { useMeetingContext } from "../../composables/useMeetingContext.js";
113114
import {
114115
addCustomBackgroundImage,
115116
allBackgroundOptions,
@@ -124,8 +125,9 @@ import {
124125
setBackgroundImageEnabled,
125126
setBlurIntensity,
126127
setSelectedBackgroundImage,
127-
} from "../data/backgroundEffects";
128-
import { selectedCameraId } from "../data/mediaPreferences.js";
128+
} from "../../data/backgroundEffects";
129+
import { selectedCameraId } from "../../data/mediaPreferences.js";
130+
import SettingsLayoutBase from "./SettingsLayoutBase.vue";
129131
130132
const props = defineProps({
131133
isVisible: {

0 commit comments

Comments
 (0)