Fix meetings (#7213)

Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
This commit is contained in:
Kristina 2024-11-21 20:03:27 +04:00 committed by GitHub
parent f740e09b3d
commit 2d102207ee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 201 additions and 99 deletions

View File

@ -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 '"')

View File

@ -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'
)
}

View File

@ -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,

View File

@ -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;

View File

@ -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}
/>

View File

@ -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

View File

@ -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}

View File

@ -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}

View File

@ -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]) => ({

View File

@ -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 })
}
}
}

View File

@ -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}

View File

@ -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}
/>

View File

@ -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={{

View File

@ -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 }
}

View File

@ -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,