mirror of
https://github.com/hcengineering/platform.git
synced 2024-11-26 13:47:26 +03:00
Fix meetings (#7213)
Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
This commit is contained in:
parent
f740e09b3d
commit
2d102207ee
@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
FILES=$(git diff origin/main --name-only --diff-filter=ACMR | sed 's| |\\ |g')
|
||||
FILES=$(git diff origin/develop --name-only --diff-filter=ACMR | sed 's| |\\ |g')
|
||||
[ -z "$FILES" ] && exit 0
|
||||
|
||||
roots=$(rush list -p --json | grep "path" | cut -f 2 -d ':' | cut -f 2 -d '"')
|
||||
|
@ -16,14 +16,15 @@
|
||||
import contact, { type Employee, type Person } from '@hcengineering/contact'
|
||||
import {
|
||||
AccountRole,
|
||||
type CollaborativeDoc,
|
||||
type CollectionSize,
|
||||
DateRangeMode,
|
||||
type Doc,
|
||||
type Domain,
|
||||
DOMAIN_TRANSIENT,
|
||||
IndexKind,
|
||||
type Ref,
|
||||
type CollaborativeDoc,
|
||||
type Doc,
|
||||
type Timestamp,
|
||||
type CollectionSize
|
||||
type Timestamp
|
||||
} from '@hcengineering/core'
|
||||
import {
|
||||
type DevicesPreference,
|
||||
@ -32,16 +33,16 @@ import {
|
||||
type JoinRequest,
|
||||
loveId,
|
||||
type Meeting,
|
||||
type MeetingMinutes,
|
||||
type MeetingStatus,
|
||||
type Office,
|
||||
type ParticipantInfo,
|
||||
type RequestStatus,
|
||||
type Room,
|
||||
type RoomAccess,
|
||||
type RoomInfo,
|
||||
type RoomType,
|
||||
type RoomLanguage,
|
||||
type MeetingMinutes,
|
||||
type MeetingStatus
|
||||
type RoomType
|
||||
} from '@hcengineering/love'
|
||||
import {
|
||||
type Builder,
|
||||
@ -53,18 +54,18 @@ import {
|
||||
Model,
|
||||
Prop,
|
||||
ReadOnly,
|
||||
TypeAny,
|
||||
TypeCollaborativeDoc,
|
||||
TypeDate,
|
||||
TypeRef,
|
||||
TypeString,
|
||||
TypeTimestamp,
|
||||
UX,
|
||||
TypeAny
|
||||
UX
|
||||
} from '@hcengineering/model'
|
||||
import calendar, { TEvent } from '@hcengineering/model-calendar'
|
||||
import core, { TAttachedDoc, TDoc } from '@hcengineering/model-core'
|
||||
import preference, { TPreference } from '@hcengineering/model-preference'
|
||||
import presentation from '@hcengineering/model-presentation'
|
||||
import view, { createAction } from '@hcengineering/model-view'
|
||||
import view, { createAction, createAttributePresenter } from '@hcengineering/model-view'
|
||||
import notification from '@hcengineering/notification'
|
||||
import { getEmbeddedLabel } from '@hcengineering/platform'
|
||||
import setting from '@hcengineering/setting'
|
||||
@ -228,12 +229,12 @@ export class TMeetingMinutes extends TAttachedDoc implements MeetingMinutes, Tod
|
||||
@Prop(PropCollection(chunter.class.ChatMessage), activity.string.Messages)
|
||||
messages?: number
|
||||
|
||||
@Prop(TypeTimestamp(), love.string.MeetingStart, { editor: view.component.TimestampPresenter })
|
||||
@Prop(TypeDate(DateRangeMode.DATETIME), love.string.MeetingStart, { editor: view.component.DateTimePresenter })
|
||||
@ReadOnly()
|
||||
@Index(IndexKind.IndexedDsc)
|
||||
declare createdOn: Timestamp
|
||||
|
||||
@Prop(TypeTimestamp(), love.string.MeetingEnd)
|
||||
@Prop(TypeDate(DateRangeMode.DATETIME), love.string.MeetingEnd, { editor: view.component.DateTimePresenter })
|
||||
@ReadOnly()
|
||||
meetingEnd?: Timestamp
|
||||
|
||||
@ -506,10 +507,10 @@ export function createModel (builder: Builder): void {
|
||||
config: [
|
||||
'',
|
||||
{ key: 'status', presenter: love.component.MeetingMinutesStatusPresenter, label: love.string.Status },
|
||||
'createdOn',
|
||||
'meetingEnd',
|
||||
{ key: 'messages', displayProps: { key: 'messages', suffix: true } },
|
||||
{ key: 'transcription', displayProps: { key: 'transcription', suffix: true } }
|
||||
{ key: 'transcription', displayProps: { key: 'transcription', suffix: true } },
|
||||
'createdOn',
|
||||
'meetingEnd'
|
||||
],
|
||||
configOptions: {
|
||||
hiddenKeys: ['description'],
|
||||
@ -529,10 +530,13 @@ export function createModel (builder: Builder): void {
|
||||
config: [
|
||||
'',
|
||||
{ key: 'status', presenter: love.component.MeetingMinutesStatusPresenter, label: love.string.Status },
|
||||
{ key: 'messages', displayProps: { key: 'messages', suffix: true } },
|
||||
{ key: 'transcription', displayProps: { key: 'transcription', suffix: true } },
|
||||
'createdOn',
|
||||
'meetingEnd'
|
||||
],
|
||||
configOptions: {
|
||||
hiddenKeys: ['description'],
|
||||
sortable: true
|
||||
},
|
||||
variant: 'embedded'
|
||||
@ -637,4 +641,19 @@ export function createModel (builder: Builder): void {
|
||||
builder.mixin(love.class.MeetingMinutes, core.class.Class, view.mixin.ObjectPanelFooter, {
|
||||
editor: love.component.PanelControlBar
|
||||
})
|
||||
|
||||
createAttributePresenter(
|
||||
builder,
|
||||
view.component.DateTimePresenter,
|
||||
love.class.MeetingMinutes,
|
||||
'createdOn',
|
||||
'attribute'
|
||||
)
|
||||
createAttributePresenter(
|
||||
builder,
|
||||
view.component.DateTimePresenter,
|
||||
love.class.MeetingMinutes,
|
||||
'meetingEnd',
|
||||
'attribute'
|
||||
)
|
||||
}
|
||||
|
@ -64,6 +64,7 @@ export default mergeIds(viewId, view, {
|
||||
TimestampPresenter: '' as AnyComponent,
|
||||
DateEditor: '' as AnyComponent,
|
||||
DatePresenter: '' as AnyComponent,
|
||||
DateTimePresenter: '' as AnyComponent,
|
||||
TableBrowser: '' as AnyComponent,
|
||||
RolePresenter: '' as AnyComponent,
|
||||
YoutubePresenter: '' as AnyComponent,
|
||||
|
@ -478,9 +478,11 @@
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="btn-icon {iconModifier}">
|
||||
<Icon icon={iconModifier === 'overdue' && !shouldIgnoreOverdue ? DPCalendarOver : DPCalendar} size={'full'} />
|
||||
</div>
|
||||
{#if shouldShowAvatar}
|
||||
<div class="btn-icon {iconModifier}">
|
||||
<Icon icon={iconModifier === 'overdue' && !shouldIgnoreOverdue ? DPCalendarOver : DPCalendar} size={'full'} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if value !== undefined && value !== null && value.toString() !== ''}
|
||||
{#if withDate}
|
||||
{new Date(value).getDate()}
|
||||
@ -524,7 +526,7 @@
|
||||
border-radius: 0.375rem;
|
||||
transition-property: border, background-color, color, box-shadow;
|
||||
transition-duration: 0.15s;
|
||||
cursor: pointer;
|
||||
cursor: default;
|
||||
|
||||
&.noPadding {
|
||||
padding: 0;
|
||||
|
@ -26,8 +26,16 @@
|
||||
const client = getClient()
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let currentTitle = object.title
|
||||
let newTitle = object.title
|
||||
|
||||
$: if (object.title !== currentTitle) {
|
||||
newTitle = object.title
|
||||
currentTitle = object.title
|
||||
}
|
||||
|
||||
async function changeTitle (): Promise<void> {
|
||||
await client.update(object, { title: object.title })
|
||||
await client.diffUpdate(object, { title: newTitle })
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
@ -41,7 +49,7 @@
|
||||
<EditBox
|
||||
disabled={readonly}
|
||||
placeholder={love.string.MeetingMinutes}
|
||||
bind:value={object.title}
|
||||
bind:value={newTitle}
|
||||
on:change={changeTitle}
|
||||
focusIndex={1}
|
||||
/>
|
||||
|
@ -21,7 +21,7 @@
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
|
||||
import love from '../plugin'
|
||||
import { getRoomName, tryConnect } from '../utils'
|
||||
import { getRoomName, tryConnect, isConnected } from '../utils'
|
||||
import { infos, invites, myInfo, myRequests, selectedRoomPlace, myOffice, currentRoom } from '../stores'
|
||||
|
||||
export let object: Room
|
||||
@ -61,7 +61,11 @@
|
||||
selectedRoomPlace.set(undefined)
|
||||
}
|
||||
|
||||
let connectLabel: IntlString = love.string.StartMeeting
|
||||
$: connecting = connecting || ($currentRoom?._id === object._id && !$isConnected)
|
||||
|
||||
let connectLabel: IntlString = $infos.some(({ room }) => room === object._id)
|
||||
? love.string.JoinMeeting
|
||||
: love.string.StartMeeting
|
||||
|
||||
$: if ($infos.some(({ room }) => room === object._id) && !connecting) {
|
||||
connectLabel = love.string.JoinMeeting
|
||||
|
@ -33,7 +33,7 @@
|
||||
</script>
|
||||
|
||||
{#if doc}
|
||||
<span class="label flex-row-center flex-gap-4 ml-3 no-word-wrap">
|
||||
<span class="label flex-row-center flex-gap-4 no-word-wrap">
|
||||
<ObjectPresenter
|
||||
objectId={doc._id}
|
||||
_class={doc._class}
|
||||
|
@ -40,7 +40,7 @@
|
||||
</script>
|
||||
|
||||
{#if data}
|
||||
<span class="flex-row-center" class:ml-3={attributeKey !== undefined}>
|
||||
<span class="flex-row-center">
|
||||
<StateTag type={data.type} label={data.label} />
|
||||
</span>
|
||||
{/if}
|
||||
|
@ -30,6 +30,7 @@
|
||||
|
||||
let container: HTMLElement
|
||||
let selectedItem: RoomLanguage = room.language
|
||||
$: selectedItem = room.language
|
||||
|
||||
let items: DropdownIntlItem[] = []
|
||||
$: items = Object.entries(languagesDisplayData).map(([lang, data]) => ({
|
||||
|
@ -12,7 +12,9 @@ import core, {
|
||||
makeCollaborativeDoc,
|
||||
type Ref,
|
||||
type Space,
|
||||
type TxOperations
|
||||
type TxOperations,
|
||||
type Hierarchy,
|
||||
type Doc
|
||||
} from '@hcengineering/core'
|
||||
import login from '@hcengineering/login'
|
||||
import {
|
||||
@ -79,7 +81,7 @@ import {
|
||||
import { type Widget, type WidgetTab } from '@hcengineering/workbench'
|
||||
import view from '@hcengineering/view'
|
||||
import chunter from '@hcengineering/chunter'
|
||||
import { openDoc } from '@hcengineering/view-resources'
|
||||
import { getObjectLinkFragment } from '@hcengineering/view-resources'
|
||||
|
||||
import { sendMessage } from './broadcast'
|
||||
import love from './plugin'
|
||||
@ -494,6 +496,20 @@ function closeMeetingMinutes (): void {
|
||||
currentMeetingMinutes.set(undefined)
|
||||
}
|
||||
|
||||
function isRoomOpened (room: Room): boolean {
|
||||
const loc = getCurrentLocation()
|
||||
|
||||
if (loc.path[2] === loveId) {
|
||||
const panel = get(panelstore).panel
|
||||
const { _id } = panel ?? {}
|
||||
|
||||
if (_id !== undefined && room._id !== undefined && _id === room._id) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export async function setCam (value: boolean): Promise<void> {
|
||||
if (value && get(currentRoom)?.type !== RoomType.Video) return
|
||||
if ($isCurrentInstanceConnected) {
|
||||
@ -604,13 +620,9 @@ async function moveToRoom (
|
||||
sessionId
|
||||
})
|
||||
}
|
||||
const loc = getCurrentLocation()
|
||||
if (room.type === RoomType.Video && loc.path[2] !== loveId) {
|
||||
loc.path[2] = loveId
|
||||
loc.path.length = 3
|
||||
loc.fragment = undefined
|
||||
loc.query = undefined
|
||||
navigate(loc)
|
||||
|
||||
if (!isRoomOpened(room)) {
|
||||
await navigateToOfficeDoc(client.getHierarchy(), room)
|
||||
}
|
||||
}
|
||||
|
||||
@ -622,63 +634,70 @@ async function connectLK (currentPerson: Person, room: Room): Promise<void> {
|
||||
])
|
||||
}
|
||||
|
||||
async function navigateToOfficeDoc (hierarchy: Hierarchy, object: Doc): Promise<void> {
|
||||
const panelComponent = hierarchy.classHierarchyMixin(object._class, view.mixin.ObjectPanel)
|
||||
const comp = panelComponent?.component ?? view.component.EditDoc
|
||||
const loc = await getObjectLinkFragment(hierarchy, object, {}, comp)
|
||||
loc.path[2] = loveId
|
||||
loc.path.length = 3
|
||||
loc.query = undefined
|
||||
navigate(loc)
|
||||
}
|
||||
|
||||
async function openMeetingMinutes (room: Room): Promise<void> {
|
||||
const client = getClient()
|
||||
const sid = await lk.getSid()
|
||||
const doc = await client.findOne(love.class.MeetingMinutes, { sid })
|
||||
|
||||
if (sid !== undefined) {
|
||||
const doc = await client.findOne(love.class.MeetingMinutes, { sid })
|
||||
|
||||
if (doc === undefined) {
|
||||
const date = new Date()
|
||||
.toLocaleDateString('en-GB', {
|
||||
day: 'numeric',
|
||||
month: 'long',
|
||||
year: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
hour12: false,
|
||||
timeZone: 'UTC'
|
||||
})
|
||||
.replace(',', ' at')
|
||||
const _id = generateId<MeetingMinutes>()
|
||||
const newDoc: MeetingMinutes = {
|
||||
_id,
|
||||
_class: love.class.MeetingMinutes,
|
||||
sid,
|
||||
attachedTo: room._id,
|
||||
attachedToClass: room._class,
|
||||
collection: 'meetings',
|
||||
space: core.space.Workspace,
|
||||
title: `${getRoomName(room, get(personByIdStore))} ${date}`,
|
||||
description: makeCollaborativeDoc(_id, 'description'),
|
||||
status: MeetingStatus.Active,
|
||||
modifiedBy: getCurrentAccount()._id,
|
||||
modifiedOn: Date.now()
|
||||
}
|
||||
await client.addCollection(
|
||||
love.class.MeetingMinutes,
|
||||
core.space.Workspace,
|
||||
room._id,
|
||||
room._class,
|
||||
'meetings',
|
||||
{ sid, title: newDoc.title, description: newDoc.description, status: newDoc.status },
|
||||
_id
|
||||
)
|
||||
currentMeetingMinutes.set(newDoc)
|
||||
const loc = getCurrentLocation()
|
||||
if (loc.path[2] === loveId) {
|
||||
await openDoc(client.getHierarchy(), newDoc)
|
||||
}
|
||||
} else {
|
||||
currentMeetingMinutes.set(doc)
|
||||
const loc = getCurrentLocation()
|
||||
if (loc.path[2] === loveId) {
|
||||
await openDoc(client.getHierarchy(), doc)
|
||||
}
|
||||
if (doc.status !== MeetingStatus.Active) {
|
||||
void client.update(doc, { status: MeetingStatus.Active, meetingEnd: undefined })
|
||||
}
|
||||
if (doc === undefined) {
|
||||
const date = new Date()
|
||||
.toLocaleDateString('en-GB', {
|
||||
day: 'numeric',
|
||||
month: 'long',
|
||||
year: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
hour12: false,
|
||||
timeZone: 'UTC'
|
||||
})
|
||||
.replace(',', ' at')
|
||||
const _id = generateId<MeetingMinutes>()
|
||||
const newDoc: MeetingMinutes = {
|
||||
_id,
|
||||
_class: love.class.MeetingMinutes,
|
||||
sid,
|
||||
attachedTo: room._id,
|
||||
attachedToClass: room._class,
|
||||
collection: 'meetings',
|
||||
space: core.space.Workspace,
|
||||
title: `${getRoomName(room, get(personByIdStore))} ${date}`,
|
||||
description: makeCollaborativeDoc(_id, 'description'),
|
||||
status: MeetingStatus.Active,
|
||||
modifiedBy: getCurrentAccount()._id,
|
||||
modifiedOn: Date.now()
|
||||
}
|
||||
await client.addCollection(
|
||||
love.class.MeetingMinutes,
|
||||
core.space.Workspace,
|
||||
room._id,
|
||||
room._class,
|
||||
'meetings',
|
||||
{ sid, title: newDoc.title, description: newDoc.description, status: newDoc.status },
|
||||
_id
|
||||
)
|
||||
currentMeetingMinutes.set(newDoc)
|
||||
const loc = getCurrentLocation()
|
||||
if (loc.path[2] === loveId || room.type === RoomType.Video) {
|
||||
await navigateToOfficeDoc(client.getHierarchy(), newDoc)
|
||||
}
|
||||
} else {
|
||||
currentMeetingMinutes.set(doc)
|
||||
const loc = getCurrentLocation()
|
||||
if (loc.path[2] === loveId || room.type === RoomType.Video) {
|
||||
await navigateToOfficeDoc(client.getHierarchy(), doc)
|
||||
}
|
||||
if (doc.status !== MeetingStatus.Active) {
|
||||
void client.update(doc, { status: MeetingStatus.Active, meetingEnd: undefined })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,18 +15,23 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { DateRangePresenter } from '@hcengineering/ui'
|
||||
import { DateRangeMode } from '@hcengineering/core'
|
||||
|
||||
export let value: number | null | undefined
|
||||
export let onChange: ((value: number | null) => void) | undefined = undefined
|
||||
export let noShift: boolean = false
|
||||
export let shouldShowAvatar: boolean = true
|
||||
export let accent: boolean = false
|
||||
export let inline: boolean = false
|
||||
export let mode: DateRangeMode = DateRangeMode.DATE
|
||||
export let readonly: boolean = false
|
||||
</script>
|
||||
|
||||
{#if onChange !== undefined}
|
||||
{#if onChange !== undefined && !readonly}
|
||||
<DateRangePresenter
|
||||
{value}
|
||||
{noShift}
|
||||
{mode}
|
||||
editable
|
||||
on:change={(e) => onChange?.(e.detail)}
|
||||
{shouldShowAvatar}
|
||||
@ -34,5 +39,5 @@
|
||||
{inline}
|
||||
/>
|
||||
{:else}
|
||||
<DateRangePresenter {value} {noShift} {shouldShowAvatar} {accent} {inline} />
|
||||
<DateRangePresenter {value} {noShift} {mode} {shouldShowAvatar} {accent} {inline} />
|
||||
{/if}
|
||||
|
@ -0,0 +1,38 @@
|
||||
<!--
|
||||
// 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 { DateRangeMode } from '@hcengineering/core'
|
||||
|
||||
import DatePresenter from './DatePresenter.svelte'
|
||||
|
||||
export let value: number | null | undefined
|
||||
export let onChange: ((value: number | null) => void) | undefined = undefined
|
||||
export let noShift: boolean = false
|
||||
export let shouldShowAvatar: boolean = false
|
||||
export let accent: boolean = false
|
||||
export let inline: boolean = false
|
||||
export let readonly: boolean = false
|
||||
</script>
|
||||
|
||||
<DatePresenter
|
||||
{value}
|
||||
{onChange}
|
||||
{noShift}
|
||||
{shouldShowAvatar}
|
||||
{accent}
|
||||
{inline}
|
||||
mode={DateRangeMode.DATETIME}
|
||||
{readonly}
|
||||
/>
|
@ -98,7 +98,9 @@
|
||||
|
||||
$: if (_class !== oldClass) {
|
||||
oldClass = _class
|
||||
realObjectClass = _class
|
||||
mainEditor = undefined
|
||||
fieldEditors = []
|
||||
}
|
||||
|
||||
let keys: KeyedAttribute[] = []
|
||||
@ -289,7 +291,7 @@
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="attributes" let:direction={dir}>
|
||||
{#if headerEditor !== undefined}
|
||||
{#if headerEditor !== undefined && object._id === _id}
|
||||
<Component
|
||||
is={headerEditor}
|
||||
props={{
|
||||
|
@ -238,13 +238,14 @@
|
||||
}
|
||||
|
||||
const joinProps = (attribute: AttributeModel, object: Doc, readonly: boolean) => {
|
||||
const readonlyParams = readonly
|
||||
? {
|
||||
readonly: true,
|
||||
editable: false,
|
||||
disabled: true
|
||||
}
|
||||
: {}
|
||||
const readonlyParams =
|
||||
readonly || (attribute?.attribute?.readonly ?? false)
|
||||
? {
|
||||
readonly: true,
|
||||
editable: false,
|
||||
disabled: true
|
||||
}
|
||||
: {}
|
||||
if (attribute.collectionAttr) {
|
||||
return { object, ...attribute.props, ...readonlyParams }
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ import CollaborativeHTMLEditor from './components/CollaborativeHTMLEditor.svelte
|
||||
import ColorsPopup from './components/ColorsPopup.svelte'
|
||||
import DateEditor from './components/DateEditor.svelte'
|
||||
import DatePresenter from './components/DatePresenter.svelte'
|
||||
import DateTimePresenter from './components/DateTimePresenter.svelte'
|
||||
import DocAttributeBar from './components/DocAttributeBar.svelte'
|
||||
import DocNavLink from './components/DocNavLink.svelte'
|
||||
import DocReferencePresenter from './components/DocReferencePresenter.svelte'
|
||||
@ -257,6 +258,7 @@ export default async (): Promise<Resources> => ({
|
||||
TimestampPresenter,
|
||||
DateEditor,
|
||||
DatePresenter,
|
||||
DateTimePresenter,
|
||||
RolePresenter,
|
||||
ObjectPresenter,
|
||||
EditDoc,
|
||||
|
Loading…
Reference in New Issue
Block a user