From f69f25241bcaff98da18737bf12890ea43d9c9d5 Mon Sep 17 00:00:00 2001 From: MartinSchuhmacher Date: Wed, 18 Dec 2024 10:02:41 +0100 Subject: [PATCH 01/48] rewrite videoconference store as composable for parallel conferences --- .../composables/VideoConference.composable.ts | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 src/modules/feature/board-video-conference-element/composables/VideoConference.composable.ts diff --git a/src/modules/feature/board-video-conference-element/composables/VideoConference.composable.ts b/src/modules/feature/board-video-conference-element/composables/VideoConference.composable.ts new file mode 100644 index 0000000000..8ac25359a8 --- /dev/null +++ b/src/modules/feature/board-video-conference-element/composables/VideoConference.composable.ts @@ -0,0 +1,117 @@ +import { ref, computed } from "vue"; +import { + VideoConferenceApiFactory, + VideoConferenceInfoResponse, + VideoConferenceJoinResponse, + VideoConferenceScope, + VideoConferenceStateResponse, +} from "@/serverApi/v3"; +import { $axios } from "@/utils/api"; +import { + VideoConferenceInfo, + VideoConferenceOptions, + VideoConferenceState, +} from "@/store/types/video-conference"; +import { AxiosResponse } from "axios"; + +const videoConferenceStateMapping: Partial< + Record +> = { + [VideoConferenceStateResponse.Running]: VideoConferenceState.RUNNING, + [VideoConferenceStateResponse.NotStarted]: VideoConferenceState.NOT_STARTED, +}; + +export const useVideoConference = ( + scope: VideoConferenceScope, + scopeId: string +) => { + const videoConferenceApi = VideoConferenceApiFactory(undefined, "v3", $axios); + + const videoConferenceInfo = ref({ + state: VideoConferenceState.NOT_STARTED, + options: { + everyAttendeeJoinsMuted: false, + everybodyJoinsAsModerator: false, + moderatorMustApproveJoinRequests: true, + }, + }); + + const loading = ref(false); + const error = ref(null); + + const isRunning = computed( + () => videoConferenceInfo.value.state === VideoConferenceState.RUNNING + ); + const isWaitingRoomActive = computed( + () => videoConferenceInfo.value.options.moderatorMustApproveJoinRequests + ); + + const fetchVideoConferenceInfo = async () => { + loading.value = true; + try { + const response: AxiosResponse = + await videoConferenceApi.videoConferenceControllerInfo(scope, scopeId); + videoConferenceInfo.value = { + state: + videoConferenceStateMapping[response.data.state] ?? + VideoConferenceState.UNKNOWN, + options: response.data.options, + }; + } catch (err) { + error.value = err; + } finally { + loading.value = false; + } + }; + + const startVideoConference = async ( + options: VideoConferenceOptions, + logoutUrl?: string + ) => { + loading.value = true; + try { + await videoConferenceApi.videoConferenceControllerStart(scope, scopeId, { + ...options, + logoutUrl, + }); + + videoConferenceInfo.value = { + state: VideoConferenceState.RUNNING, + options, + }; + } catch (err) { + error.value = err; + } finally { + loading.value = false; + } + }; + + const joinVideoConference = async (): Promise => { + loading.value = true; + try { + const response: AxiosResponse = + await videoConferenceApi.videoConferenceControllerJoin(scope, scopeId); + return response.data.url; + } catch (err) { + error.value = err; + } finally { + loading.value = false; + } + }; + + const resetError = () => { + error.value = null; + }; + + return { + videoConferenceInfo, + loading, + error, + isRunning, + isWaitingRoomActive, + fetchVideoConferenceInfo, + startVideoConference, + joinVideoConference, + resetError, + }; +}; From 827effd669e54c06de780953841a08b157a0e814 Mon Sep 17 00:00:00 2001 From: MartinSchuhmacher Date: Wed, 18 Dec 2024 10:03:02 +0100 Subject: [PATCH 02/48] rewrite VC element to fit new composable usage --- .../VideoConferenceContentElement.vue | 243 ++++++------------ 1 file changed, 78 insertions(+), 165 deletions(-) diff --git a/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElement.vue b/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElement.vue index dcb52f0873..6a96c8a302 100644 --- a/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElement.vue +++ b/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElement.vue @@ -19,7 +19,7 @@ :is-running="isRunning" :is-edit-mode="isEditMode" @click="onContentClick" - @refresh="onRefresh" + @refresh="fetchVideoConferenceInfo" > @@ -63,7 +63,7 @@ {{ t("common.labels.close") }} @@ -88,7 +88,7 @@ {{ t("common.actions.create") }} @@ -142,15 +144,17 @@ From 964d9c5e6c91eff155ecf90d11286db27464f0e4 Mon Sep 17 00:00:00 2001 From: MartinSchuhmacher Date: Wed, 18 Dec 2024 10:03:24 +0100 Subject: [PATCH 03/48] rewrite unit tests to summarize setups --- .../VideoConferenceContentElement.unit.ts | 226 +++++++----------- 1 file changed, 80 insertions(+), 146 deletions(-) diff --git a/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElement.unit.ts b/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElement.unit.ts index 991d498b12..5619b96bd2 100644 --- a/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElement.unit.ts +++ b/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElement.unit.ts @@ -197,11 +197,10 @@ describe("VideoConferenceContentElement", () => { describe("when video conference element is displayed", () => { describe("and content title is undefined", () => { + const { wrapper } = setupWrapper({ + isEditMode: false, + }); it("should not render display of video conference content", () => { - const { wrapper } = setupWrapper({ - isEditMode: false, - }); - const videoConferenceElementDisplay = wrapper.findComponent( VideoConferenceContentElementDisplay ); @@ -210,10 +209,6 @@ describe("VideoConferenceContentElement", () => { }); it("should not render video conference element menu", () => { - const { wrapper } = setupWrapper({ - isEditMode: false, - }); - const videoConferenceElementMenu = wrapper.findComponent(BoardMenu); expect(videoConferenceElementMenu.exists()).toBe(false); @@ -221,14 +216,13 @@ describe("VideoConferenceContentElement", () => { }); describe("and content title is defined", () => { + const videoConferenceElementContent = + videoConferenceElementContentFactory.build({ title: "test-title" }); + const { wrapper, element } = setupWrapper({ + content: videoConferenceElementContent, + isEditMode: false, + }); it("should render display of video conference content with correct props", () => { - const videoConferenceElementContent = - videoConferenceElementContentFactory.build({ title: "test-title" }); - const { wrapper, element } = setupWrapper({ - content: videoConferenceElementContent, - isEditMode: false, - }); - const videoConferenceElementDisplay = wrapper.findComponent( VideoConferenceContentElementDisplay ); @@ -245,13 +239,6 @@ describe("VideoConferenceContentElement", () => { }); it("should have the correct aria-label", () => { - const videoConferenceElementContent = - videoConferenceElementContentFactory.build(); - const { wrapper } = setupWrapper({ - content: videoConferenceElementContent, - isEditMode: false, - }); - const videoConferenceElement = wrapper.findComponent( '[data-testid="video-conference-element"]' ); @@ -262,16 +249,15 @@ describe("VideoConferenceContentElement", () => { }); describe("and element is in edit mode", () => { + const videoConferenceElementContent = + videoConferenceElementContentFactory.build(); + const { wrapper } = setupWrapper({ + content: videoConferenceElementContent, + isEditMode: true, + }); it.each(["up", "down"])( "should 'emit move-keyboard:edit' when arrow key %s is pressed", async (key) => { - const videoConferenceElementContent = - videoConferenceElementContentFactory.build(); - const { wrapper } = setupWrapper({ - content: videoConferenceElementContent, - isEditMode: true, - }); - const videoConferenceElement = wrapper.findComponent( '[data-testid="video-conference-element"]' ); @@ -284,16 +270,15 @@ describe("VideoConferenceContentElement", () => { }); describe("and element is in view mode", () => { + const videoConferenceElementContent = + videoConferenceElementContentFactory.build(); + const { wrapper } = setupWrapper({ + content: videoConferenceElementContent, + isEditMode: false, + }); it.each(["up", "down"])( "should not 'emit move-keyboard:edit' when arrow key %s is pressed", async (key) => { - const videoConferenceElementContent = - videoConferenceElementContentFactory.build(); - const { wrapper } = setupWrapper({ - content: videoConferenceElementContent, - isEditMode: false, - }); - const videoConferenceElement = wrapper.findComponent( '[data-testid="video-conference-element"]' ); @@ -306,27 +291,19 @@ describe("VideoConferenceContentElement", () => { }); describe("video conference element menu", () => { + const videoConferenceElementContent = + videoConferenceElementContentFactory.build(); + const { wrapper } = setupWrapper({ + content: videoConferenceElementContent, + isEditMode: true, + }); it("should render video conference element menu", () => { - const videoConferenceElementContent = - videoConferenceElementContentFactory.build(); - const { wrapper } = setupWrapper({ - content: videoConferenceElementContent, - isEditMode: true, - }); - const videoConferenceElementMenu = wrapper.findComponent(BoardMenu); expect(videoConferenceElementMenu.exists()).toBe(true); }); it("should emit 'move-down:edit' event when move down menu item is clicked", async () => { - const videoConferenceElementContent = - videoConferenceElementContentFactory.build(); - const { wrapper } = setupWrapper({ - content: videoConferenceElementContent, - isEditMode: true, - }); - const menuItem = wrapper.findComponent(BoardMenuActionMoveDown); await menuItem.trigger("click"); @@ -334,13 +311,6 @@ describe("VideoConferenceContentElement", () => { }); it("should emit 'move-up:edit' event when move up menu item is clicked", async () => { - const videoConferenceElementContent = - videoConferenceElementContentFactory.build(); - const { wrapper } = setupWrapper({ - content: videoConferenceElementContent, - isEditMode: true, - }); - const menuItem = wrapper.findComponent(BoardMenuActionMoveUp); await menuItem.trigger("click"); @@ -348,13 +318,6 @@ describe("VideoConferenceContentElement", () => { }); it("should emit 'delete:element' event when delete menu item is clicked", async () => { - const videoConferenceElementContent = - videoConferenceElementContentFactory.build(); - const { wrapper } = setupWrapper({ - content: videoConferenceElementContent, - isEditMode: true, - }); - const menuItem = wrapper.findComponent(BoardMenuActionDelete); await menuItem.trigger("click"); @@ -364,12 +327,11 @@ describe("VideoConferenceContentElement", () => { describe("onElementClick", () => { describe("and video conference is not running", () => { + const { wrapper } = setupWrapper({ + content: videoConferenceElementContentFactory.build(), + isEditMode: false, + }); it("should open the configuration dialog", async () => { - const { wrapper } = setupWrapper({ - content: videoConferenceElementContentFactory.build(), - isEditMode: false, - }); - const videoConferenceElementDisplay = wrapper.findComponent( VideoConferenceContentElementDisplay ); @@ -384,23 +346,22 @@ describe("VideoConferenceContentElement", () => { }); describe("and video conference is running", () => { - it("should call joinVideoConference", async () => { - const { element, videoConferenceModule, wrapper } = setupWrapper({ - content: videoConferenceElementContentFactory.build(), - isEditMode: false, - videoConferenceModuleGetter: { - getVideoConferenceInfo: { - state: VideoConferenceState.RUNNING, - options: { - everyAttendeeJoinsMuted: false, - moderatorMustApproveJoinRequests: false, - everybodyJoinsAsModerator: false, - }, + const { element, videoConferenceModule, wrapper } = setupWrapper({ + content: videoConferenceElementContentFactory.build(), + isEditMode: false, + videoConferenceModuleGetter: { + getVideoConferenceInfo: { + state: VideoConferenceState.RUNNING, + options: { + everyAttendeeJoinsMuted: false, + moderatorMustApproveJoinRequests: false, + everybodyJoinsAsModerator: false, }, - getLoading: true, }, - }); - + getLoading: true, + }, + }); + it("should call joinVideoConference", async () => { const videoConferenceElementDisplay = wrapper.findComponent( VideoConferenceContentElementDisplay ); @@ -417,12 +378,11 @@ describe("VideoConferenceContentElement", () => { }); describe("onRefresh", () => { + const { element, videoConferenceModule, wrapper } = setupWrapper({ + content: videoConferenceElementContentFactory.build(), + isEditMode: false, + }); it("should call fetchVideoConferenceInfo", async () => { - const { element, videoConferenceModule, wrapper } = setupWrapper({ - content: videoConferenceElementContentFactory.build(), - isEditMode: false, - }); - const videoConferenceElementDisplay = wrapper.findComponent( VideoConferenceContentElementDisplay ); @@ -441,11 +401,10 @@ describe("VideoConferenceContentElement", () => { describe("when video conference element is being created", () => { describe("and no title was entered", () => { + const { wrapper } = setupWrapper({ + isEditMode: false, + }); it("should hide video conference element in view mode", () => { - const { wrapper } = setupWrapper({ - isEditMode: false, - }); - const videoConferenceElement = wrapper.findComponent( '[data-testid="video-conference-element"]' ); @@ -454,10 +413,6 @@ describe("VideoConferenceContentElement", () => { }); it("should not render video conference element menu in view mode", () => { - const { wrapper } = setupWrapper({ - isEditMode: false, - }); - const videoConferenceElementMenu = wrapper.findComponent(BoardMenu); expect(videoConferenceElementMenu.exists()).toBe(false); @@ -465,9 +420,8 @@ describe("VideoConferenceContentElement", () => { }); describe("and element is in edit mode", () => { + const { wrapper } = setupWrapper({ isEditMode: true }); it("should render VideoConferenceContentElementCreate component", () => { - const { wrapper } = setupWrapper({ isEditMode: true }); - const videoConferenceCreateComponent = wrapper.findComponent( VideoConferenceContentElementCreate ); @@ -478,10 +432,6 @@ describe("VideoConferenceContentElement", () => { it.each(["up", "down"])( "should not 'emit move-keyboard:edit' when arrow key %s is pressed", async (key) => { - const { wrapper } = setupWrapper({ - isEditMode: true, - }); - const videoConferenceElement = wrapper.findComponent( '[data-testid="video-conference-element"]' ); @@ -493,21 +443,16 @@ describe("VideoConferenceContentElement", () => { ); describe("video conference element menu", () => { + const { wrapper } = setupWrapper({ + isEditMode: true, + }); it("should render video conference element menu", () => { - const { wrapper } = setupWrapper({ - isEditMode: true, - }); - const videoConferenceElementMenu = wrapper.findComponent(BoardMenu); expect(videoConferenceElementMenu.exists()).toBe(true); }); it("should emit 'move-down:edit' event when move down menu item is clicked", async () => { - const { wrapper } = setupWrapper({ - isEditMode: true, - }); - const menuItem = wrapper.findComponent(BoardMenuActionMoveDown); await menuItem.trigger("click"); @@ -515,10 +460,6 @@ describe("VideoConferenceContentElement", () => { }); it("should emit 'move-up:edit' event when move up menu item is clicked", async () => { - const { wrapper } = setupWrapper({ - isEditMode: true, - }); - const menuItem = wrapper.findComponent(BoardMenuActionMoveUp); await menuItem.trigger("click"); @@ -526,10 +467,6 @@ describe("VideoConferenceContentElement", () => { }); it("should emit 'delete:element' event when delete menu item is clicked", async () => { - const { wrapper } = setupWrapper({ - isEditMode: true, - }); - const menuItem = wrapper.findComponent(BoardMenuActionDelete); await menuItem.trigger("click"); @@ -540,15 +477,14 @@ describe("VideoConferenceContentElement", () => { describe("onCreateTitle", () => { describe("and title was provided", () => { + const videoConferenceTitle = "Very specific vc title"; + const { wrapper } = setupWrapper({ + content: videoConferenceElementContentFactory.build({ + title: videoConferenceTitle, + }), + isEditMode: false, + }); it("should display the title ", async () => { - const videoConferenceTitle = "Very specific vc title"; - const { wrapper } = setupWrapper({ - content: videoConferenceElementContentFactory.build({ - title: videoConferenceTitle, - }), - isEditMode: false, - }); - expect(wrapper.html()).toEqual( expect.stringContaining(videoConferenceTitle) ); @@ -558,18 +494,17 @@ describe("VideoConferenceContentElement", () => { }); describe("when a videoconference is started or joined", () => { describe("and an error occurs", () => { + const error = jest.fn(() => { + throw new Error(); + }); + const { wrapper } = setupWrapper({ + content: videoConferenceElementContentFactory.build(), + isEditMode: false, + videoConferenceModuleGetter: { + getError: error, + }, + }); it("should display an error dialog", async () => { - const error = jest.fn(() => { - throw new Error(); - }); - const { wrapper } = setupWrapper({ - content: videoConferenceElementContentFactory.build(), - isEditMode: false, - videoConferenceModuleGetter: { - getError: error, - }, - }); - const videoConferenceElement = wrapper.findComponent( '[data-testid="video-conference-element"]' ); @@ -584,15 +519,14 @@ describe("VideoConferenceContentElement", () => { }); describe("and no error occurs", () => { + const { wrapper } = setupWrapper({ + content: videoConferenceElementContentFactory.build(), + isEditMode: false, + videoConferenceModuleGetter: { + getError: null, + }, + }); it("should not display an error dialog", async () => { - const { wrapper } = setupWrapper({ - content: videoConferenceElementContentFactory.build(), - isEditMode: false, - videoConferenceModuleGetter: { - getError: null, - }, - }); - const videoConferenceElement = wrapper.findComponent( '[data-testid="video-conference-element"]' ); From a10cd70ef045dfb137be2f5577a3084f22841520 Mon Sep 17 00:00:00 2001 From: MartinSchuhmacher Date: Wed, 18 Dec 2024 11:13:48 +0100 Subject: [PATCH 04/48] separating ConfigurationDialog into separate component --- .../VideoConferenceConfigurationDialog.vue | 111 ++++++++++++++++++ .../VideoConferenceContentElement.vue | 74 ++---------- 2 files changed, 118 insertions(+), 67 deletions(-) create mode 100644 src/modules/feature/board-video-conference-element/components/VideoConferenceConfigurationDialog.vue diff --git a/src/modules/feature/board-video-conference-element/components/VideoConferenceConfigurationDialog.vue b/src/modules/feature/board-video-conference-element/components/VideoConferenceConfigurationDialog.vue new file mode 100644 index 0000000000..4ff5071b54 --- /dev/null +++ b/src/modules/feature/board-video-conference-element/components/VideoConferenceConfigurationDialog.vue @@ -0,0 +1,111 @@ + + + diff --git a/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElement.vue b/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElement.vue index 6a96c8a302..ecdb423a3e 100644 --- a/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElement.vue +++ b/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElement.vue @@ -72,73 +72,12 @@ - - - -

- {{ t("pages.common.tools.configureVideoconferenceDialog.title") }} -

-
- - - - - - - - - {{ t("common.actions.cancel") }} - - - {{ t("common.actions.create") }} - - -
-
+ @@ -155,6 +94,7 @@ import { import { useI18n } from "vue-i18n"; import VideoConferenceContentElementCreate from "./VideoConferenceContentElementCreate.vue"; import VideoConferenceContentElementDisplay from "./VideoConferenceContentElementDisplay.vue"; +import VideoConferenceConfigurationDialog from "./VideoConferenceConfigurationDialog.vue"; import { BoardMenu, BoardMenuActionDelete, From 96f3ca4b3f36241de34ee52e7826fd98dc9d1b37 Mon Sep 17 00:00:00 2001 From: MartinSchuhmacher Date: Wed, 18 Dec 2024 11:59:53 +0100 Subject: [PATCH 05/48] approaching unit tests for VideoConferenceConfigurationDialog --- ...VideoConferenceConfigurationDialog.unit.ts | 97 +++++++++++++++++++ .../VideoConferenceConfigurationDialog.vue | 4 +- 2 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 src/modules/feature/board-video-conference-element/components/VideoConferenceConfigurationDialog.unit.ts diff --git a/src/modules/feature/board-video-conference-element/components/VideoConferenceConfigurationDialog.unit.ts b/src/modules/feature/board-video-conference-element/components/VideoConferenceConfigurationDialog.unit.ts new file mode 100644 index 0000000000..d8282aeee0 --- /dev/null +++ b/src/modules/feature/board-video-conference-element/components/VideoConferenceConfigurationDialog.unit.ts @@ -0,0 +1,97 @@ +import { mount, VueWrapper } from "@vue/test-utils"; +import { + createTestingI18n, + createTestingVuetify, +} from "@@/tests/test-utils/setup"; +import VideoConferenceConfigurationDialog from "./VideoConferenceConfigurationDialog.vue"; +import { ref } from "vue"; + +const defaultOptions = ref({ + everyAttendeeJoinsMuted: false, + moderatorMustApproveJoinRequests: true, + everybodyJoinsAsModerator: false, +}); + +describe("VideoConferenceConfigurationDialog", () => { + const setup = (props: { isOpen: boolean }) => { + const wrapper: VueWrapper = mount(VideoConferenceConfigurationDialog, { + global: { + plugins: [createTestingVuetify(), createTestingI18n()], + }, + props: { + options: defaultOptions.value, + ...props, + }, + attachTo: document.body, + }); + + return wrapper; + }; + + describe("when component is mounted", () => { + it("should render in the DOM", () => { + const wrapper = setup({ isOpen: true }); + expect(wrapper).toBeDefined(); + }); + + it("should open the dialog when isOpen is true", async () => { + const wrapper = setup({ isOpen: true }); + await wrapper.vm.$nextTick(); + const dialog = wrapper.find( + "[data-testid='videoconference-config-dialog']" + ); + expect(dialog.exists()).toBe(true); + }); + + it("should not render the dialog when isOpen is false", () => { + const wrapper = setup({ isOpen: false }); + const dialog = wrapper.findComponent( + "[data-testid='videoconference-config-dialog']" + ); + expect(dialog.exists()).toBe(false); + }); + + it("should emit 'close' when cancel button is clicked", async () => { + const wrapper = setup({ isOpen: true }); + const cancelButton = wrapper.findComponent( + "[data-testid='dialog-cancel']" + ); + await cancelButton.trigger("click"); + expect(wrapper.emitted()).toHaveProperty("close"); + }); + + it("should emit 'start-video-conference' when create button is clicked", async () => { + const wrapper = setup({ isOpen: true }); + const createButton = wrapper.find("[data-testid='dialog-create']"); + await createButton.trigger("click"); + expect(wrapper.emitted()).toHaveProperty("start-video-conference"); + }); + + it("should toggle everyAttendeeJoinsMuted option when checkbox is clicked", async () => { + const wrapper = setup({ isOpen: true }); + const checkbox = wrapper.find( + "[data-testid='every-attendee-joins-muted']" + ); + await checkbox.setValue(true); + expect(defaultOptions.value.everyAttendeeJoinsMuted).toBe(true); + }); + + it("should toggle moderatorMustApproveJoinRequests option when checkbox is clicked", async () => { + const wrapper = setup({ isOpen: true }); + const checkbox = wrapper.find( + "[data-testid='moderator-must-approve-join-requests']" + ); + await checkbox.setValue(false); + expect(defaultOptions.value.moderatorMustApproveJoinRequests).toBe(false); + }); + + it("should toggle everybodyJoinsAsModerator option when checkbox is clicked", async () => { + const wrapper = setup({ isOpen: true }); + const checkbox = wrapper.find( + "[data-testid='everybody-joins-as-moderator']" + ); + await checkbox.setValue(true); + expect(defaultOptions.value.everybodyJoinsAsModerator).toBe(true); + }); + }); +}); diff --git a/src/modules/feature/board-video-conference-element/components/VideoConferenceConfigurationDialog.vue b/src/modules/feature/board-video-conference-element/components/VideoConferenceConfigurationDialog.vue index 4ff5071b54..fe93720ec6 100644 --- a/src/modules/feature/board-video-conference-element/components/VideoConferenceConfigurationDialog.vue +++ b/src/modules/feature/board-video-conference-element/components/VideoConferenceConfigurationDialog.vue @@ -83,6 +83,8 @@ const isOpen: ModelRef = defineModel("isOpen", { required: true, }); +const { t } = useI18n(); + const emit = defineEmits(["close", "update:options", "start-video-conference"]); const localOptions = ref({ @@ -106,6 +108,4 @@ watch( }, { deep: true } ); - -const { t } = useI18n(); From cdec9b7bc77956a2d607c2b1d769a6b4a9e42e20 Mon Sep 17 00:00:00 2001 From: MartinSchuhmacher Date: Wed, 18 Dec 2024 14:04:29 +0100 Subject: [PATCH 06/48] pushing current test WIP for configuration dialog component --- ...VideoConferenceConfigurationDialog.unit.ts | 45 ++++++++++++------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/src/modules/feature/board-video-conference-element/components/VideoConferenceConfigurationDialog.unit.ts b/src/modules/feature/board-video-conference-element/components/VideoConferenceConfigurationDialog.unit.ts index d8282aeee0..c61e33cc6d 100644 --- a/src/modules/feature/board-video-conference-element/components/VideoConferenceConfigurationDialog.unit.ts +++ b/src/modules/feature/board-video-conference-element/components/VideoConferenceConfigurationDialog.unit.ts @@ -5,8 +5,10 @@ import { } from "@@/tests/test-utils/setup"; import VideoConferenceConfigurationDialog from "./VideoConferenceConfigurationDialog.vue"; import { ref } from "vue"; +import { VDialog } from "vuetify/lib/components/index.mjs"; +import { VideoConferenceOptions } from "@/store/types/video-conference"; -const defaultOptions = ref({ +const defaultOptions = ref({ everyAttendeeJoinsMuted: false, moderatorMustApproveJoinRequests: true, everybodyJoinsAsModerator: false, @@ -34,26 +36,24 @@ describe("VideoConferenceConfigurationDialog", () => { expect(wrapper).toBeDefined(); }); - it("should open the dialog when isOpen is true", async () => { + it("should open the dialog when isOpen is true", /* async */ () => { const wrapper = setup({ isOpen: true }); - await wrapper.vm.$nextTick(); - const dialog = wrapper.find( - "[data-testid='videoconference-config-dialog']" - ); - expect(dialog.exists()).toBe(true); + const dialog = wrapper.getComponent(VDialog); + const isOpen = dialog.props().modelValue; + expect(isOpen).toBe(true); }); it("should not render the dialog when isOpen is false", () => { const wrapper = setup({ isOpen: false }); - const dialog = wrapper.findComponent( - "[data-testid='videoconference-config-dialog']" - ); - expect(dialog.exists()).toBe(false); + const dialog = wrapper.getComponent(VDialog); + const isOpen = dialog.props().modelValue; + expect(isOpen).toBe(false); }); it("should emit 'close' when cancel button is clicked", async () => { const wrapper = setup({ isOpen: true }); - const cancelButton = wrapper.findComponent( + const dialog = wrapper.getComponent(VDialog); + const cancelButton = dialog.findComponent( "[data-testid='dialog-cancel']" ); await cancelButton.trigger("click"); @@ -62,23 +62,33 @@ describe("VideoConferenceConfigurationDialog", () => { it("should emit 'start-video-conference' when create button is clicked", async () => { const wrapper = setup({ isOpen: true }); - const createButton = wrapper.find("[data-testid='dialog-create']"); + const dialog = wrapper.getComponent(VDialog); + const createButton = dialog.findComponent( + "[data-testid='dialog-create']" + ); await createButton.trigger("click"); expect(wrapper.emitted()).toHaveProperty("start-video-conference"); }); it("should toggle everyAttendeeJoinsMuted option when checkbox is clicked", async () => { const wrapper = setup({ isOpen: true }); - const checkbox = wrapper.find( + + const dialog = wrapper.getComponent(VDialog); + const checkbox = dialog.findComponent( "[data-testid='every-attendee-joins-muted']" ); await checkbox.setValue(true); - expect(defaultOptions.value.everyAttendeeJoinsMuted).toBe(true); + const emitted = wrapper.emitted(); + expect(emitted).toHaveLength(1); + const emittedValue = emitted[0][0]; + // from here on currently not working + // expect(emittedValue.everyAttendeeJoinsMuted).toBe(true); }); it("should toggle moderatorMustApproveJoinRequests option when checkbox is clicked", async () => { const wrapper = setup({ isOpen: true }); - const checkbox = wrapper.find( + const dialog = wrapper.getComponent(VDialog); + const checkbox = dialog.findComponent( "[data-testid='moderator-must-approve-join-requests']" ); await checkbox.setValue(false); @@ -87,7 +97,8 @@ describe("VideoConferenceConfigurationDialog", () => { it("should toggle everybodyJoinsAsModerator option when checkbox is clicked", async () => { const wrapper = setup({ isOpen: true }); - const checkbox = wrapper.find( + const dialog = wrapper.getComponent(VDialog); + const checkbox = dialog.findComponent( "[data-testid='everybody-joins-as-moderator']" ); await checkbox.setValue(true); From 1d5e6d360d263dd445bc72126581a8cd0b938c65 Mon Sep 17 00:00:00 2001 From: Odalys Adam Date: Wed, 18 Dec 2024 15:04:52 +0100 Subject: [PATCH 07/48] use outlined video icon --- src/components/icons/material/index.ts | 4 ++-- .../components/VideoConferenceContentElementDisplay.vue | 4 ++-- .../feature/board/shared/AddElementDialog.composable.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/icons/material/index.ts b/src/components/icons/material/index.ts index 2bb64c1a26..1d838ad28f 100644 --- a/src/components/icons/material/index.ts +++ b/src/components/icons/material/index.ts @@ -136,7 +136,7 @@ import { mdiTune, mdiUndo, mdiUndoVariant, - mdiVideo, + mdiVideoOutline, mdiViewAgendaOutline, mdiViewDashboard, mdiViewDashboardOutline, @@ -283,7 +283,7 @@ export { mdiTune, mdiUndo, mdiUndoVariant, - mdiVideo, + mdiVideoOutline, mdiViewAgendaOutline, mdiViewDashboard, mdiViewDashboardOutline, diff --git a/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElementDisplay.vue b/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElementDisplay.vue index e863102603..b0d24fad32 100644 --- a/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElementDisplay.vue +++ b/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElementDisplay.vue @@ -2,7 +2,7 @@
import image from "@/assets/img/videoConference.svg"; import { computed, ref } from "vue"; -import { mdiVideo } from "@icons/material"; +import { mdiVideoOutline } from "@icons/material"; import { ContentElementBar } from "@ui-board"; import { injectStrict } from "@/utils/inject"; import { useDisplay } from "vuetify"; diff --git a/src/modules/feature/board/shared/AddElementDialog.composable.ts b/src/modules/feature/board/shared/AddElementDialog.composable.ts index 41b07a8a7d..e74b1a8432 100644 --- a/src/modules/feature/board/shared/AddElementDialog.composable.ts +++ b/src/modules/feature/board/shared/AddElementDialog.composable.ts @@ -10,7 +10,7 @@ import { mdiPuzzleOutline, mdiTextBoxEditOutline, mdiTrayArrowUp, - mdiVideo, + mdiVideoOutline, } from "@icons/material"; import { useBoardNotifier } from "@util-board"; import { useI18n } from "vue-i18n"; @@ -161,7 +161,7 @@ export const useAddElementDialog = ( if (envConfigModule.getEnv.FEATURE_COLUMN_BOARD_VIDEOCONFERENCE_ENABLED) { options.push({ - icon: mdiVideo, + icon: mdiVideoOutline, label: t( "components.elementTypeSelection.elements.videoConferenceElement.subtitle" ), From e4064d884af90f9abf379d43fa02ae2ec297b647 Mon Sep 17 00:00:00 2001 From: Odalys Adam Date: Wed, 18 Dec 2024 15:34:56 +0100 Subject: [PATCH 08/48] adjust pulse animation & pulsate 5 times --- .../VideoConferenceContentElementDisplay.vue | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElementDisplay.vue b/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElementDisplay.vue index b0d24fad32..b6b4c4d017 100644 --- a/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElementDisplay.vue +++ b/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElementDisplay.vue @@ -189,21 +189,21 @@ $pulseIconColor: #15ba97; margin: 10px; box-shadow: 0 0 0 0 $pulseIconColor; transform: scale(1); - animation: pulse 1.5s infinite; + animation: pulse 1.5s 5; @keyframes pulse { 0% { + transform: scale(1); + box-shadow: 0 0 0 0 rgba(0, 0, 0, 0); + } + 30% { transform: scale(0.95); box-shadow: 0 0 0 0 $pulseIconColor; } - 70% { + 100% { transform: scale(1); box-shadow: 0 0 0 10px rgba(0, 0, 0, 0); } - 100% { - transform: scale(0.95); - box-shadow: 0 0 0 0 rgba(0, 0, 0, 0); - } } @media (prefers-reduced-motion: reduce) { From ac2a7f7c1bd9dd7ca046ed12708ab573d47d9bb0 Mon Sep 17 00:00:00 2001 From: MartinSchuhmacher Date: Thu, 19 Dec 2024 11:35:39 +0100 Subject: [PATCH 09/48] fixing and adding tests for VideoConferenceConfigurationDialog --- ...VideoConferenceConfigurationDialog.unit.ts | 37 +++++++++++++++---- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/src/modules/feature/board-video-conference-element/components/VideoConferenceConfigurationDialog.unit.ts b/src/modules/feature/board-video-conference-element/components/VideoConferenceConfigurationDialog.unit.ts index c61e33cc6d..dc579e20c9 100644 --- a/src/modules/feature/board-video-conference-element/components/VideoConferenceConfigurationDialog.unit.ts +++ b/src/modules/feature/board-video-conference-element/components/VideoConferenceConfigurationDialog.unit.ts @@ -10,8 +10,8 @@ import { VideoConferenceOptions } from "@/store/types/video-conference"; const defaultOptions = ref({ everyAttendeeJoinsMuted: false, - moderatorMustApproveJoinRequests: true, everybodyJoinsAsModerator: false, + moderatorMustApproveJoinRequests: true, }); describe("VideoConferenceConfigurationDialog", () => { @@ -79,10 +79,15 @@ describe("VideoConferenceConfigurationDialog", () => { ); await checkbox.setValue(true); const emitted = wrapper.emitted(); - expect(emitted).toHaveLength(1); - const emittedValue = emitted[0][0]; - // from here on currently not working - // expect(emittedValue.everyAttendeeJoinsMuted).toBe(true); + + const expectedObject = { + everyAttendeeJoinsMuted: true, + everybodyJoinsAsModerator: false, + moderatorMustApproveJoinRequests: true, + }; + + expect(emitted["update:options"]).toHaveLength(1); + expect(emitted["update:options"]).toStrictEqual([[expectedObject]]); }); it("should toggle moderatorMustApproveJoinRequests option when checkbox is clicked", async () => { @@ -92,7 +97,16 @@ describe("VideoConferenceConfigurationDialog", () => { "[data-testid='moderator-must-approve-join-requests']" ); await checkbox.setValue(false); - expect(defaultOptions.value.moderatorMustApproveJoinRequests).toBe(false); + const emitted = wrapper.emitted(); + + const expectedObject = { + everyAttendeeJoinsMuted: false, + everybodyJoinsAsModerator: false, + moderatorMustApproveJoinRequests: false, + }; + + expect(emitted["update:options"]).toHaveLength(1); + expect(emitted["update:options"]).toStrictEqual([[expectedObject]]); }); it("should toggle everybodyJoinsAsModerator option when checkbox is clicked", async () => { @@ -102,7 +116,16 @@ describe("VideoConferenceConfigurationDialog", () => { "[data-testid='everybody-joins-as-moderator']" ); await checkbox.setValue(true); - expect(defaultOptions.value.everybodyJoinsAsModerator).toBe(true); + const emitted = wrapper.emitted(); + + const expectedObject = { + everyAttendeeJoinsMuted: false, + everybodyJoinsAsModerator: true, + moderatorMustApproveJoinRequests: true, + }; + + expect(emitted["update:options"]).toHaveLength(1); + expect(emitted["update:options"]).toStrictEqual([[expectedObject]]); }); }); }); From d6b50b934524b3a9bcaae7f562d3bf0035fa9d2b Mon Sep 17 00:00:00 2001 From: MartinSchuhmacher Date: Thu, 19 Dec 2024 14:37:50 +0100 Subject: [PATCH 10/48] adding note for deep watcher in configuration dialog --- .../components/VideoConferenceConfigurationDialog.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/feature/board-video-conference-element/components/VideoConferenceConfigurationDialog.vue b/src/modules/feature/board-video-conference-element/components/VideoConferenceConfigurationDialog.vue index fe93720ec6..9fcb4a5684 100644 --- a/src/modules/feature/board-video-conference-element/components/VideoConferenceConfigurationDialog.vue +++ b/src/modules/feature/board-video-conference-element/components/VideoConferenceConfigurationDialog.vue @@ -106,6 +106,7 @@ watch( (newLocalOptions) => { emit("update:options", newLocalOptions); }, + // "deep" might be not necessary here as the deep watcher is created implicitly { deep: true } ); From 859ffe362d03a957ee6ce1251da66f09af8b00a2 Mon Sep 17 00:00:00 2001 From: MartinSchuhmacher Date: Thu, 19 Dec 2024 14:38:11 +0100 Subject: [PATCH 11/48] adding type safety for boardapi composable --- src/modules/data/board/BoardApi.composable.ts | 111 ++++++++++++++---- 1 file changed, 86 insertions(+), 25 deletions(-) diff --git a/src/modules/data/board/BoardApi.composable.ts b/src/modules/data/board/BoardApi.composable.ts index 033fdfaedc..a3559f6b46 100644 --- a/src/modules/data/board/BoardApi.composable.ts +++ b/src/modules/data/board/BoardApi.composable.ts @@ -10,14 +10,23 @@ import { CourseRoomsApiFactory, CreateCardBodyParamsRequiredEmptyElementsEnum, CreateContentElementBodyParams, - DrawingElementContent, + DrawingElementContentBody, + DrawingElementResponse, + ExternalToolContentBody, ExternalToolElementContentBody, + ExternalToolElementResponse, FileElementContentBody, + FileElementResponse, + LinkContentBody, LinkElementContentBody, + LinkElementResponse, RichTextElementContentBody, + RichTextElementResponse, RoomApiFactory, SubmissionContainerElementContentBody, + SubmissionContainerElementResponse, VideoConferenceElementContentBody, + VideoConferenceElementResponse, } from "@/serverApi/v3"; import { BoardContextType } from "@/types/board/BoardContext"; import { AnyContentElement } from "@/types/board/ContentElement"; @@ -84,54 +93,106 @@ export const useBoardApi = () => { }; const generateDataProp = (element: AnyContentElement) => { - if (element.type === ContentElementType.RichText) { - return { + const isRichTextElement = ( + element: AnyContentElement + ): element is RichTextElementResponse => { + return element.type === ContentElementType.RichText; + }; + + if (isRichTextElement(element)) { + const body: RichTextElementContentBody = { content: element.content, - type: element.type, - } as RichTextElementContentBody; + type: ContentElementType.RichText, + }; + return body; } - if (element.type === ContentElementType.File) { - return { + const isFileElement = ( + element: AnyContentElement + ): element is FileElementResponse => { + return element.type === ContentElementType.File; + }; + + if (isFileElement(element)) { + const body: FileElementContentBody = { content: element.content, type: ContentElementType.File, - } as FileElementContentBody; + }; + return body; } - if (element.type === ContentElementType.SubmissionContainer) { - return { + const isSubmissionContainerElement = ( + element: AnyContentElement + ): element is SubmissionContainerElementResponse => { + return element.type === ContentElementType.SubmissionContainer; + }; + + if (isSubmissionContainerElement(element)) { + const body: SubmissionContainerElementContentBody = { content: element.content, type: ContentElementType.SubmissionContainer, - } as SubmissionContainerElementContentBody; + }; + return body; } - if (element.type === ContentElementType.Link) { - return { - content: element.content, + const isLinkElement = ( + element: AnyContentElement + ): element is LinkElementResponse => { + return element.type === ContentElementType.Link; + }; + + if (isLinkElement(element)) { + const body: LinkElementContentBody = { + // LinkElementContent is not type equal with LinkContentBody + content: element.content as LinkContentBody, type: ContentElementType.Link, - } as LinkElementContentBody; + }; + return body; } - if (element.type === ContentElementType.ExternalTool) { - return { - content: element.content, + const isExternalToolElement = ( + element: AnyContentElement + ): element is ExternalToolElementResponse => { + return element.type === ContentElementType.ExternalTool; + }; + + if (isExternalToolElement(element)) { + const body: ExternalToolElementContentBody = { + // ExternalToolElementContent is not type equal with ExternalToolContentBody + content: element.content as ExternalToolContentBody, type: ContentElementType.ExternalTool, - } as ExternalToolElementContentBody; + }; + return body; } - if (element.type === ContentElementType.Drawing) { - return { - content: element.content as DrawingElementContent, + const isDrawingElement = ( + element: AnyContentElement + ): element is DrawingElementResponse => { + return element.type === ContentElementType.Drawing; + }; + + if (isDrawingElement(element)) { + const body: DrawingElementContentBody = { + content: element.content, type: ContentElementType.Drawing, }; + return body; } - if (element.type === ContentElementType.VideoConference) { - return { + const isVideoConferenceElement = ( + element: AnyContentElement + ): element is VideoConferenceElementResponse => { + return element.type === ContentElementType.VideoConference; + }; + + if (isVideoConferenceElement(element)) { + const body: VideoConferenceElementContentBody = { content: element.content, type: ContentElementType.VideoConference, - } as VideoConferenceElementContentBody; + }; + return body; } + throw new Error("element.type mapping is undefined for updateElementCall"); }; From 0b3625f92e0d1216886df80a962a2dc2a8096a1e Mon Sep 17 00:00:00 2001 From: Franz Schuenzel Date: Fri, 20 Dec 2024 14:43:58 +0100 Subject: [PATCH 12/48] Fix wording in dialog and show status right aligned --- src/locales/de.ts | 6 +++--- src/locales/en.ts | 6 +++--- src/locales/es.ts | 6 +++--- src/locales/uk.ts | 6 +++--- .../components/VideoConferenceContentElementDisplay.vue | 8 ++++---- .../ui/board/content-element/ContentElementBar.vue | 7 +++++++ 6 files changed, 23 insertions(+), 16 deletions(-) diff --git a/src/locales/de.ts b/src/locales/de.ts index a5ef6e327c..a65ca113c3 100644 --- a/src/locales/de.ts +++ b/src/locales/de.ts @@ -1505,11 +1505,11 @@ export default { "pages.common.tools.configureVideoconferenceDialog.title": "Videokonferenz erstellen", "pages.common.tools.configureVideoconferenceDialog.text.allModeratorPermission": - "Alle Nutzer:innen nehmen als Moderator:in teil", + "Moderationsrechte für alle Teilnehmenden", "pages.common.tools.configureVideoconferenceDialog.text.mute": - "Teilnehmer:innen beim Betreten stummschalten", + "Teilnehmende beim Betreten stummschalten", "pages.common.tools.configureVideoconferenceDialog.text.waitingRoom": - "Freigabe durch Moderator:in, bevor der Raum betreten werden kann", + "Warteraum für Teilnehmende aktivieren", "pages.content._id.addToTopic": "Hinzufügen zu", "pages.content._id.collection.selectElements": "Wählen Sie die Elemente, die Sie zum Thema hinzufügen möchten", diff --git a/src/locales/en.ts b/src/locales/en.ts index 0c1252d8d0..9c537970cf 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -1482,11 +1482,11 @@ export default { "pages.common.tools.configureVideoconferenceDialog.title": "Create video conference", "pages.common.tools.configureVideoconferenceDialog.text.allModeratorPermission": - "All users participate as moderators", + "Moderation rights for all participants", "pages.common.tools.configureVideoconferenceDialog.text.mute": - "Mute participants when entering", + "Mute participants on entering", "pages.common.tools.configureVideoconferenceDialog.text.waitingRoom": - "Approval by the moderator before the room can be entered", + "Activate waiting room for participants", "pages.content._id.addToTopic": "To be added to", "pages.content._id.collection.selectElements": "Select the items you want to add to the topic", diff --git a/src/locales/es.ts b/src/locales/es.ts index 27db41e08f..da8a69fb02 100644 --- a/src/locales/es.ts +++ b/src/locales/es.ts @@ -1528,11 +1528,11 @@ export default { "pages.common.tools.configureVideoconferenceDialog.title": "Crear videoconferencia", "pages.common.tools.configureVideoconferenceDialog.text.allModeratorPermission": - "Todas las usuarias participan como moderadoras", + "Derechos de moderación para todos los participantes", "pages.common.tools.configureVideoconferenceDialog.text.mute": - "Silenciar a las participantes al entrar", + "Silenciar a los participantes al entrar", "pages.common.tools.configureVideoconferenceDialog.text.waitingRoom": - "Aprobación del moderador antes de poder ingresar a la sala", + "Activar la sala de espera para los participantes", "pages.content._id.addToTopic": "Para ser añadido a", "pages.content._id.collection.selectElements": "Selecciona los elementos que deses añadir al tema", diff --git a/src/locales/uk.ts b/src/locales/uk.ts index ac45b869b6..185875624b 100644 --- a/src/locales/uk.ts +++ b/src/locales/uk.ts @@ -1511,11 +1511,11 @@ export default { "pages.common.tools.configureVideoconferenceDialog.title": "Створити відеоконференцію", "pages.common.tools.configureVideoconferenceDialog.text.allModeratorPermission": - "Усі користувачі беруть участь як модератори", + "Права модерації для всіх учасників", "pages.common.tools.configureVideoconferenceDialog.text.mute": - "Вимкнення звуку учасників при вході", + "Вимкнути звук учасникам при вході", "pages.common.tools.configureVideoconferenceDialog.text.waitingRoom": - "Схвалення модератором перед входом до кімнати", + "Активуйте кімнату очікування для учасників", "pages.content._id.addToTopic": "Для додавання в", "pages.content._id.collection.selectElements": "Виберіть елементи, які треба додати до теми", diff --git a/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElementDisplay.vue b/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElementDisplay.vue index b6b4c4d017..35155e86f2 100644 --- a/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElementDisplay.vue +++ b/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElementDisplay.vue @@ -70,12 +70,13 @@ -
@@ -186,7 +187,6 @@ $pulseIconColor: #15ba97; border-radius: 50%; height: 20px; width: 20px; - margin: 10px; box-shadow: 0 0 0 0 $pulseIconColor; transform: scale(1); animation: pulse 1.5s 5; diff --git a/src/modules/ui/board/content-element/ContentElementBar.vue b/src/modules/ui/board/content-element/ContentElementBar.vue index d2a9c9e162..2998d9a9b8 100644 --- a/src/modules/ui/board/content-element/ContentElementBar.vue +++ b/src/modules/ui/board/content-element/ContentElementBar.vue @@ -50,6 +50,10 @@ > + +
+ +
From c910de4177f2367c18387e456f022a63be27831a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20Sch=C3=BCnzel?= Date: Fri, 20 Dec 2024 14:52:41 +0100 Subject: [PATCH 13/48] remove comment --- .../components/VideoConferenceContentElementDisplay.vue | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElementDisplay.vue b/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElementDisplay.vue index 35155e86f2..43bc7e7fe7 100644 --- a/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElementDisplay.vue +++ b/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElementDisplay.vue @@ -72,11 +72,6 @@
From ba56f0b2d6970e277ed5aeebd261155ac63ef490 Mon Sep 17 00:00:00 2001 From: MartinSchuhmacher Date: Fri, 20 Dec 2024 21:05:30 +0100 Subject: [PATCH 14/48] moving VC configuration dialog to ui folder --- config/webpack/webpack.common.js | 3 +++ .../components/VideoConferenceContentElement.vue | 2 +- .../VideoConferenceConfigurationDialog.unit.ts | 0 .../VideoConferenceConfigurationDialog.vue | 0 src/modules/ui/video-conference-configuration-dialog/index.ts | 3 +++ tsconfig.json | 3 +++ 6 files changed, 10 insertions(+), 1 deletion(-) rename src/modules/{feature/board-video-conference-element/components => ui/video-conference-configuration-dialog}/VideoConferenceConfigurationDialog.unit.ts (100%) rename src/modules/{feature/board-video-conference-element/components => ui/video-conference-configuration-dialog}/VideoConferenceConfigurationDialog.vue (100%) create mode 100644 src/modules/ui/video-conference-configuration-dialog/index.ts diff --git a/config/webpack/webpack.common.js b/config/webpack/webpack.common.js index c9105942e9..45c7abe5e5 100644 --- a/config/webpack/webpack.common.js +++ b/config/webpack/webpack.common.js @@ -175,6 +175,9 @@ module.exports = { "@ui-skip-link": getDir("src/modules/ui/skip-link"), "@ui-speed-dial-menu": getDir("src/modules/ui/speed-dial-menu"), "@ui-qr-code": getDir("src/modules/ui/qr-code"), + "@ui-video-conference-configuration-dialog": getDir( + "src/modules/ui/video-conference-configuration-dialog" + ), "@util-board": getDir("src/modules/util/board"), "@util-validators": getDir("src/modules/util/validators"), "@util-vue": getDir("src/modules/util/vue"), diff --git a/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElement.vue b/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElement.vue index ecdb423a3e..1037141d0f 100644 --- a/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElement.vue +++ b/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElement.vue @@ -94,7 +94,7 @@ import { import { useI18n } from "vue-i18n"; import VideoConferenceContentElementCreate from "./VideoConferenceContentElementCreate.vue"; import VideoConferenceContentElementDisplay from "./VideoConferenceContentElementDisplay.vue"; -import VideoConferenceConfigurationDialog from "./VideoConferenceConfigurationDialog.vue"; +import { VideoConferenceConfigurationDialog } from "@ui-video-conference-configuration-dialog"; import { BoardMenu, BoardMenuActionDelete, diff --git a/src/modules/feature/board-video-conference-element/components/VideoConferenceConfigurationDialog.unit.ts b/src/modules/ui/video-conference-configuration-dialog/VideoConferenceConfigurationDialog.unit.ts similarity index 100% rename from src/modules/feature/board-video-conference-element/components/VideoConferenceConfigurationDialog.unit.ts rename to src/modules/ui/video-conference-configuration-dialog/VideoConferenceConfigurationDialog.unit.ts diff --git a/src/modules/feature/board-video-conference-element/components/VideoConferenceConfigurationDialog.vue b/src/modules/ui/video-conference-configuration-dialog/VideoConferenceConfigurationDialog.vue similarity index 100% rename from src/modules/feature/board-video-conference-element/components/VideoConferenceConfigurationDialog.vue rename to src/modules/ui/video-conference-configuration-dialog/VideoConferenceConfigurationDialog.vue diff --git a/src/modules/ui/video-conference-configuration-dialog/index.ts b/src/modules/ui/video-conference-configuration-dialog/index.ts new file mode 100644 index 0000000000..ab9c113fb8 --- /dev/null +++ b/src/modules/ui/video-conference-configuration-dialog/index.ts @@ -0,0 +1,3 @@ +import VideoConferenceConfigurationDialog from "./VideoConferenceConfigurationDialog.vue"; + +export { VideoConferenceConfigurationDialog }; diff --git a/tsconfig.json b/tsconfig.json index acf375518c..e437157bf9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -70,6 +70,9 @@ "@ui-skip-link": ["src/modules/ui/skip-link"], "@ui-speed-dial-menu": ["src/modules/ui/speed-dial-menu"], "@ui-qr-code": ["src/modules/ui/qr-code"], + "@ui-video-conference-configuration-dialog": [ + "src/modules/ui/video-conference-configuration-dialog" + ], "@util-board": ["src/modules/util/board"], "@util-validators": ["src/modules/util/validators"], "@util-vue": ["src/modules/util/vue"], From 1b8a56302397a84999b3baa7a66ce9a38636a4cd Mon Sep 17 00:00:00 2001 From: MartinSchuhmacher Date: Fri, 20 Dec 2024 21:06:17 +0100 Subject: [PATCH 15/48] adjusting Room VC section to script setup and use configuration dialog component --- .../tools/RoomVideoConferenceSection.unit.ts | 206 ++------- .../tools/RoomVideoConferenceSection.vue | 395 +++++++----------- 2 files changed, 183 insertions(+), 418 deletions(-) diff --git a/src/pages/course-rooms/tools/RoomVideoConferenceSection.unit.ts b/src/pages/course-rooms/tools/RoomVideoConferenceSection.unit.ts index 9eda6802bc..46476cb429 100644 --- a/src/pages/course-rooms/tools/RoomVideoConferenceSection.unit.ts +++ b/src/pages/course-rooms/tools/RoomVideoConferenceSection.unit.ts @@ -14,8 +14,10 @@ import { createTestingVuetify, } from "@@/tests/test-utils/setup"; import { shallowMount } from "@vue/test-utils"; -import { VDialog, VSwitch } from "vuetify/lib/components/index.mjs"; +import { VDialog } from "vuetify/lib/components/index.mjs"; import RoomVideoConferenceSection from "./RoomVideoConferenceSection.vue"; +import RoomVideoConferenceCard from "@/components/rooms/RoomVideoConferenceCard.vue"; +import { VideoConferenceConfigurationDialog } from "@ui-video-conference-configuration-dialog"; describe("RoomVideoConferenceSection", () => { const mockUrl = "https://mock.com"; @@ -70,10 +72,6 @@ describe("RoomVideoConferenceSection", () => { [VIDEO_CONFERENCE_MODULE_KEY.valueOf()]: videoConferenceModule, [COURSE_ROOM_DETAILS_MODULE_KEY.valueOf()]: courseRoomDetailsModule, }, - mocks: { - $t: (key: string, dynamic?: object): string => - key + (dynamic ? ` ${JSON.stringify(dynamic)}` : ""), - }, }, props, }); @@ -118,9 +116,7 @@ describe("RoomVideoConferenceSection", () => { it("should set the video conference card to not running", () => { const { wrapper } = setup(); - const card = wrapper.findComponent({ - name: "room-video-conference-card", - }); + const card = wrapper.findComponent(RoomVideoConferenceCard); expect(card.props("isRunning")).toEqual(false); }); @@ -154,9 +150,7 @@ describe("RoomVideoConferenceSection", () => { it("should set the video conference card to running", () => { const { wrapper } = setup(); - const card = wrapper.findComponent({ - name: "room-video-conference-card", - }); + const card = wrapper.findComponent(RoomVideoConferenceCard); expect(card.props("isRunning")).toEqual(true); }); @@ -191,9 +185,7 @@ describe("RoomVideoConferenceSection", () => { it("should set the video conference card to refreshing", () => { const { wrapper } = setup(); - const card = wrapper.findComponent({ - name: "room-video-conference-card", - }); + const card = wrapper.findComponent(RoomVideoConferenceCard); expect(card.props("isRefreshing")).toEqual(true); }); @@ -228,9 +220,7 @@ describe("RoomVideoConferenceSection", () => { it("should set the video conference card to not have permission", () => { const { wrapper } = setup(); - const card = wrapper.findComponent({ - name: "room-video-conference-card", - }); + const card = wrapper.findComponent(RoomVideoConferenceCard); expect(card.props("hasPermission")).toEqual(false); }); @@ -265,9 +255,7 @@ describe("RoomVideoConferenceSection", () => { it("should set the video conference card to have permission", () => { const { wrapper } = setup(); - const card = wrapper.findComponent({ - name: "room-video-conference-card", - }); + const card = wrapper.findComponent(RoomVideoConferenceCard); expect(card.props("hasPermission")).toEqual(true); }); @@ -302,9 +290,7 @@ describe("RoomVideoConferenceSection", () => { it("should set the video conference card to have permission", () => { const { wrapper } = setup(); - const card = wrapper.findComponent({ - name: "room-video-conference-card", - }); + const card = wrapper.findComponent(RoomVideoConferenceCard); expect(card.props("hasPermission")).toEqual(true); }); @@ -339,9 +325,7 @@ describe("RoomVideoConferenceSection", () => { it("should set the video conference card to have permission", () => { const { wrapper } = setup(); - const card = wrapper.findComponent({ - name: "room-video-conference-card", - }); + const card = wrapper.findComponent(RoomVideoConferenceCard); expect(card.props("hasPermission")).toEqual(true); }); @@ -376,9 +360,7 @@ describe("RoomVideoConferenceSection", () => { it("should set the video conference card to have permission", () => { const { wrapper } = setup(); - const card = wrapper.findComponent({ - name: "room-video-conference-card", - }); + const card = wrapper.findComponent(RoomVideoConferenceCard); expect(card.props("hasPermission")).toEqual(false); }); @@ -414,9 +396,7 @@ describe("RoomVideoConferenceSection", () => { it("should call fetchVideoConferenceInfo", async () => { const { wrapper, videoConferenceModule } = setup(); - const card = wrapper.findComponent({ - name: "room-video-conference-card", - }); + const card = wrapper.findComponent(RoomVideoConferenceCard); await card.vm.$emit("refresh"); @@ -460,9 +440,7 @@ describe("RoomVideoConferenceSection", () => { it("should call joinVideoConference", async () => { const { wrapper, videoConferenceModule } = setup(); - const card = wrapper.findComponent({ - name: "room-video-conference-card", - }); + const card = wrapper.findComponent(RoomVideoConferenceCard); await card.vm.$emit("click"); @@ -502,13 +480,11 @@ describe("RoomVideoConferenceSection", () => { it("should open the videoconference configuration dialog", async () => { const { wrapper } = setup(); - const card = wrapper.findComponent({ - name: "room-video-conference-card", - }); + const card = wrapper.findComponent(RoomVideoConferenceCard); await card.vm.$emit("click"); const configurationDialog = wrapper.findComponent( - '[data-testId="videoconference-config-dialog"]' + VideoConferenceConfigurationDialog ); expect(configurationDialog.props("modelValue")).toBe(true); @@ -516,59 +492,7 @@ describe("RoomVideoConferenceSection", () => { }); }); - describe("when open videoconference configuration dialog", () => { - const setup = () => { - const { wrapper, videoConferenceModule, courseRoomDetailsModule } = - getWrapper( - { - roomId: "roomId", - }, - ["start_meeting"], - false, - { - getVideoConferenceInfo: { - state: VideoConferenceState.NOT_STARTED, - options: { - everyAttendeeJoinsMuted: false, - moderatorMustApproveJoinRequests: true, - everybodyJoinsAsModerator: false, - }, - }, - getLoading: true, - } - ); - - return { - wrapper, - videoConferenceModule, - courseRoomDetailsModule, - }; - }; - - it("should set the roomName in dialog title", async () => { - const { wrapper } = setup(); - - const card = wrapper.findComponent({ - name: "room-video-conference-card", - }); - - await card.vm.$emit("click"); - - const configurationDialog = wrapper.find( - '[data-testid="videoconference-config-dialog"]' - ); - const cardTitle = configurationDialog.find( - '[data-testid="videoconference-config-dialog-title"]' - ); - const title = cardTitle.text(); - - expect(title).toContain( - "pages.common.tools.configureVideoconferenceDialog.title" - ); - }); - }); - - describe("when clicking on create button of videoconference configuration dialog", () => { + describe("when videoconference configuration dialog emits 'start-video-conference'", () => { const setup = () => { const roomId = "roomId"; @@ -604,74 +528,20 @@ describe("RoomVideoConferenceSection", () => { }; }; - it("should call start with all options true ", async () => { - const { wrapper, videoConferenceModule, params, roomId } = setup(); - - const card = wrapper.findComponent({ - name: "room-video-conference-card", - }); - await card.vm.$emit("click"); - - const svEveryAttendeeJoinsMuted = wrapper.findComponent( - '[data-testId="every-attendee-joins-muted"]' - ); - const svModeratorMustApproveJoinRequests = wrapper.findComponent( - '[data-testId="moderator-must-approve-join-requests"]' - ); - const svEverybodyJoinsAsModerator = wrapper.findComponent( - '[data-testId="everybody-joins-as-moderator"]' - ); - - svEveryAttendeeJoinsMuted.vm.$emit("update:modelValue", true); - svModeratorMustApproveJoinRequests.vm.$emit("update:modelValue", true); - svEverybodyJoinsAsModerator.vm.$emit("update:modelValue", true); - - const createBtn = wrapper.find('[data-testId="dialog-create"]'); - await createBtn.trigger("click"); - - expect(videoConferenceModule.startVideoConference).toHaveBeenCalledWith({ - scope: params.scope, - scopeId: params.scopeId, - videoConferenceOptions: { - everyAttendeeJoinsMuted: true, - moderatorMustApproveJoinRequests: true, - everybodyJoinsAsModerator: true, - }, - logoutUrl: `${mockUrl}/rooms/${roomId}?tab=tools`, - }); - }); - - it("should call start with all options false ", async () => { + it("should call start with correct options", async () => { const { wrapper, videoConferenceModule, params, roomId } = setup(); - const card = wrapper.findComponent({ - name: "room-video-conference-card", - }); - await card.vm.$emit("click"); - - const svEveryAttendeeJoinsMuted = wrapper.findComponent( - '[data-testId="every-attendee-joins-muted"]' + const configurationDialog = wrapper.findComponent( + VideoConferenceConfigurationDialog ); - const svModeratorMustApproveJoinRequests = wrapper.findComponent( - '[data-testId="moderator-must-approve-join-requests"]' - ); - const svEverybodyJoinsAsModerator = wrapper.findComponent( - '[data-testId="everybody-joins-as-moderator"]' - ); - - svEveryAttendeeJoinsMuted.vm.$emit("update:modelValue", false); - svModeratorMustApproveJoinRequests.vm.$emit("update:modelValue", false); - svEverybodyJoinsAsModerator.vm.$emit("update:modelValue", false); - - const createBtn = wrapper.find('[data-testId="dialog-create"]'); - await createBtn.trigger("click"); + await configurationDialog.vm.$emit("start-video-conference"); expect(videoConferenceModule.startVideoConference).toHaveBeenCalledWith({ scope: params.scope, scopeId: params.scopeId, videoConferenceOptions: { everyAttendeeJoinsMuted: false, - moderatorMustApproveJoinRequests: false, + moderatorMustApproveJoinRequests: true, everybodyJoinsAsModerator: false, }, logoutUrl: `${mockUrl}/rooms/${roomId}?tab=tools`, @@ -681,17 +551,10 @@ describe("RoomVideoConferenceSection", () => { it("should call start and join videoconference function of store", async () => { const { wrapper, videoConferenceModule, params, roomId } = setup(); - const card = wrapper.findComponent({ - name: "room-video-conference-card", - }); - await card.vm.$emit("click"); - const configurationDialog = wrapper.findComponent( - '[data-testid="videoconference-config-dialog"]' + VideoConferenceConfigurationDialog ); - - const createBtn = wrapper.find('[data-testId="dialog-create"]'); - await createBtn.trigger("click"); + await configurationDialog.vm.$emit("start-video-conference"); expect(configurationDialog.props("modelValue")).toBe(false); expect(videoConferenceModule.startVideoConference).toHaveBeenCalledWith({ @@ -708,7 +571,7 @@ describe("RoomVideoConferenceSection", () => { }); }); - describe("when clicking on cancel button of videoconference configuration dialog", () => { + describe("when videoconference configuration dialog emits 'close'", () => { const setup = () => { const { wrapper, videoConferenceModule } = getWrapper( { @@ -738,19 +601,10 @@ describe("RoomVideoConferenceSection", () => { it("should close the videoconference configuration dialog", async () => { const { wrapper, videoConferenceModule } = setup(); - const card = wrapper.findComponent({ - name: "room-video-conference-card", - }); - await card.vm.$emit("click"); - const configurationDialog = wrapper.findComponent( - '[data-testId="videoconference-config-dialog"]' - ); - - const cancelBtn = configurationDialog.findComponent( - '[data-testid="dialog-cancel"]' + VideoConferenceConfigurationDialog ); - await cancelBtn.trigger("click"); + await configurationDialog.vm.$emit("close"); expect(configurationDialog.props("modelValue")).toBe(false); expect(videoConferenceModule.startVideoConference).not.toHaveBeenCalled(); @@ -793,9 +647,7 @@ describe("RoomVideoConferenceSection", () => { it("should display an error dialog", async () => { const { wrapper } = setup(); - const card = wrapper.findComponent({ - name: "room-video-conference-card", - }); + const card = wrapper.findComponent(RoomVideoConferenceCard); await card.vm.$emit("click"); const dialog = wrapper.find('[data-testId="error-dialog"]'); @@ -834,9 +686,7 @@ describe("RoomVideoConferenceSection", () => { it("should not display an error dialog", async () => { const { wrapper } = setup(); - const card = wrapper.findComponent({ - name: "room-video-conference-card", - }); + const card = wrapper.findComponent(RoomVideoConferenceCard); await card.vm.$emit("click"); const dialog = wrapper.find('[data-testId="error-dialog"]'); diff --git a/src/pages/course-rooms/tools/RoomVideoConferenceSection.vue b/src/pages/course-rooms/tools/RoomVideoConferenceSection.vue index ea307c7e4c..22310e508d 100644 --- a/src/pages/course-rooms/tools/RoomVideoConferenceSection.vue +++ b/src/pages/course-rooms/tools/RoomVideoConferenceSection.vue @@ -1,6 +1,6 @@ - From ae126057e95f76bef389cd67f0c9a9f6ff38d1bc Mon Sep 17 00:00:00 2001 From: Odalys Adam Date: Mon, 6 Jan 2025 16:44:48 +0100 Subject: [PATCH 16/48] fix pulsating dot always being there --- .../VideoConferenceContentElementDisplay.vue | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElementDisplay.vue b/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElementDisplay.vue index 43bc7e7fe7..a03e1c1b49 100644 --- a/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElementDisplay.vue +++ b/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElementDisplay.vue @@ -1,7 +1,7 @@ @@ -164,9 +168,9 @@ const onContentClick = () => { From 12bf75d3fc36242fb78a9ca950d699edd472eff3 Mon Sep 17 00:00:00 2001 From: MartinSchuhmacher Date: Wed, 8 Jan 2025 14:31:39 +0100 Subject: [PATCH 22/48] refactoring configuration dialog --- .../composables/VideoConference.composable.ts | 4 +- ...VideoConferenceConfigurationDialog.unit.ts | 58 ------------------- .../VideoConferenceConfigurationDialog.vue | 27 ++------- 3 files changed, 6 insertions(+), 83 deletions(-) diff --git a/src/modules/feature/board-video-conference-element/composables/VideoConference.composable.ts b/src/modules/feature/board-video-conference-element/composables/VideoConference.composable.ts index 8ac25359a8..ca929ef18f 100644 --- a/src/modules/feature/board-video-conference-element/composables/VideoConference.composable.ts +++ b/src/modules/feature/board-video-conference-element/composables/VideoConference.composable.ts @@ -30,9 +30,9 @@ export const useVideoConference = ( const videoConferenceInfo = ref({ state: VideoConferenceState.NOT_STARTED, options: { - everyAttendeeJoinsMuted: false, + everyAttendeeJoinsMuted: true, everybodyJoinsAsModerator: false, - moderatorMustApproveJoinRequests: true, + moderatorMustApproveJoinRequests: false, }, }); diff --git a/src/modules/ui/video-conference-configuration-dialog/VideoConferenceConfigurationDialog.unit.ts b/src/modules/ui/video-conference-configuration-dialog/VideoConferenceConfigurationDialog.unit.ts index dc579e20c9..df637b6955 100644 --- a/src/modules/ui/video-conference-configuration-dialog/VideoConferenceConfigurationDialog.unit.ts +++ b/src/modules/ui/video-conference-configuration-dialog/VideoConferenceConfigurationDialog.unit.ts @@ -69,63 +69,5 @@ describe("VideoConferenceConfigurationDialog", () => { await createButton.trigger("click"); expect(wrapper.emitted()).toHaveProperty("start-video-conference"); }); - - it("should toggle everyAttendeeJoinsMuted option when checkbox is clicked", async () => { - const wrapper = setup({ isOpen: true }); - - const dialog = wrapper.getComponent(VDialog); - const checkbox = dialog.findComponent( - "[data-testid='every-attendee-joins-muted']" - ); - await checkbox.setValue(true); - const emitted = wrapper.emitted(); - - const expectedObject = { - everyAttendeeJoinsMuted: true, - everybodyJoinsAsModerator: false, - moderatorMustApproveJoinRequests: true, - }; - - expect(emitted["update:options"]).toHaveLength(1); - expect(emitted["update:options"]).toStrictEqual([[expectedObject]]); - }); - - it("should toggle moderatorMustApproveJoinRequests option when checkbox is clicked", async () => { - const wrapper = setup({ isOpen: true }); - const dialog = wrapper.getComponent(VDialog); - const checkbox = dialog.findComponent( - "[data-testid='moderator-must-approve-join-requests']" - ); - await checkbox.setValue(false); - const emitted = wrapper.emitted(); - - const expectedObject = { - everyAttendeeJoinsMuted: false, - everybodyJoinsAsModerator: false, - moderatorMustApproveJoinRequests: false, - }; - - expect(emitted["update:options"]).toHaveLength(1); - expect(emitted["update:options"]).toStrictEqual([[expectedObject]]); - }); - - it("should toggle everybodyJoinsAsModerator option when checkbox is clicked", async () => { - const wrapper = setup({ isOpen: true }); - const dialog = wrapper.getComponent(VDialog); - const checkbox = dialog.findComponent( - "[data-testid='everybody-joins-as-moderator']" - ); - await checkbox.setValue(true); - const emitted = wrapper.emitted(); - - const expectedObject = { - everyAttendeeJoinsMuted: false, - everybodyJoinsAsModerator: true, - moderatorMustApproveJoinRequests: true, - }; - - expect(emitted["update:options"]).toHaveLength(1); - expect(emitted["update:options"]).toStrictEqual([[expectedObject]]); - }); }); }); diff --git a/src/modules/ui/video-conference-configuration-dialog/VideoConferenceConfigurationDialog.vue b/src/modules/ui/video-conference-configuration-dialog/VideoConferenceConfigurationDialog.vue index 9fcb4a5684..6fee6216ee 100644 --- a/src/modules/ui/video-conference-configuration-dialog/VideoConferenceConfigurationDialog.vue +++ b/src/modules/ui/video-conference-configuration-dialog/VideoConferenceConfigurationDialog.vue @@ -67,7 +67,7 @@ From c4a5b083e3eacebf95277d64f2b55b618c82902e Mon Sep 17 00:00:00 2001 From: MartinSchuhmacher Date: Wed, 8 Jan 2025 14:58:00 +0100 Subject: [PATCH 23/48] adjust base values to BE defaults --- .../composables/VideoConference.composable.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/feature/board-video-conference-element/composables/VideoConference.composable.ts b/src/modules/feature/board-video-conference-element/composables/VideoConference.composable.ts index ca929ef18f..8ac25359a8 100644 --- a/src/modules/feature/board-video-conference-element/composables/VideoConference.composable.ts +++ b/src/modules/feature/board-video-conference-element/composables/VideoConference.composable.ts @@ -30,9 +30,9 @@ export const useVideoConference = ( const videoConferenceInfo = ref({ state: VideoConferenceState.NOT_STARTED, options: { - everyAttendeeJoinsMuted: true, + everyAttendeeJoinsMuted: false, everybodyJoinsAsModerator: false, - moderatorMustApproveJoinRequests: false, + moderatorMustApproveJoinRequests: true, }, }); From 2766bf9670d025f7de86cd91d283a32101a6d221 Mon Sep 17 00:00:00 2001 From: MartinSchuhmacher Date: Wed, 8 Jan 2025 14:58:30 +0100 Subject: [PATCH 24/48] refactor variable naming for list layout --- .../components/VideoConferenceContentElementCreate.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElementCreate.vue b/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElementCreate.vue index 319f413110..9cd69790fd 100644 --- a/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElementCreate.vue +++ b/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElementCreate.vue @@ -3,8 +3,8 @@ data-testid="board-video-conference-element-create" class="d-flex" :class="{ - 'flex-row': hasRowStyle, - 'flex-column': !hasRowStyle, + 'flex-row': isRenderedAsList, + 'flex-column': !isRenderedAsList, }" >
@@ -89,7 +89,7 @@ const onKeydown = (e: KeyboardEvent) => { const isListLayout = ref(injectStrict(BOARD_IS_LIST_LAYOUT)); const { smAndUp } = useDisplay(); -const hasRowStyle = computed(() => { +const isRenderedAsList = computed(() => { return smAndUp.value && isListLayout.value; }); From 3cd664663d3870df68064af6c10ebb4b7f7fca62 Mon Sep 17 00:00:00 2001 From: MartinSchuhmacher Date: Wed, 8 Jan 2025 15:02:08 +0100 Subject: [PATCH 25/48] fixing css class name --- .../components/VideoConferenceContentElementCreate.vue | 8 ++++---- .../ui/board/content-element/ContentElementBar.vue | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElementCreate.vue b/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElementCreate.vue index 9cd69790fd..ea01a4647a 100644 --- a/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElementCreate.vue +++ b/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElementCreate.vue @@ -7,10 +7,10 @@ 'flex-column': !isRenderedAsList, }" > -
+
-
+
@@ -101,11 +101,11 @@ const isRenderedAsList = computed(() => { .menu { margin-right: -6px; } -.display-listboard { +.display-list-board { flex: 0 0 33%; } -.text-listboard { +.text-list-board { flex: 0 0 67%; } diff --git a/src/modules/ui/board/content-element/ContentElementBar.vue b/src/modules/ui/board/content-element/ContentElementBar.vue index 2998d9a9b8..b6a3baefd5 100644 --- a/src/modules/ui/board/content-element/ContentElementBar.vue +++ b/src/modules/ui/board/content-element/ContentElementBar.vue @@ -15,7 +15,7 @@ v-if="$slots.display" class="content-element-display" :class="{ - 'content-element-display-listboard': hasRowStyle, + 'content-element-display-list-board': hasRowStyle, }" > @@ -27,7 +27,7 @@ " :class="{ 'bg-surface-light': props.hasGreyBackground === true, - 'content-element-bar-texts-listboard': hasRowStyle, + 'content-element-bar-texts-list-board': hasRowStyle, }" class="content-element-bar-texts py-4" > @@ -119,11 +119,11 @@ const props = defineProps({ position: relative; } -.content-element-bar-texts-listboard { +.content-element-bar-texts-list-board { flex: 0 0 67%; } -.content-element-display-listboard { +.content-element-display-list-board { flex: 0 0 33%; } From 891e63b29f46076db05750db1e55c2ad7f21caa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20Sch=C3=BCnzel?= Date: Thu, 9 Jan 2025 16:00:20 +0100 Subject: [PATCH 26/48] Add Tab and Enter for VK-Element --- .../components/VideoConferenceContentElement.vue | 9 +++++++++ .../components/VideoConferenceContentElementDisplay.vue | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElement.vue b/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElement.vue index 76b1017114..a321375f69 100644 --- a/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElement.vue +++ b/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElement.vue @@ -7,10 +7,13 @@ :variant="outlined" ref="videoConferenceElement" :ripple="false" + tabindex="0" + role="link" target="_blank" link :aria-label="ariaLabel" @keydown.stop.up.down="onKeydownArrow" + @keyup.enter="onContentEnter" > { } }); }; + +const onContentEnter = async () => { + if (!props.isEditMode) { + onContentClick(); + } +}; diff --git a/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElementDisplay.vue b/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElementDisplay.vue index 96de110fb0..995d89d64c 100644 --- a/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElementDisplay.vue +++ b/src/modules/feature/board-video-conference-element/components/VideoConferenceContentElementDisplay.vue @@ -1,5 +1,5 @@