mirror of
https://github.com/hcengineering/platform.git
synced 2026-08-17 18:05:42 +02:00
Uberf-13261: Improve office UX (#9733)
Signed-off-by: Anton Alexeyev <alexeyev.anton@gmail.com>
This commit is contained in:
@@ -698,10 +698,6 @@ export function createModel (builder: Builder): void {
|
||||
searchDisabled: true
|
||||
})
|
||||
|
||||
builder.mixin(love.class.MeetingMinutes, core.class.Class, view.mixin.ObjectPanelFooter, {
|
||||
editor: love.component.PanelControlBar
|
||||
})
|
||||
|
||||
builder.createDoc(core.class.FullTextSearchContext, core.space.Model, {
|
||||
toClass: love.class.MeetingMinutes,
|
||||
fullTextSummary: true,
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
"@hcengineering/workbench": "^0.6.16",
|
||||
"@hcengineering/workbench-resources": "^0.6.1",
|
||||
"@hcengineering/emoji": "^0.6.0",
|
||||
"@hcengineering/emoji-resources": "^0.6.0",
|
||||
"@hcengineering/theme": "^0.6.5",
|
||||
"@livekit/krisp-noise-filter": "^0.3.0",
|
||||
"@livekit/track-processors": "^0.5.6",
|
||||
|
||||
@@ -1,326 +0,0 @@
|
||||
<!--
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { getCurrentEmployee } from '@hcengineering/contact'
|
||||
import { AccountRole, getCurrentAccount, hasAccountRole } from '@hcengineering/core'
|
||||
import { Room, RoomType, isOffice, roomAccessIcon, RoomAccess } from '@hcengineering/love'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import {
|
||||
ButtonMenu,
|
||||
DropdownIntlItem,
|
||||
IconMaximize,
|
||||
IconMoreV,
|
||||
IconUpOutline,
|
||||
ModernButton,
|
||||
Popup,
|
||||
SplitButton,
|
||||
eventToHTMLElement,
|
||||
showPopup,
|
||||
TooltipInstance
|
||||
} from '@hcengineering/ui'
|
||||
import view, { Action } from '@hcengineering/view'
|
||||
import { getActions } from '@hcengineering/view-resources'
|
||||
import { toggleCamState, toggleMicState, state } from '@hcengineering/media-resources'
|
||||
|
||||
import love from '../plugin'
|
||||
import { currentRoom, myInfo, myOffice } from '../stores'
|
||||
import {
|
||||
isFullScreen,
|
||||
isRecording,
|
||||
isRecordingAvailable,
|
||||
isShareWithSound,
|
||||
isSharingEnabled,
|
||||
isTranscription,
|
||||
isTranscriptionAllowed,
|
||||
leaveRoom,
|
||||
record,
|
||||
screenSharing,
|
||||
startTranscription,
|
||||
stopTranscription,
|
||||
lk,
|
||||
liveKitClient
|
||||
} from '../utils'
|
||||
import CamSettingPopup from './meeting/CamSettingPopup.svelte'
|
||||
import ControlBarContainer from './ControlBarContainer.svelte'
|
||||
import MicSettingPopup from './meeting/MicSettingPopup.svelte'
|
||||
import RoomAccessPopup from './RoomAccessPopup.svelte'
|
||||
import RoomModal from './RoomModal.svelte'
|
||||
import ShareSettingPopup from './ShareSettingPopup.svelte'
|
||||
import { lkSessionConnected } from '../liveKitClient'
|
||||
import emojiPlugin from '@hcengineering/emoji'
|
||||
|
||||
export let room: Room
|
||||
export let canMaximize: boolean = true
|
||||
export let fullScreen: boolean = false
|
||||
export let onFullScreen: (() => void) | undefined = undefined
|
||||
|
||||
let allowCam: boolean = false
|
||||
const allowShare: boolean = true
|
||||
let allowLeave: boolean = false
|
||||
let noLabel: boolean = false
|
||||
|
||||
$: allowCam = $currentRoom?.type === RoomType.Video
|
||||
$: allowLeave = $myInfo?.room !== ($myOffice?._id ?? love.ids.Reception)
|
||||
$: isMicEnabled = $state.microphone?.enabled === true
|
||||
$: isCamEnabled = $state.camera?.enabled === true
|
||||
|
||||
async function changeShare (): Promise<void> {
|
||||
const newValue = !$isSharingEnabled
|
||||
const audio = newValue && $isShareWithSound
|
||||
await liveKitClient.setScreenShareEnabled(newValue, audio)
|
||||
}
|
||||
|
||||
async function leave (): Promise<void> {
|
||||
await leaveRoom($myInfo, $myOffice)
|
||||
}
|
||||
|
||||
function micSettings (e: MouseEvent): void {
|
||||
showPopup(MicSettingPopup, {}, eventToHTMLElement(e))
|
||||
}
|
||||
|
||||
function camSettings (e: MouseEvent): void {
|
||||
showPopup(CamSettingPopup, {}, eventToHTMLElement(e))
|
||||
}
|
||||
|
||||
function shareSettings (e: MouseEvent): void {
|
||||
showPopup(ShareSettingPopup, {}, eventToHTMLElement(e))
|
||||
}
|
||||
|
||||
function setAccess (e: MouseEvent): void {
|
||||
if (isOffice(room) && room.person !== me) return
|
||||
showPopup(RoomAccessPopup, { room }, eventToHTMLElement(e))
|
||||
}
|
||||
|
||||
const me = getCurrentEmployee()
|
||||
const client = getClient()
|
||||
|
||||
const camKeys = client.getModel().findAllSync(view.class.Action, { _id: love.action.ToggleVideo })?.[0]?.keyBinding
|
||||
const micKeys = client.getModel().findAllSync(view.class.Action, { _id: love.action.ToggleMic })?.[0]?.keyBinding
|
||||
|
||||
let actions: Action[] = []
|
||||
let moreItems: DropdownIntlItem[] = []
|
||||
|
||||
$: void getActions(client, room, love.class.Room).then((res) => {
|
||||
actions = res
|
||||
})
|
||||
|
||||
$: moreItems = actions.map((action) => ({
|
||||
id: action._id,
|
||||
label: action.label,
|
||||
icon: action.icon
|
||||
}))
|
||||
|
||||
async function handleMenuOption (e: CustomEvent<DropdownIntlItem['id']>): Promise<void> {
|
||||
const action = actions.find((action) => action._id === e.detail)
|
||||
if (action !== undefined) {
|
||||
await handleAction(action)
|
||||
}
|
||||
}
|
||||
|
||||
async function handleAction (action: Action): Promise<void> {
|
||||
const fn = await getResource(action.action)
|
||||
await fn(room)
|
||||
}
|
||||
$: withVideo = $screenSharing || room.type === RoomType.Video
|
||||
|
||||
function maximize (): void {
|
||||
showPopup(RoomModal, { room }, 'full-centered')
|
||||
}
|
||||
|
||||
function addReaction (event: MouseEvent): void {
|
||||
showPopup(
|
||||
emojiPlugin.component.EmojiPopup,
|
||||
{},
|
||||
event?.target as HTMLElement,
|
||||
async (result) => {
|
||||
const emoji = result?.text
|
||||
if (emoji == null) return
|
||||
void lk.localParticipant.sendChatMessage(emoji, { topic: 'reaction' })
|
||||
},
|
||||
() => {}
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
<ControlBarContainer bind:noLabel>
|
||||
<svelte:fragment slot="right">
|
||||
{#if room._id !== love.ids.Reception && $lkSessionConnected}
|
||||
<ModernButton
|
||||
icon={roomAccessIcon[room.access]}
|
||||
iconProps={{
|
||||
fill:
|
||||
room.access === RoomAccess.Open
|
||||
? 'var(--bg-positive-default)'
|
||||
: room.access === RoomAccess.DND
|
||||
? 'var(--bg-negative-default)'
|
||||
: 'currentColor'
|
||||
}}
|
||||
tooltip={{ label: love.string.ChangeAccess }}
|
||||
kind={'secondary'}
|
||||
size={'large'}
|
||||
disabled={isOffice(room) && room.person !== me}
|
||||
on:click={setAccess}
|
||||
/>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="center">
|
||||
{#if $lkSessionConnected}
|
||||
<ModernButton icon={emojiPlugin.icon.Emoji} kind={'secondary'} size={'large'} on:click={addReaction} />
|
||||
<SplitButton
|
||||
size={'large'}
|
||||
icon={isMicEnabled ? love.icon.MicEnabled : love.icon.MicDisabled}
|
||||
showTooltip={{
|
||||
label: isMicEnabled ? love.string.Mute : love.string.UnMute,
|
||||
keys: micKeys
|
||||
}}
|
||||
action={toggleMicState}
|
||||
secondIcon={IconUpOutline}
|
||||
secondAction={micSettings}
|
||||
separate
|
||||
/>
|
||||
{#if allowCam}
|
||||
<SplitButton
|
||||
size={'large'}
|
||||
icon={isCamEnabled ? love.icon.CamEnabled : love.icon.CamDisabled}
|
||||
showTooltip={{
|
||||
label: isCamEnabled ? love.string.StopVideo : love.string.StartVideo,
|
||||
keys: camKeys
|
||||
}}
|
||||
action={toggleCamState}
|
||||
secondIcon={IconUpOutline}
|
||||
secondAction={camSettings}
|
||||
separate
|
||||
/>
|
||||
{/if}
|
||||
{#if allowShare}
|
||||
<SplitButton
|
||||
size={'large'}
|
||||
icon={$isSharingEnabled ? love.icon.SharingEnabled : love.icon.SharingDisabled}
|
||||
iconProps={{
|
||||
fill: $isSharingEnabled ? 'var(--bg-negative-default)' : 'var(--bg-positive-default)'
|
||||
}}
|
||||
showTooltip={{ label: $isSharingEnabled ? love.string.StopShare : love.string.Share }}
|
||||
disabled={($screenSharing && !$isSharingEnabled) || !$lkSessionConnected}
|
||||
action={changeShare}
|
||||
secondIcon={IconUpOutline}
|
||||
secondAction={shareSettings}
|
||||
separate
|
||||
/>
|
||||
{/if}
|
||||
{#if hasAccountRole(getCurrentAccount(), AccountRole.User) && $isRecordingAvailable}
|
||||
<ModernButton
|
||||
icon={$isRecording ? love.icon.StopRecord : love.icon.Record}
|
||||
tooltip={{ label: $isRecording ? love.string.StopRecord : love.string.Record }}
|
||||
disabled={!$lkSessionConnected}
|
||||
kind={'secondary'}
|
||||
size={'large'}
|
||||
on:click={() => record(room)}
|
||||
/>
|
||||
{/if}
|
||||
{#if hasAccountRole(getCurrentAccount(), AccountRole.User) && isTranscriptionAllowed() && $lkSessionConnected}
|
||||
<ModernButton
|
||||
icon={view.icon.Feather}
|
||||
iconProps={$isTranscription ? { fill: 'var(--button-negative-BackgroundColor)' } : {}}
|
||||
tooltip={{ label: $isTranscription ? love.string.StopTranscription : love.string.StartTranscription }}
|
||||
kind="secondary"
|
||||
size="large"
|
||||
on:click={() => {
|
||||
if ($isTranscription) {
|
||||
void stopTranscription(room)
|
||||
} else {
|
||||
void startTranscription(room)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
{:else}
|
||||
<ModernButton
|
||||
icon={roomAccessIcon[room.access]}
|
||||
iconProps={{
|
||||
fill:
|
||||
room.access === RoomAccess.Open
|
||||
? 'var(--bg-positive-default)'
|
||||
: room.access === RoomAccess.DND
|
||||
? 'var(--bg-negative-default)'
|
||||
: 'currentColor'
|
||||
}}
|
||||
tooltip={{ label: love.string.ChangeAccess }}
|
||||
kind={'secondary'}
|
||||
size={'large'}
|
||||
disabled={isOffice(room) && room.person !== me}
|
||||
on:click={setAccess}
|
||||
/>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="left">
|
||||
{#if $lkSessionConnected && withVideo && onFullScreen}
|
||||
<ModernButton
|
||||
icon={$isFullScreen ? love.icon.ExitFullScreen : love.icon.FullScreen}
|
||||
tooltip={{
|
||||
label: $isFullScreen ? love.string.ExitingFullscreenMode : love.string.FullscreenMode,
|
||||
direction: 'top'
|
||||
}}
|
||||
kind={'secondary'}
|
||||
size={'large'}
|
||||
on:click={() => {
|
||||
$isFullScreen = !$isFullScreen
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if ($screenSharing || room.type === RoomType.Video) && $lkSessionConnected && canMaximize}
|
||||
<ModernButton
|
||||
icon={IconMaximize}
|
||||
tooltip={{
|
||||
label: love.string.FullscreenMode,
|
||||
direction: 'top'
|
||||
}}
|
||||
kind={'secondary'}
|
||||
iconSize="medium"
|
||||
size={'large'}
|
||||
on:click={maximize}
|
||||
/>
|
||||
{/if}
|
||||
{#if $lkSessionConnected && moreItems.length > 0}
|
||||
<ButtonMenu
|
||||
items={moreItems}
|
||||
icon={IconMoreV}
|
||||
tooltip={{ label: love.string.MoreOptions, direction: 'top' }}
|
||||
kind="secondary"
|
||||
size="large"
|
||||
noSelection
|
||||
on:selected={handleMenuOption}
|
||||
/>
|
||||
{/if}
|
||||
{#if allowLeave}
|
||||
<ModernButton
|
||||
icon={love.icon.LeaveRoom}
|
||||
label={noLabel ? undefined : love.string.LeaveRoom}
|
||||
tooltip={{ label: love.string.LeaveRoom, direction: 'top' }}
|
||||
kind={'negative'}
|
||||
size={'large'}
|
||||
on:click={leave}
|
||||
/>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="extra">
|
||||
{#if fullScreen}
|
||||
<Popup fullScreen />
|
||||
<TooltipInstance fullScreen />
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</ControlBarContainer>
|
||||
@@ -21,8 +21,7 @@
|
||||
import { Viewlet, ViewletPreference } from '@hcengineering/view'
|
||||
|
||||
import lovePlg from '../plugin'
|
||||
import { currentRoom, floors } from '../stores'
|
||||
import ControlBar from './ControlBar.svelte'
|
||||
import { floors } from '../stores'
|
||||
|
||||
export let rooms: Room[] = []
|
||||
export let floor: Ref<Floor>
|
||||
@@ -63,7 +62,4 @@
|
||||
<Component is={viewlet.$lookup.descriptor.component} props={{ floor, rooms }} on:open />
|
||||
{/if}
|
||||
</div>
|
||||
{#if $currentRoom}
|
||||
<ControlBar room={$currentRoom} />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -16,12 +16,11 @@
|
||||
import { deviceOptionsStore as deviceInfo } from '@hcengineering/ui'
|
||||
import { onDestroy, onMount } from 'svelte'
|
||||
import presentation from '@hcengineering/presentation'
|
||||
import { RoomType } from '@hcengineering/love'
|
||||
|
||||
import Hall from './Hall.svelte'
|
||||
import { getMetadata } from '@hcengineering/platform'
|
||||
import love from '../plugin'
|
||||
import { tryConnect, screenSharing } from '../utils'
|
||||
import { tryConnect } from '../utils'
|
||||
import { infos, invites, myInfo, myRequests, waitForOfficeLoaded, currentRoom } from '../stores'
|
||||
import { lkSessionConnected } from '../liveKitClient'
|
||||
|
||||
@@ -48,8 +47,7 @@
|
||||
|
||||
if (
|
||||
!$lkSessionConnected &&
|
||||
(room.type === RoomType.Video || $screenSharing) &&
|
||||
$myInfo?.sessionId &&
|
||||
$myInfo?.sessionId !== undefined &&
|
||||
$myInfo.sessionId === getMetadata(presentation.metadata.SessionId)
|
||||
) {
|
||||
const info = $infos.filter((p) => p.room === room._id)
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
import { isOffice } from '@hcengineering/love'
|
||||
import { Button } from '@hcengineering/ui'
|
||||
import view from '@hcengineering/view'
|
||||
import { DocNavLink } from '@hcengineering/view-resources'
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import love from '../plugin'
|
||||
import { currentRoom, infos, myInfo, myOffice, rooms, currentMeetingMinutes } from '../stores'
|
||||
import { endMeeting, leaveRoom } from '../utils'
|
||||
import { currentRoom, infos, myInfo, myOffice, rooms } from '../stores'
|
||||
import { endMeeting, isSharingEnabled, leaveRoom, liveKitClient } from '../utils'
|
||||
import { lkSessionConnected } from '../liveKitClient'
|
||||
import MeetingHeader from './meeting/MeetingHeader.svelte'
|
||||
|
||||
export let limit: number = 4
|
||||
|
||||
@@ -34,31 +34,12 @@
|
||||
dispatch('close')
|
||||
}
|
||||
|
||||
function formatElapsedTime (elapsed: number): string {
|
||||
const seconds = Math.floor(elapsed / 1000)
|
||||
const minutes = Math.floor(seconds / 60)
|
||||
const hours = Math.floor(minutes / 60)
|
||||
|
||||
const displaySeconds = (seconds % 60).toString().padStart(2, '0')
|
||||
const displayMinutes = (minutes % 60).toString().padStart(2, '0')
|
||||
|
||||
return hours > 0 ? `${hours}:${displayMinutes}:${displaySeconds}` : `${displayMinutes}:${displaySeconds}`
|
||||
async function stopShare (): Promise<void> {
|
||||
await liveKitClient.setScreenShareEnabled(false)
|
||||
}
|
||||
|
||||
let now = Date.now()
|
||||
|
||||
$: participants = $infos.filter((p) => p.room === $currentRoom?._id)
|
||||
$: personByRefStore = getPersonByPersonRefStore(participants.map((p) => p.person))
|
||||
|
||||
onMount(() => {
|
||||
const interval = setInterval(() => {
|
||||
now = Date.now()
|
||||
}, 1000)
|
||||
|
||||
return () => {
|
||||
clearInterval(interval)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if $lkSessionConnected && $currentRoom != null}
|
||||
@@ -66,34 +47,7 @@
|
||||
|
||||
<div class="m-1 p-2">
|
||||
<div class="flex-col flex-grow flex-gap-0-5">
|
||||
<!-- subtitle -->
|
||||
{#if $currentMeetingMinutes !== undefined}
|
||||
<div class="flex-between flex-gap-2">
|
||||
<DocNavLink object={$currentRoom}>
|
||||
<span class="font-medium-12 secondary-textColor overflow-label">{$currentRoom.name}</span>
|
||||
</DocNavLink>
|
||||
|
||||
<!-- elapsed time from start -->
|
||||
{#if $currentMeetingMinutes?.createdOn !== undefined}
|
||||
{@const elapsed = now - $currentMeetingMinutes.createdOn}
|
||||
<div class="font-medium-12 secondary-textColor">{formatElapsedTime(elapsed)}</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="flex-between flex-gap-2">
|
||||
<!-- title -->
|
||||
{#if $currentMeetingMinutes !== undefined}
|
||||
<DocNavLink object={$currentMeetingMinutes}>
|
||||
<span class="font-medium overflow-label">{$currentMeetingMinutes.title}</span>
|
||||
</DocNavLink>
|
||||
{:else}
|
||||
<DocNavLink object={$currentRoom}>
|
||||
<span class="font-medium overflow-label">{$currentRoom.name}</span>
|
||||
</DocNavLink>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<MeetingHeader room={$currentRoom} />
|
||||
<div class="flex-between items-start flex-gap-2 mt-2">
|
||||
<!-- Avatars -->
|
||||
{#if overLimit}
|
||||
@@ -123,28 +77,41 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Leave Button -->
|
||||
{#if allowLeave || leaving}
|
||||
<Button
|
||||
icon={love.icon.LeaveRoom}
|
||||
kind={'dangerous'}
|
||||
size={'x-small'}
|
||||
label={view.string.Leave}
|
||||
showTooltip={{ label: love.string.LeaveRoom }}
|
||||
padding={'0 .5rem'}
|
||||
on:click={handleLeaveClick}
|
||||
/>
|
||||
{:else if isMyOffice || ending}
|
||||
<Button
|
||||
icon={love.icon.LeaveRoom}
|
||||
kind={'dangerous'}
|
||||
size={'x-small'}
|
||||
label={love.string.EndMeeting}
|
||||
showTooltip={{ label: love.string.EndMeeting }}
|
||||
padding={'0 .5rem'}
|
||||
on:click={handleEndMeetingClick}
|
||||
/>
|
||||
{/if}
|
||||
<div class="flex-row-center flex-gap-3">
|
||||
<!-- Leave Button -->
|
||||
{#if allowLeave || leaving}
|
||||
<Button
|
||||
icon={love.icon.LeaveRoom}
|
||||
kind={$isSharingEnabled ? 'regular' : 'dangerous'}
|
||||
size={'x-small'}
|
||||
label={view.string.Leave}
|
||||
showTooltip={{ label: love.string.LeaveRoom }}
|
||||
padding={'0 .5rem'}
|
||||
on:click={handleLeaveClick}
|
||||
/>
|
||||
{:else if isMyOffice || ending}
|
||||
<Button
|
||||
icon={love.icon.LeaveRoom}
|
||||
kind={$isSharingEnabled ? 'regular' : 'dangerous'}
|
||||
size={'x-small'}
|
||||
label={love.string.EndMeeting}
|
||||
showTooltip={{ label: love.string.EndMeeting }}
|
||||
padding={'0 .5rem'}
|
||||
on:click={handleEndMeetingClick}
|
||||
/>
|
||||
{/if}
|
||||
{#if $isSharingEnabled}
|
||||
<Button
|
||||
icon={love.icon.SharingEnabled}
|
||||
kind="dangerous"
|
||||
size={'x-small'}
|
||||
label={love.string.StopShare}
|
||||
showTooltip={{ label: love.string.StopShare }}
|
||||
padding={'0 .5rem'}
|
||||
on:click={stopShare}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
<!--
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { MeetingMinutes } from '@hcengineering/love'
|
||||
|
||||
import ControlBar from './ControlBar.svelte'
|
||||
import { currentRoom, currentMeetingMinutes } from '../stores'
|
||||
|
||||
export let object: MeetingMinutes
|
||||
</script>
|
||||
|
||||
{#if $currentRoom && $currentMeetingMinutes?._id === object._id}
|
||||
<div class="flex-grow flex-shrink">
|
||||
<ControlBar room={$currentRoom} />
|
||||
</div>
|
||||
{/if}
|
||||
@@ -22,7 +22,7 @@
|
||||
import love from '../plugin'
|
||||
import { waitForOfficeLoaded, currentRoom, infos, invites, myInfo, myRequests } from '../stores'
|
||||
import { isFullScreen, lk, tryConnect } from '../utils'
|
||||
import ControlBar from './ControlBar.svelte'
|
||||
import ControlBar from './meeting/ControlBar.svelte'
|
||||
import { lkSessionConnected } from '../liveKitClient'
|
||||
import ParticipantsListView from './meeting/ParticipantsListView.svelte'
|
||||
import ScreenSharingView from './meeting/ScreenSharingView.svelte'
|
||||
|
||||
@@ -16,28 +16,29 @@
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import presentation from '@hcengineering/presentation'
|
||||
import { Modal } from '@hcengineering/ui'
|
||||
import love, { RoomType } from '@hcengineering/love'
|
||||
import { RoomType } from '@hcengineering/love'
|
||||
|
||||
import { currentRoom } from '../stores'
|
||||
import RoomComponent from './Room.svelte'
|
||||
import { screenSharing } from '../utils'
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
$: if ($currentRoom === undefined || (!$screenSharing && $currentRoom.type !== RoomType.Video)) {
|
||||
$: if ($currentRoom === undefined) {
|
||||
dispatch('close')
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if ($currentRoom !== undefined && $screenSharing) || $currentRoom?.type === RoomType.Video}
|
||||
{#if $currentRoom !== undefined}
|
||||
<Modal
|
||||
label={love.string.Room}
|
||||
type="type-popup"
|
||||
okLabel={presentation.string.Create}
|
||||
hideFooter
|
||||
padding="0"
|
||||
on:close={() => dispatch('close')}
|
||||
>
|
||||
<svelte:fragment slot="title">
|
||||
{$currentRoom.name}
|
||||
</svelte:fragment>
|
||||
<RoomComponent withVideo={$currentRoom.type === RoomType.Video} room={$currentRoom} canMaximize={false} />
|
||||
</Modal>
|
||||
{/if}
|
||||
|
||||
@@ -13,57 +13,26 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Person, getCurrentEmployee } from '@hcengineering/contact'
|
||||
import { Person } from '@hcengineering/contact'
|
||||
import { UserInfo, getPersonByPersonRef } from '@hcengineering/contact-resources'
|
||||
import { Class, Doc, Ref } from '@hcengineering/core'
|
||||
|
||||
import {
|
||||
IconArrowLeft,
|
||||
IconUpOutline,
|
||||
Label,
|
||||
Location,
|
||||
ModernButton,
|
||||
Scroller,
|
||||
SplitButton,
|
||||
eventToHTMLElement,
|
||||
location,
|
||||
navigate,
|
||||
panelstore,
|
||||
showPopup
|
||||
} from '@hcengineering/ui'
|
||||
import { IconArrowLeft, Location, ModernButton, Scroller, location, navigate, panelstore } from '@hcengineering/ui'
|
||||
|
||||
import {
|
||||
MeetingMinutes,
|
||||
ParticipantInfo,
|
||||
Room,
|
||||
RoomType,
|
||||
isOffice,
|
||||
loveId,
|
||||
roomAccessIcon,
|
||||
roomAccessLabel
|
||||
} from '@hcengineering/love'
|
||||
import { MeetingMinutes, ParticipantInfo, Room, loveId } from '@hcengineering/love'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import view from '@hcengineering/view'
|
||||
import { getObjectLinkFragment } from '@hcengineering/view-resources'
|
||||
import { toggleCamState, toggleMicState, state } from '@hcengineering/media-resources'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import love from '../plugin'
|
||||
import { currentMeetingMinutes, currentRoom, infos, invites, myInfo, myOffice, myRequests, rooms } from '../stores'
|
||||
import {
|
||||
endMeeting,
|
||||
getRoomName,
|
||||
isShareWithSound,
|
||||
isSharingEnabled,
|
||||
leaveRoom,
|
||||
liveKitClient,
|
||||
screenSharing,
|
||||
tryConnect
|
||||
} from '../utils'
|
||||
import { currentMeetingMinutes, infos, invites, myInfo, myRequests } from '../stores'
|
||||
import { tryConnect } from '../utils'
|
||||
import { lkSessionConnected } from '../liveKitClient'
|
||||
import CamSettingPopup from './meeting/CamSettingPopup.svelte'
|
||||
import MicSettingPopup from './meeting/MicSettingPopup.svelte'
|
||||
import RoomAccessPopup from './RoomAccessPopup.svelte'
|
||||
import ShareSettingPopup from './ShareSettingPopup.svelte'
|
||||
import MicrophoneButton from './meeting/controls/MicrophoneButton.svelte'
|
||||
import CameraButton from './meeting/controls/CameraButton.svelte'
|
||||
import ShareScreenButton from './meeting/controls/ShareScreenButton.svelte'
|
||||
import LeaveRoomButton from './meeting/controls/LeaveRoomButton.svelte'
|
||||
import MeetingHeader from './meeting/MeetingHeader.svelte'
|
||||
|
||||
export let room: Room
|
||||
|
||||
@@ -79,45 +48,11 @@
|
||||
let joined: boolean = false
|
||||
$: joined = $myInfo?.room === room._id
|
||||
|
||||
$: isMyOffice = $myInfo?.room === $myOffice?._id
|
||||
|
||||
$: allowLeave = !isMyOffice && $myInfo?.room !== love.ids.Reception
|
||||
|
||||
let info: ParticipantInfo[] = []
|
||||
$: info = $infos.filter((p) => p.room === room._id)
|
||||
|
||||
let allowCam: boolean = false
|
||||
const allowShare: boolean = true
|
||||
|
||||
$: allowCam = $currentRoom?.type === RoomType.Video
|
||||
$: isMicEnabled = $state.microphone?.enabled === true
|
||||
$: isCamEnabled = $state.camera?.enabled === true
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
async function changeShare (): Promise<void> {
|
||||
const newValue = !$isSharingEnabled
|
||||
const audio = newValue && $isShareWithSound
|
||||
await liveKitClient.setScreenShareEnabled(newValue, audio)
|
||||
dispatch('close')
|
||||
}
|
||||
|
||||
async function leave (): Promise<void> {
|
||||
await leaveRoom($myInfo, $myOffice)
|
||||
dispatch('close')
|
||||
}
|
||||
|
||||
async function end (): Promise<void> {
|
||||
if (isOffice(room) && $myInfo !== undefined) {
|
||||
await endMeeting(room, $rooms, $infos, $myInfo)
|
||||
}
|
||||
dispatch('close')
|
||||
}
|
||||
|
||||
function shareSettings (e: MouseEvent): void {
|
||||
showPopup(ShareSettingPopup, {}, eventToHTMLElement(e))
|
||||
}
|
||||
|
||||
async function connect (): Promise<void> {
|
||||
await tryConnect($myInfo, room, info, $myRequests, $invites)
|
||||
dispatch('close')
|
||||
@@ -139,23 +74,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
function micSettings (e: MouseEvent): void {
|
||||
showPopup(MicSettingPopup, {}, eventToHTMLElement(e))
|
||||
}
|
||||
|
||||
function camSettings (e: MouseEvent): void {
|
||||
showPopup(CamSettingPopup, {}, eventToHTMLElement(e))
|
||||
}
|
||||
|
||||
function setAccess (e: MouseEvent): void {
|
||||
showPopup(RoomAccessPopup, { room }, eventToHTMLElement(e), (res) => {
|
||||
if (res !== undefined) {
|
||||
room.access = res
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const me = getCurrentEmployee()
|
||||
function canGoBack (joined: boolean, location: Location, meetingMinutes?: MeetingMinutes): boolean {
|
||||
if (!joined) return false
|
||||
if (location.path[2] !== loveId) return true
|
||||
@@ -168,13 +86,8 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="antiPopup room-popup">
|
||||
<div class="room-label"><Label label={love.string.Room} /></div>
|
||||
<div class="title overflow-label">
|
||||
{#await getRoomName(room) then name}
|
||||
{name}
|
||||
{/await}
|
||||
</div>
|
||||
<div class="antiPopup room-popup flex-gap-4">
|
||||
<MeetingHeader {room} />
|
||||
<div class="room-popup__content">
|
||||
<Scroller padding={'0.5rem'} stickedScrollBars>
|
||||
<div class="room-popup__content-grid">
|
||||
@@ -188,98 +101,37 @@
|
||||
</div>
|
||||
</Scroller>
|
||||
</div>
|
||||
{#if joined && $lkSessionConnected}
|
||||
<div class="room-btns" class:no-video={!allowCam}>
|
||||
<SplitButton
|
||||
size={'large'}
|
||||
icon={isMicEnabled ? love.icon.MicEnabled : love.icon.MicDisabled}
|
||||
label={isMicEnabled ? love.string.Mute : love.string.UnMute}
|
||||
showTooltip={{
|
||||
label: isMicEnabled ? love.string.Mute : love.string.UnMute
|
||||
}}
|
||||
action={toggleMicState}
|
||||
secondIcon={IconUpOutline}
|
||||
secondAction={micSettings}
|
||||
separate
|
||||
/>
|
||||
{#if allowCam}
|
||||
<SplitButton
|
||||
size={'large'}
|
||||
icon={isCamEnabled ? love.icon.CamEnabled : love.icon.CamDisabled}
|
||||
label={isCamEnabled ? love.string.StopVideo : love.string.StartVideo}
|
||||
showTooltip={{
|
||||
label: isCamEnabled ? love.string.StopVideo : love.string.StartVideo
|
||||
}}
|
||||
action={toggleCamState}
|
||||
secondIcon={IconUpOutline}
|
||||
secondAction={camSettings}
|
||||
separate
|
||||
/>
|
||||
{/if}
|
||||
{#if allowShare}
|
||||
<SplitButton
|
||||
size={'large'}
|
||||
icon={$isSharingEnabled ? love.icon.SharingEnabled : love.icon.SharingDisabled}
|
||||
showTooltip={{ label: $isSharingEnabled ? love.string.StopShare : love.string.Share }}
|
||||
disabled={($screenSharing && !$isSharingEnabled) || !$lkSessionConnected}
|
||||
action={changeShare}
|
||||
secondIcon={IconUpOutline}
|
||||
secondAction={shareSettings}
|
||||
separate
|
||||
/>
|
||||
{/if}
|
||||
<div class="flex-between gap-2">
|
||||
{#if joined && $lkSessionConnected}
|
||||
<div class="flex-between gap-2">
|
||||
<MicrophoneButton size="medium" />
|
||||
<CameraButton size="medium" />
|
||||
<ShareScreenButton size="medium" on:changeShare={() => dispatch('close')} />
|
||||
</div>
|
||||
{/if}
|
||||
<div style="width: auto" />
|
||||
{#if canGoBack(joined, $location, $currentMeetingMinutes)}
|
||||
<ModernButton
|
||||
icon={roomAccessIcon[room.access]}
|
||||
label={roomAccessLabel[room.access]}
|
||||
tooltip={{ label: love.string.ChangeAccess }}
|
||||
kind={'secondary'}
|
||||
size={'large'}
|
||||
disabled={isOffice(room) && room.person !== me}
|
||||
on:click={setAccess}
|
||||
icon={IconArrowLeft}
|
||||
label={love.string.MeetingMinutes}
|
||||
kind={'primary'}
|
||||
size={'medium'}
|
||||
on:click={back}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{#if $location.path[2] !== loveId || (joined && (allowLeave || isMyOffice)) || !joined}
|
||||
<div class="btns flex-row-center flex-reverse flex-no-shrink w-full flex-gap-2">
|
||||
{#if joined}
|
||||
{#if allowLeave}
|
||||
<ModernButton
|
||||
label={love.string.LeaveRoom}
|
||||
icon={love.icon.LeaveRoom}
|
||||
size={'large'}
|
||||
kind={'negative'}
|
||||
on:click={leave}
|
||||
/>
|
||||
{:else if isMyOffice}
|
||||
<ModernButton
|
||||
label={love.string.EndMeeting}
|
||||
icon={love.icon.LeaveRoom}
|
||||
size={'large'}
|
||||
kind={'negative'}
|
||||
on:click={end}
|
||||
/>
|
||||
{/if}
|
||||
{:else}
|
||||
<ModernButton
|
||||
icon={love.icon.EnterRoom}
|
||||
label={love.string.EnterRoom}
|
||||
size={'large'}
|
||||
kind={'primary'}
|
||||
autoFocus
|
||||
on:click={connect}
|
||||
/>
|
||||
{/if}
|
||||
{#if canGoBack(joined, $location, $currentMeetingMinutes)}
|
||||
<ModernButton
|
||||
icon={IconArrowLeft}
|
||||
label={love.string.MeetingMinutes}
|
||||
kind={'primary'}
|
||||
size={'large'}
|
||||
on:click={back}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
{#if joined}
|
||||
<LeaveRoomButton {room} noLabel={false} size="medium" on:leave={() => dispatch('close')} />
|
||||
{:else}
|
||||
<ModernButton
|
||||
icon={love.icon.EnterRoom}
|
||||
label={love.string.EnterRoom}
|
||||
size={'medium'}
|
||||
kind={'primary'}
|
||||
autoFocus
|
||||
on:click={connect}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@@ -13,12 +13,10 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import ui, { Label, ModernToggle } from '@hcengineering/ui'
|
||||
import { Label, ModernToggle } from '@hcengineering/ui'
|
||||
import love, { Room } from '@hcengineering/love'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
|
||||
import RoomLanguageSelector from './RoomLanguageSelector.svelte'
|
||||
|
||||
export let room: Room
|
||||
|
||||
const client = getClient()
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
<!--
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Room, RoomType } from '@hcengineering/love'
|
||||
import { IconMaximize, ModernButton, Popup, showPopup, TooltipInstance } from '@hcengineering/ui'
|
||||
|
||||
import love from '../../plugin'
|
||||
import { myInfo, myOffice } from '../../stores'
|
||||
import { isFullScreen, screenSharing } from '../../utils'
|
||||
import ControlBarContainer from './ControlBarContainer.svelte'
|
||||
import RoomModal from '../RoomModal.svelte'
|
||||
import { lkSessionConnected } from '../../liveKitClient'
|
||||
import MeetingOptionsButton from './controls/MeetingOptionsButton.svelte'
|
||||
import SendReactionButton from './controls/SendReactionButton.svelte'
|
||||
import RoomAccessButton from './controls/RoomAccessButton.svelte'
|
||||
import LeaveRoomButton from './controls/LeaveRoomButton.svelte'
|
||||
import RecordingButton from './controls/RecordingButton.svelte'
|
||||
import TranscriptionButton from './controls/TranscriptionButton.svelte'
|
||||
import MicrophoneButton from './controls/MicrophoneButton.svelte'
|
||||
import CameraButton from './controls/CameraButton.svelte'
|
||||
import ShareScreenButton from './controls/ShareScreenButton.svelte'
|
||||
|
||||
export let room: Room
|
||||
export let canMaximize: boolean = true
|
||||
export let fullScreen: boolean = false
|
||||
export let onFullScreen: (() => void) | undefined = undefined
|
||||
|
||||
let allowLeave: boolean = false
|
||||
let noLabel: boolean = false
|
||||
|
||||
$: allowLeave = $myInfo?.room !== ($myOffice?._id ?? love.ids.Reception)
|
||||
|
||||
$: withVideo = $screenSharing || room.type === RoomType.Video
|
||||
|
||||
function maximize (): void {
|
||||
showPopup(RoomModal, { room }, 'full-centered')
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="control-bar">
|
||||
<ControlBarContainer bind:noLabel>
|
||||
<svelte:fragment slot="right">
|
||||
{#if room._id !== love.ids.Reception && $lkSessionConnected}
|
||||
<RoomAccessButton {room} />
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="center">
|
||||
{#if $lkSessionConnected}
|
||||
<SendReactionButton />
|
||||
<MicrophoneButton />
|
||||
<CameraButton />
|
||||
<ShareScreenButton />
|
||||
<RecordingButton {room} />
|
||||
<TranscriptionButton {room} />
|
||||
{:else}
|
||||
<RoomAccessButton {room} />
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="left">
|
||||
{#if $lkSessionConnected && withVideo && onFullScreen}
|
||||
<ModernButton
|
||||
icon={$isFullScreen ? love.icon.ExitFullScreen : love.icon.FullScreen}
|
||||
tooltip={{
|
||||
label: $isFullScreen ? love.string.ExitingFullscreenMode : love.string.FullscreenMode,
|
||||
direction: 'top'
|
||||
}}
|
||||
kind={'secondary'}
|
||||
size={'large'}
|
||||
on:click={() => {
|
||||
$isFullScreen = !$isFullScreen
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if $lkSessionConnected && canMaximize}
|
||||
<ModernButton
|
||||
icon={IconMaximize}
|
||||
tooltip={{
|
||||
label: love.string.FullscreenMode,
|
||||
direction: 'top'
|
||||
}}
|
||||
kind={'secondary'}
|
||||
iconSize="medium"
|
||||
size={'large'}
|
||||
on:click={maximize}
|
||||
/>
|
||||
{/if}
|
||||
<MeetingOptionsButton {room} />
|
||||
{#if allowLeave}
|
||||
<LeaveRoomButton {room} {noLabel} />
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="extra">
|
||||
{#if fullScreen}
|
||||
<Popup fullScreen />
|
||||
<TooltipInstance fullScreen />
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</ControlBarContainer>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.control-bar {
|
||||
width: 100%;
|
||||
border-top: 1px solid var(--theme-divider-color);
|
||||
}
|
||||
</style>
|
||||
+14
-28
@@ -15,16 +15,7 @@
|
||||
<script lang="ts">
|
||||
import { IdMap, Ref, toIdMap } from '@hcengineering/core'
|
||||
import { getCurrentEmployee } from '@hcengineering/contact'
|
||||
import {
|
||||
Invite,
|
||||
isOffice,
|
||||
JoinRequest,
|
||||
Office,
|
||||
ParticipantInfo,
|
||||
RequestStatus,
|
||||
Room,
|
||||
RoomType
|
||||
} from '@hcengineering/love'
|
||||
import { Invite, isOffice, JoinRequest, Office, ParticipantInfo, RequestStatus, Room } from '@hcengineering/love'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import {
|
||||
closePopup,
|
||||
@@ -39,8 +30,8 @@
|
||||
import workbench from '@hcengineering/workbench'
|
||||
import { closeWidget, closeWidgetTab, sidebarStore } from '@hcengineering/workbench-resources'
|
||||
|
||||
import love from '../plugin'
|
||||
import { activeInvites, currentRoom, infos, myInfo, myInvites, myOffice, myRequests, rooms } from '../stores'
|
||||
import love from '../../plugin'
|
||||
import { activeInvites, currentRoom, infos, myInfo, myInvites, myOffice, myRequests, rooms } from '../../stores'
|
||||
import {
|
||||
connectRoom,
|
||||
createMeetingVideoWidgetTab,
|
||||
@@ -48,18 +39,17 @@
|
||||
disconnect,
|
||||
endMeeting,
|
||||
getRoomName,
|
||||
leaveRoom,
|
||||
screenSharing
|
||||
} from '../utils'
|
||||
import ActiveInvitesPopup from './ActiveInvitesPopup.svelte'
|
||||
import InvitePopup from './InvitePopup.svelte'
|
||||
import PersonActionPopup from './PersonActionPopup.svelte'
|
||||
import RequestPopup from './RequestPopup.svelte'
|
||||
import RequestingPopup from './RequestingPopup.svelte'
|
||||
import RoomPopup from './RoomPopup.svelte'
|
||||
import RoomButton from './RoomButton.svelte'
|
||||
leaveRoom
|
||||
} from '../../utils'
|
||||
import ActiveInvitesPopup from '../ActiveInvitesPopup.svelte'
|
||||
import InvitePopup from '../InvitePopup.svelte'
|
||||
import PersonActionPopup from '../PersonActionPopup.svelte'
|
||||
import RequestPopup from '../RequestPopup.svelte'
|
||||
import RequestingPopup from '../RequestingPopup.svelte'
|
||||
import RoomPopup from '../RoomPopup.svelte'
|
||||
import RoomButton from '../RoomButton.svelte'
|
||||
import { getPersonByPersonRef } from '@hcengineering/contact-resources'
|
||||
import { lkSessionConnected } from '../liveKitClient'
|
||||
import { lkSessionConnected } from '../../liveKitClient'
|
||||
|
||||
const client = getClient()
|
||||
|
||||
@@ -250,11 +240,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
$: checkActiveVideo(
|
||||
$location,
|
||||
$lkSessionConnected && ($currentRoom?.type === RoomType.Video || $screenSharing),
|
||||
$currentRoom?._id
|
||||
)
|
||||
$: checkActiveVideo($location, $lkSessionConnected, $currentRoom?._id)
|
||||
|
||||
$: joined = activeRooms.filter((r) => $myInfo?.room === r._id)
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
<script lang="ts">
|
||||
import { DocNavLink } from '@hcengineering/view-resources'
|
||||
import love, { MeetingMinutes, MeetingStatus, Room } from '@hcengineering/love'
|
||||
import { onMount } from 'svelte'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { SortingOrder } from '@hcengineering/core'
|
||||
|
||||
export let room: Room
|
||||
|
||||
const client = getClient()
|
||||
let currentMeetingMinutes: MeetingMinutes | undefined
|
||||
|
||||
$: void client
|
||||
.findOne(
|
||||
love.class.MeetingMinutes,
|
||||
{ attachedTo: room._id, status: MeetingStatus.Active },
|
||||
{ sort: { createdOn: SortingOrder.Descending } }
|
||||
)
|
||||
.then((res) => {
|
||||
currentMeetingMinutes = res
|
||||
})
|
||||
|
||||
function formatElapsedTime (elapsed: number): string {
|
||||
const seconds = Math.floor(elapsed / 1000)
|
||||
const minutes = Math.floor(seconds / 60)
|
||||
const hours = Math.floor(minutes / 60)
|
||||
|
||||
const displaySeconds = (seconds % 60).toString().padStart(2, '0')
|
||||
const displayMinutes = (minutes % 60).toString().padStart(2, '0')
|
||||
|
||||
return hours > 0 ? `${hours}:${displayMinutes}:${displaySeconds}` : `${displayMinutes}:${displaySeconds}`
|
||||
}
|
||||
|
||||
let now = Date.now()
|
||||
|
||||
onMount(() => {
|
||||
const interval = setInterval(() => {
|
||||
now = Date.now()
|
||||
}, 1000)
|
||||
|
||||
return () => {
|
||||
clearInterval(interval)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if currentMeetingMinutes !== undefined}
|
||||
<div class="flex-between flex-gap-2">
|
||||
<DocNavLink object={room}>
|
||||
<span class="font-medium-12 secondary-textColor overflow-label">{room.name}</span>
|
||||
</DocNavLink>
|
||||
|
||||
<!-- elapsed time from start -->
|
||||
{#if currentMeetingMinutes?.createdOn !== undefined}
|
||||
{@const elapsed = now - currentMeetingMinutes.createdOn}
|
||||
<div class="font-medium-12 secondary-textColor">{formatElapsedTime(elapsed)}</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="flex-between flex-gap-2">
|
||||
<!-- title -->
|
||||
{#if currentMeetingMinutes !== undefined}
|
||||
<DocNavLink object={currentMeetingMinutes}>
|
||||
<span class="font-medium overflow-label">{currentMeetingMinutes.title}</span>
|
||||
</DocNavLink>
|
||||
{:else}
|
||||
<DocNavLink object={room}>
|
||||
<span class="font-medium overflow-label">{room.name}</span>
|
||||
</DocNavLink>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -1,5 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
import { getBlobRef } from '@hcengineering/presentation'
|
||||
import { getEmojiByShortCode } from '@hcengineering/emoji-resources'
|
||||
import { isCustomEmoji } from '@hcengineering/emoji'
|
||||
|
||||
export let emoji: string = ''
|
||||
export let width: number = 0
|
||||
@@ -13,6 +16,8 @@
|
||||
let x: number = 0
|
||||
let size: number = 0
|
||||
|
||||
$: customEmoji = getEmojiByShortCode(emoji)
|
||||
|
||||
onMount(() => {
|
||||
offsetX = (Math.random() * width) / 4 - width / 8
|
||||
size = height / 4
|
||||
@@ -35,7 +40,15 @@
|
||||
--duration: {duration}ms;
|
||||
"
|
||||
>
|
||||
{emoji}
|
||||
{#if customEmoji !== undefined && isCustomEmoji(customEmoji)}
|
||||
<span class="emoji">
|
||||
{#await getBlobRef(customEmoji.image) then blobSrc}
|
||||
<img src={blobSrc.src} alt={emoji} />
|
||||
{/await}
|
||||
</span>
|
||||
{:else}
|
||||
{emoji}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
<script lang="ts">
|
||||
import { eventToHTMLElement, IconUpOutline, showPopup, SplitButton } from '@hcengineering/ui'
|
||||
import CamSettingPopup from '../CamSettingPopup.svelte'
|
||||
import { RoomType } from '@hcengineering/love'
|
||||
import { currentRoom } from '../../../stores'
|
||||
import love from '../../../plugin'
|
||||
import { state, toggleCamState } from '@hcengineering/media-resources'
|
||||
import view from '@hcengineering/view'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
|
||||
export let size: 'large' | 'medium' | 'small' | 'extra-small' | 'min' = 'large'
|
||||
|
||||
$: allowCam = $currentRoom?.type === RoomType.Video
|
||||
$: isCamEnabled = $state.camera?.enabled === true
|
||||
|
||||
const client = getClient()
|
||||
const camKeys = client.getModel().findAllSync(view.class.Action, { _id: love.action.ToggleVideo })?.[0]?.keyBinding
|
||||
|
||||
function camSettings (e: MouseEvent): void {
|
||||
showPopup(CamSettingPopup, {}, eventToHTMLElement(e))
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if allowCam}
|
||||
<SplitButton
|
||||
{size}
|
||||
icon={isCamEnabled ? love.icon.CamEnabled : love.icon.CamDisabled}
|
||||
showTooltip={{
|
||||
label: isCamEnabled ? love.string.StopVideo : love.string.StartVideo,
|
||||
keys: camKeys
|
||||
}}
|
||||
action={toggleCamState}
|
||||
secondIcon={IconUpOutline}
|
||||
secondAction={camSettings}
|
||||
separate
|
||||
/>
|
||||
{/if}
|
||||
@@ -0,0 +1,34 @@
|
||||
<script lang="ts">
|
||||
import love from '../../../plugin'
|
||||
import { ModernButton } from '@hcengineering/ui'
|
||||
import { endMeeting, leaveRoom } from '../../../utils'
|
||||
import { infos, myInfo, myOffice, rooms } from '../../../stores'
|
||||
import { isOffice, Room } from '@hcengineering/love'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
export let room: Room
|
||||
export let size: 'large' | 'medium' | 'small' | 'extra-small' | 'min' = 'large'
|
||||
export let noLabel: boolean = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
$: isMyOffice = isOffice(room) && $myInfo?.room === $myOffice?._id
|
||||
|
||||
async function leave (): Promise<void> {
|
||||
if (isMyOffice && isOffice(room) && $myInfo !== undefined) {
|
||||
await endMeeting(room, $rooms, $infos, $myInfo)
|
||||
} else {
|
||||
await leaveRoom($myInfo, $myOffice)
|
||||
}
|
||||
dispatch('leave')
|
||||
}
|
||||
</script>
|
||||
|
||||
<ModernButton
|
||||
icon={love.icon.LeaveRoom}
|
||||
label={noLabel ? undefined : isMyOffice ? love.string.EndMeeting : love.string.LeaveRoom}
|
||||
tooltip={{ label: isMyOffice ? love.string.EndMeeting : love.string.LeaveRoom, direction: 'top' }}
|
||||
kind={'negative'}
|
||||
{size}
|
||||
on:click={leave}
|
||||
/>
|
||||
@@ -0,0 +1,53 @@
|
||||
<script lang="ts">
|
||||
import { ButtonMenu, DropdownIntlItem, IconMoreV } from '@hcengineering/ui'
|
||||
import { Action } from '@hcengineering/view'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import { lkSessionConnected } from '../../../liveKitClient'
|
||||
import { getActions } from '@hcengineering/view-resources'
|
||||
import love from '../../../plugin'
|
||||
import { Room } from '@hcengineering/love'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
|
||||
export let room: Room
|
||||
export let kind: 'primary' | 'secondary' | 'tertiary' | 'negative' = 'secondary'
|
||||
export let size: 'large' | 'medium' | 'small' | 'extra-small' | 'min' = 'large'
|
||||
|
||||
let actions: Action[] = []
|
||||
let moreItems: DropdownIntlItem[] = []
|
||||
|
||||
const client = getClient()
|
||||
|
||||
$: void getActions(client, room, love.class.Room).then((res) => {
|
||||
actions = res
|
||||
})
|
||||
|
||||
$: moreItems = actions.map((action) => ({
|
||||
id: action._id,
|
||||
label: action.label,
|
||||
icon: action.icon
|
||||
}))
|
||||
|
||||
async function handleMenuOption (e: CustomEvent<DropdownIntlItem['id']>): Promise<void> {
|
||||
const action = actions.find((action) => action._id === e.detail)
|
||||
if (action !== undefined) {
|
||||
await handleAction(action)
|
||||
}
|
||||
}
|
||||
|
||||
async function handleAction (action: Action): Promise<void> {
|
||||
const fn = await getResource(action.action)
|
||||
await fn(room)
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if $lkSessionConnected && moreItems.length > 0}
|
||||
<ButtonMenu
|
||||
items={moreItems}
|
||||
icon={IconMoreV}
|
||||
tooltip={{ label: love.string.MoreOptions, direction: 'top' }}
|
||||
{kind}
|
||||
{size}
|
||||
noSelection
|
||||
on:selected={handleMenuOption}
|
||||
/>
|
||||
{/if}
|
||||
@@ -0,0 +1,32 @@
|
||||
<script lang="ts">
|
||||
import love from '../../../plugin'
|
||||
import { state, toggleMicState } from '@hcengineering/media-resources'
|
||||
import { eventToHTMLElement, IconUpOutline, showPopup, SplitButton } from '@hcengineering/ui'
|
||||
import view from '@hcengineering/view'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import MicSettingPopup from '../MicSettingPopup.svelte'
|
||||
|
||||
export let size: 'large' | 'medium' | 'small' | 'extra-small' | 'min' = 'large'
|
||||
|
||||
$: isMicEnabled = $state.microphone?.enabled === true
|
||||
|
||||
const client = getClient()
|
||||
const micKeys = client.getModel().findAllSync(view.class.Action, { _id: love.action.ToggleMic })?.[0]?.keyBinding
|
||||
|
||||
function micSettings (e: MouseEvent): void {
|
||||
showPopup(MicSettingPopup, {}, eventToHTMLElement(e))
|
||||
}
|
||||
</script>
|
||||
|
||||
<SplitButton
|
||||
{size}
|
||||
icon={isMicEnabled ? love.icon.MicEnabled : love.icon.MicDisabled}
|
||||
showTooltip={{
|
||||
label: isMicEnabled ? love.string.Mute : love.string.UnMute,
|
||||
keys: micKeys
|
||||
}}
|
||||
action={toggleMicState}
|
||||
secondIcon={IconUpOutline}
|
||||
secondAction={micSettings}
|
||||
separate
|
||||
/>
|
||||
@@ -0,0 +1,22 @@
|
||||
<script lang="ts">
|
||||
import { AccountRole, getCurrentAccount, hasAccountRole } from '@hcengineering/core'
|
||||
import { ModernButton } from '@hcengineering/ui'
|
||||
import { isRecording, isRecordingAvailable, record } from '../../../utils'
|
||||
import love from '../../../plugin'
|
||||
import { lkSessionConnected } from '../../../liveKitClient'
|
||||
import { Room } from '@hcengineering/love'
|
||||
|
||||
export let room: Room
|
||||
export let size: 'large' | 'medium' | 'small' | 'extra-small' | 'min' = 'large'
|
||||
</script>
|
||||
|
||||
{#if hasAccountRole(getCurrentAccount(), AccountRole.User) && $isRecordingAvailable}
|
||||
<ModernButton
|
||||
icon={$isRecording ? love.icon.StopRecord : love.icon.Record}
|
||||
tooltip={{ label: $isRecording ? love.string.StopRecord : love.string.Record }}
|
||||
disabled={!$lkSessionConnected}
|
||||
kind={'secondary'}
|
||||
{size}
|
||||
on:click={() => record(room)}
|
||||
/>
|
||||
{/if}
|
||||
@@ -0,0 +1,34 @@
|
||||
<script lang="ts">
|
||||
import { isOffice, Room, RoomAccess, roomAccessIcon } from '@hcengineering/love'
|
||||
import love from '../../../plugin'
|
||||
import { eventToHTMLElement, ModernButton, showPopup } from '@hcengineering/ui'
|
||||
import RoomAccessPopup from '../../RoomAccessPopup.svelte'
|
||||
import { getCurrentEmployee } from '@hcengineering/contact'
|
||||
|
||||
export let room: Room
|
||||
export let size: 'large' | 'medium' | 'small' | 'extra-small' | 'min' = 'large'
|
||||
|
||||
const me = getCurrentEmployee()
|
||||
|
||||
function setAccess (e: MouseEvent): void {
|
||||
if (isOffice(room) && room.person !== me) return
|
||||
showPopup(RoomAccessPopup, { room }, eventToHTMLElement(e))
|
||||
}
|
||||
</script>
|
||||
|
||||
<ModernButton
|
||||
icon={roomAccessIcon[room.access]}
|
||||
iconProps={{
|
||||
fill:
|
||||
room.access === RoomAccess.Open
|
||||
? 'var(--bg-positive-default)'
|
||||
: room.access === RoomAccess.DND
|
||||
? 'var(--bg-negative-default)'
|
||||
: 'currentColor'
|
||||
}}
|
||||
tooltip={{ label: love.string.ChangeAccess }}
|
||||
kind={'secondary'}
|
||||
{size}
|
||||
disabled={isOffice(room) && room.person !== me}
|
||||
on:click={setAccess}
|
||||
/>
|
||||
@@ -0,0 +1,23 @@
|
||||
<script lang="ts">
|
||||
import emojiPlugin from '@hcengineering/emoji'
|
||||
import { ModernButton, showPopup } from '@hcengineering/ui'
|
||||
import { lk } from '../../../utils'
|
||||
|
||||
export let size: 'large' | 'medium' | 'small' | 'extra-small' | 'min' = 'large'
|
||||
|
||||
function addReaction (event: MouseEvent): void {
|
||||
showPopup(
|
||||
emojiPlugin.component.EmojiPopup,
|
||||
{},
|
||||
event?.target as HTMLElement,
|
||||
async (result) => {
|
||||
const emoji = result?.text
|
||||
if (emoji == null) return
|
||||
void lk.localParticipant.sendChatMessage(emoji, { topic: 'reaction' })
|
||||
},
|
||||
() => {}
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
<ModernButton icon={emojiPlugin.icon.Emoji} kind={'secondary'} {size} on:click={addReaction} />
|
||||
@@ -0,0 +1,37 @@
|
||||
<script lang="ts">
|
||||
import { eventToHTMLElement, IconUpOutline, showPopup, SplitButton } from '@hcengineering/ui'
|
||||
import { isShareWithSound, isSharingEnabled, liveKitClient, screenSharing } from '../../../utils'
|
||||
import ShareSettingPopup from '../../ShareSettingPopup.svelte'
|
||||
import love from '../../../plugin'
|
||||
import { lkSessionConnected } from '../../../liveKitClient'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
export let size: 'large' | 'medium' | 'small' | 'extra-small' | 'min' = 'large'
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
async function changeShare (): Promise<void> {
|
||||
const newValue = !$isSharingEnabled
|
||||
const audio = newValue && $isShareWithSound
|
||||
await liveKitClient.setScreenShareEnabled(newValue, audio)
|
||||
dispatch('changeShare')
|
||||
}
|
||||
|
||||
function shareSettings (e: MouseEvent): void {
|
||||
showPopup(ShareSettingPopup, {}, eventToHTMLElement(e))
|
||||
}
|
||||
</script>
|
||||
|
||||
<SplitButton
|
||||
{size}
|
||||
icon={$isSharingEnabled ? love.icon.SharingEnabled : love.icon.SharingDisabled}
|
||||
iconProps={{
|
||||
fill: $isSharingEnabled ? 'var(--bg-negative-default)' : 'var(--bg-positive-default)'
|
||||
}}
|
||||
showTooltip={{ label: $isSharingEnabled ? love.string.StopShare : love.string.Share }}
|
||||
disabled={($screenSharing && !$isSharingEnabled) || !$lkSessionConnected}
|
||||
action={changeShare}
|
||||
secondIcon={IconUpOutline}
|
||||
secondAction={shareSettings}
|
||||
separate
|
||||
/>
|
||||
@@ -0,0 +1,29 @@
|
||||
<script lang="ts">
|
||||
import { AccountRole, getCurrentAccount, hasAccountRole } from '@hcengineering/core'
|
||||
import { isTranscription, isTranscriptionAllowed, startTranscription, stopTranscription } from '../../../utils'
|
||||
import { lkSessionConnected } from '../../../liveKitClient'
|
||||
import love from '../../../plugin'
|
||||
import view from '@hcengineering/view'
|
||||
import { ModernButton } from '@hcengineering/ui'
|
||||
import { Room } from '@hcengineering/love'
|
||||
|
||||
export let room: Room
|
||||
export let size: 'large' | 'medium' | 'small' | 'extra-small' | 'min' = 'large'
|
||||
</script>
|
||||
|
||||
{#if hasAccountRole(getCurrentAccount(), AccountRole.User) && isTranscriptionAllowed() && $lkSessionConnected}
|
||||
<ModernButton
|
||||
icon={view.icon.Feather}
|
||||
iconProps={$isTranscription ? { fill: 'var(--button-negative-BackgroundColor)' } : {}}
|
||||
tooltip={{ label: $isTranscription ? love.string.StopTranscription : love.string.StartTranscription }}
|
||||
kind="secondary"
|
||||
{size}
|
||||
on:click={() => {
|
||||
if ($isTranscription) {
|
||||
void stopTranscription(room)
|
||||
} else {
|
||||
void startTranscription(room)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
+2
-8
@@ -13,15 +13,13 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import love, { MeetingMinutes, Room } from '@hcengineering/love'
|
||||
import love, { MeetingMinutes } from '@hcengineering/love'
|
||||
import { ChannelEmbeddedContent } from '@hcengineering/chunter-resources'
|
||||
import { ActivityMessage } from '@hcengineering/activity'
|
||||
import { updateTabData, WidgetState } from '@hcengineering/workbench-resources'
|
||||
import MeetingWidgetHeader from './MeetingWidgetHeader.svelte'
|
||||
|
||||
export let widgetState: WidgetState
|
||||
export let meetingMinutes: MeetingMinutes
|
||||
export let room: Room
|
||||
export let height: string
|
||||
export let width: string
|
||||
|
||||
@@ -43,8 +41,4 @@
|
||||
on:channel={closeThread}
|
||||
onReply={replyToThread}
|
||||
on:close
|
||||
>
|
||||
<svelte:fragment slot="header">
|
||||
<MeetingWidgetHeader doc={meetingMinutes} {room} on:close />
|
||||
</svelte:fragment>
|
||||
</ChannelEmbeddedContent>
|
||||
></ChannelEmbeddedContent>
|
||||
+52
-18
@@ -18,12 +18,19 @@
|
||||
import { MeetingMinutes, MeetingStatus, Room } from '@hcengineering/love'
|
||||
import { Loading } from '@hcengineering/ui'
|
||||
|
||||
import love from '../../plugin'
|
||||
import love from '../../../plugin'
|
||||
import VideoTab from './VideoTab.svelte'
|
||||
import { currentMeetingMinutes, currentRoom } from '../../stores'
|
||||
import { currentMeetingMinutes, currentRoom } from '../../../stores'
|
||||
import ChatTab from './ChatTab.svelte'
|
||||
import TranscriptionTab from './TranscriptionTab.svelte'
|
||||
import { lkSessionConnected } from '../../liveKitClient'
|
||||
import { lkSessionConnected } from '../../../liveKitClient'
|
||||
import LeaveRoomButton from '../controls/LeaveRoomButton.svelte'
|
||||
import RoomAccessButton from '../controls/RoomAccessButton.svelte'
|
||||
import RecordingButton from '../controls/RecordingButton.svelte'
|
||||
import TranscriptionButton from '../controls/TranscriptionButton.svelte'
|
||||
import ControlBarContainer from '../ControlBarContainer.svelte'
|
||||
import SendReactionButton from '../controls/SendReactionButton.svelte'
|
||||
import MeetingWidgetHeader from './MeetingWidgetHeader.svelte'
|
||||
|
||||
export let widgetState: WidgetState | undefined
|
||||
export let height: string
|
||||
@@ -36,6 +43,8 @@
|
||||
|
||||
let room: Room | undefined = undefined
|
||||
|
||||
let contentHeight: number = 0
|
||||
|
||||
$: room = $currentRoom
|
||||
|
||||
$: if (
|
||||
@@ -70,20 +79,45 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if widgetState && room}
|
||||
{#if widgetState.tab === 'video'}
|
||||
<VideoTab {room} doc={meetingMinutes} on:close={handleClose} />
|
||||
{:else if widgetState.tab === 'chat'}
|
||||
{#if !isMeetingMinutesLoaded}
|
||||
<Loading />
|
||||
{:else if meetingMinutes}
|
||||
<ChatTab {meetingMinutes} {room} {widgetState} {height} {width} on:close={handleClose} />
|
||||
{#if widgetState !== undefined && room}
|
||||
<div>
|
||||
<MeetingWidgetHeader doc={meetingMinutes} {room} on:close={handleClose} />
|
||||
</div>
|
||||
<div style="height: 100%;" bind:clientHeight={contentHeight}>
|
||||
{#if widgetState.tab === 'video'}
|
||||
<VideoTab {room} doc={meetingMinutes} on:close={handleClose} />
|
||||
{:else if widgetState.tab === 'chat'}
|
||||
{#if !isMeetingMinutesLoaded}
|
||||
<Loading />
|
||||
{:else if meetingMinutes}
|
||||
<ChatTab {meetingMinutes} {widgetState} height={contentHeight + 'px'} {width} on:close={handleClose} />
|
||||
{/if}
|
||||
{:else if widgetState.tab === 'transcription'}
|
||||
{#if !isMeetingMinutesLoaded}
|
||||
<Loading />
|
||||
{:else if meetingMinutes}
|
||||
<TranscriptionTab
|
||||
{meetingMinutes}
|
||||
{room}
|
||||
{widgetState}
|
||||
height={contentHeight + 'px'}
|
||||
{width}
|
||||
on:close={handleClose}
|
||||
/>
|
||||
{/if}
|
||||
{/if}
|
||||
{:else if widgetState.tab === 'transcription'}
|
||||
{#if !isMeetingMinutesLoaded}
|
||||
<Loading />
|
||||
{:else if meetingMinutes}
|
||||
<TranscriptionTab {meetingMinutes} {room} {widgetState} {height} {width} on:close={handleClose} />
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
<ControlBarContainer>
|
||||
<svelte:fragment slot="right">
|
||||
<RoomAccessButton {room} size="small" />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="center">
|
||||
<SendReactionButton size="small" />
|
||||
<RecordingButton {room} size="small" />
|
||||
<TranscriptionButton {room} size="small" />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="left">
|
||||
<LeaveRoomButton {room} noLabel={true} size="small" />
|
||||
</svelte:fragment>
|
||||
</ControlBarContainer>
|
||||
{/if}
|
||||
+7
-6
@@ -22,13 +22,13 @@
|
||||
showPopup,
|
||||
PopupResult
|
||||
} from '@hcengineering/ui'
|
||||
import { MeetingMinutes, Room, RoomType } from '@hcengineering/love'
|
||||
import { MeetingMinutes, Room } from '@hcengineering/love'
|
||||
import { onDestroy } from 'svelte'
|
||||
|
||||
import love from '../../plugin'
|
||||
import RoomModal from '../RoomModal.svelte'
|
||||
import { currentRoom } from '../../stores'
|
||||
import { screenSharing } from '../../utils'
|
||||
import love from '../../../plugin'
|
||||
import RoomModal from '../../RoomModal.svelte'
|
||||
import { currentRoom } from '../../../stores'
|
||||
import MeetingOptionsButton from '../controls/MeetingOptionsButton.svelte'
|
||||
|
||||
export let room: Room
|
||||
export let doc: MeetingMinutes | undefined = undefined
|
||||
@@ -56,8 +56,9 @@
|
||||
<Header type={'type-aside'} adaptive={'disabled'} closeOnEscape={false} on:close>
|
||||
<Breadcrumbs items={breadcrumbs} currentOnly />
|
||||
<svelte:fragment slot="actions">
|
||||
{#if ($currentRoom !== undefined && $screenSharing) || $currentRoom?.type === RoomType.Video}
|
||||
{#if $currentRoom !== undefined}
|
||||
<ButtonIcon icon={IconMaximize} kind="tertiary" size="small" noPrint on:click={maximize} />
|
||||
<MeetingOptionsButton {room} kind="tertiary" size="small" />
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</Header>
|
||||
+1
-6
@@ -16,7 +16,6 @@
|
||||
import { MeetingMinutes, Room } from '@hcengineering/love'
|
||||
import { ChannelEmbeddedContent } from '@hcengineering/chunter-resources'
|
||||
import { WidgetState } from '@hcengineering/workbench-resources'
|
||||
import MeetingWidgetHeader from './MeetingWidgetHeader.svelte'
|
||||
|
||||
export let widgetState: WidgetState
|
||||
export let meetingMinutes: MeetingMinutes
|
||||
@@ -33,8 +32,4 @@
|
||||
threadId={undefined}
|
||||
collection="transcription"
|
||||
on:close
|
||||
>
|
||||
<svelte:fragment slot="header">
|
||||
<MeetingWidgetHeader doc={meetingMinutes} {room} on:close />
|
||||
</svelte:fragment>
|
||||
</ChannelEmbeddedContent>
|
||||
></ChannelEmbeddedContent>
|
||||
+1
-3
@@ -15,14 +15,12 @@
|
||||
<script lang="ts">
|
||||
import { MeetingMinutes, Room } from '@hcengineering/love'
|
||||
|
||||
import VideoPopup from '../VideoPopup.svelte'
|
||||
import MeetingWidgetHeader from './MeetingWidgetHeader.svelte'
|
||||
import VideoPopup from '../../VideoPopup.svelte'
|
||||
|
||||
export let room: Room
|
||||
export let doc: MeetingMinutes | undefined = undefined
|
||||
</script>
|
||||
|
||||
<MeetingWidgetHeader {doc} {room} on:close />
|
||||
<div class="root">
|
||||
<VideoPopup room={room._id} isDock />
|
||||
</div>
|
||||
+4
-4
@@ -17,11 +17,11 @@
|
||||
import type { AnySvelteComponent } from '@hcengineering/ui'
|
||||
import { AppItem } from '@hcengineering/workbench-resources'
|
||||
import { RoomType } from '@hcengineering/love'
|
||||
import { currentRoom } from '../../stores'
|
||||
import { isSharingEnabled } from '../../utils'
|
||||
import { currentRoom } from '../../../stores'
|
||||
import { isSharingEnabled } from '../../../utils'
|
||||
import { state } from '@hcengineering/media-resources'
|
||||
import love from '../../plugin'
|
||||
import { lkSessionConnected } from '../../liveKitClient'
|
||||
import love from '../../../plugin'
|
||||
import { lkSessionConnected } from '../../../liveKitClient'
|
||||
|
||||
export let label: IntlString
|
||||
export let icon: Asset | AnySvelteComponent
|
||||
@@ -2,7 +2,7 @@ import { getMetadata, type Resources } from '@hcengineering/platform'
|
||||
import aiBot from '@hcengineering/ai-bot'
|
||||
import { AccountRole, getCurrentAccount, hasAccountRole } from '@hcengineering/core'
|
||||
|
||||
import ControlExt from './components/ControlExt.svelte'
|
||||
import ControlExt from './components/meeting/ControlExt.svelte'
|
||||
import EditMeetingData from './components/EditMeetingData.svelte'
|
||||
import Main from './components/Main.svelte'
|
||||
import MeetingData from './components/MeetingData.svelte'
|
||||
@@ -10,8 +10,8 @@ import SelectScreenSourcePopup from './components/SelectScreenSourcePopup.svelte
|
||||
import Settings from './components/Settings.svelte'
|
||||
import WorkbenchExtension from './components/WorkbenchExtension.svelte'
|
||||
import LoveWidget from './components/LoveWidget.svelte'
|
||||
import MeetingWidget from './components/widget/MeetingWidget.svelte'
|
||||
import WidgetSwitcher from './components/widget/WidgetSwitcher.svelte'
|
||||
import MeetingWidget from './components/meeting/widget/MeetingWidget.svelte'
|
||||
import WidgetSwitcher from './components/meeting/widget/WidgetSwitcher.svelte'
|
||||
import MeetingMinutesPresenter from './components/MeetingMinutesPresenter.svelte'
|
||||
import MeetingMinutesSection from './components/MeetingMinutesSection.svelte'
|
||||
import EditMeetingMinutes from './components/EditMeetingMinutes.svelte'
|
||||
@@ -19,7 +19,6 @@ import EditRoom from './components/EditRoom.svelte'
|
||||
import FloorAttributePresenter from './components/FloorAttributePresenter.svelte'
|
||||
import FloorView from './components/FloorView.svelte'
|
||||
import MeetingMinutesTable from './components/MeetingMinutesTable.svelte'
|
||||
import PanelControlBar from './components/PanelControlBar.svelte'
|
||||
import RoomPresenter from './components/RoomPresenter.svelte'
|
||||
import MeetingMinutesDocEditor from './components/MeetingMinutesDocEditor.svelte'
|
||||
import MeetingMinutesStatusPresenter from './components/MeetingMinutesStatusPresenter.svelte'
|
||||
@@ -62,7 +61,6 @@ export default async (): Promise<Resources> => ({
|
||||
FloorAttributePresenter,
|
||||
FloorView,
|
||||
MeetingMinutesTable,
|
||||
PanelControlBar,
|
||||
RoomPresenter,
|
||||
MeetingMinutesDocEditor,
|
||||
MeetingMinutesStatusPresenter,
|
||||
|
||||
@@ -208,6 +208,7 @@ export class LiveKitClient {
|
||||
}
|
||||
|
||||
async setCameraEnabled (value: boolean): Promise<void> {
|
||||
if (!this.currentSessionSupportsVideo) return
|
||||
try {
|
||||
await this.liveKitRoom.localParticipant.setCameraEnabled(value)
|
||||
} catch (e) {
|
||||
|
||||
@@ -32,7 +32,6 @@ export default mergeIds(loveId, love, {
|
||||
FloorAttributePresenter: '' as AnyComponent,
|
||||
MeetingMinutesTable: '' as AnyComponent,
|
||||
FloorView: '' as AnyComponent,
|
||||
PanelControlBar: '' as AnyComponent,
|
||||
MeetingMinutesDocEditor: '' as AnyComponent,
|
||||
MeetingMinutesStatusPresenter: '' as AnyComponent,
|
||||
MeetingScheduleData: '' as AnyComponent,
|
||||
|
||||
Reference in New Issue
Block a user