diff --git a/common/scripts/each-diff.sh b/common/scripts/each-diff.sh index 35b367f18b..8fc1d9dada 100755 --- a/common/scripts/each-diff.sh +++ b/common/scripts/each-diff.sh @@ -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 '"') diff --git a/models/love/src/index.ts b/models/love/src/index.ts index 567edb65c4..0467e74658 100644 --- a/models/love/src/index.ts +++ b/models/love/src/index.ts @@ -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' + ) } diff --git a/models/view/src/plugin.ts b/models/view/src/plugin.ts index c1f25d7a67..3c0b6a2bf7 100644 --- a/models/view/src/plugin.ts +++ b/models/view/src/plugin.ts @@ -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, diff --git a/packages/ui/src/components/calendar/DateRangePresenter.svelte b/packages/ui/src/components/calendar/DateRangePresenter.svelte index 1a5226be91..8f73ecc74f 100644 --- a/packages/ui/src/components/calendar/DateRangePresenter.svelte +++ b/packages/ui/src/components/calendar/DateRangePresenter.svelte @@ -478,9 +478,11 @@ {/if} {:else} -
- -
+ {#if shouldShowAvatar} +
+ +
+ {/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; diff --git a/plugins/love-resources/src/components/EditMeetingMinutes.svelte b/plugins/love-resources/src/components/EditMeetingMinutes.svelte index 4b9569a024..e99d438368 100644 --- a/plugins/love-resources/src/components/EditMeetingMinutes.svelte +++ b/plugins/love-resources/src/components/EditMeetingMinutes.svelte @@ -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 { - await client.update(object, { title: object.title }) + await client.diffUpdate(object, { title: newTitle }) } onMount(() => { @@ -41,7 +49,7 @@ diff --git a/plugins/love-resources/src/components/EditRoom.svelte b/plugins/love-resources/src/components/EditRoom.svelte index 1483194cb5..8dbdc824b8 100644 --- a/plugins/love-resources/src/components/EditRoom.svelte +++ b/plugins/love-resources/src/components/EditRoom.svelte @@ -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 diff --git a/plugins/love-resources/src/components/MeetingMinutesDocEditor.svelte b/plugins/love-resources/src/components/MeetingMinutesDocEditor.svelte index 0f33ec2e5d..d3ba1c66af 100644 --- a/plugins/love-resources/src/components/MeetingMinutesDocEditor.svelte +++ b/plugins/love-resources/src/components/MeetingMinutesDocEditor.svelte @@ -33,7 +33,7 @@ {#if doc} - + {#if data} - + {/if} diff --git a/plugins/love-resources/src/components/RoomLanguageSelector.svelte b/plugins/love-resources/src/components/RoomLanguageSelector.svelte index d21634b9c1..4e0e527c60 100644 --- a/plugins/love-resources/src/components/RoomLanguageSelector.svelte +++ b/plugins/love-resources/src/components/RoomLanguageSelector.svelte @@ -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]) => ({ diff --git a/plugins/love-resources/src/utils.ts b/plugins/love-resources/src/utils.ts index 49ffd92888..f906e1bd98 100644 --- a/plugins/love-resources/src/utils.ts +++ b/plugins/love-resources/src/utils.ts @@ -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 { 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 { ]) } +async function navigateToOfficeDoc (hierarchy: Hierarchy, object: Doc): Promise { + 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 { 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() - 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() + 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 }) } } } diff --git a/plugins/view-resources/src/components/DatePresenter.svelte b/plugins/view-resources/src/components/DatePresenter.svelte index 7890bc6d18..daa3d2854c 100644 --- a/plugins/view-resources/src/components/DatePresenter.svelte +++ b/plugins/view-resources/src/components/DatePresenter.svelte @@ -15,18 +15,23 @@ --> -{#if onChange !== undefined} +{#if onChange !== undefined && !readonly} onChange?.(e.detail)} {shouldShowAvatar} @@ -34,5 +39,5 @@ {inline} /> {:else} - + {/if} diff --git a/plugins/view-resources/src/components/DateTimePresenter.svelte b/plugins/view-resources/src/components/DateTimePresenter.svelte new file mode 100644 index 0000000000..da84c3e115 --- /dev/null +++ b/plugins/view-resources/src/components/DateTimePresenter.svelte @@ -0,0 +1,38 @@ + + + + diff --git a/plugins/view-resources/src/components/EditDoc.svelte b/plugins/view-resources/src/components/EditDoc.svelte index ab3082b50f..2e7758a0a5 100644 --- a/plugins/view-resources/src/components/EditDoc.svelte +++ b/plugins/view-resources/src/components/EditDoc.svelte @@ -98,7 +98,9 @@ $: if (_class !== oldClass) { oldClass = _class + realObjectClass = _class mainEditor = undefined + fieldEditors = [] } let keys: KeyedAttribute[] = [] @@ -289,7 +291,7 @@ - {#if headerEditor !== undefined} + {#if headerEditor !== undefined && object._id === _id} { - 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 } } diff --git a/plugins/view-resources/src/index.ts b/plugins/view-resources/src/index.ts index bdc437bc11..bb099c3cc9 100644 --- a/plugins/view-resources/src/index.ts +++ b/plugins/view-resources/src/index.ts @@ -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 => ({ TimestampPresenter, DateEditor, DatePresenter, + DateTimePresenter, RolePresenter, ObjectPresenter, EditDoc,