diff --git a/communication b/communication index 88ecdd1be7..ffed29fc6b 160000 --- a/communication +++ b/communication @@ -1 +1 @@ -Subproject commit 88ecdd1be78105f616ffd2b217b65fea8f8ebf25 +Subproject commit ffed29fc6bbdf7e7d766ee902f3c5a7089098d5a diff --git a/models/communication/package.json b/models/communication/package.json index 2c46d85dec..039d08bfc7 100644 --- a/models/communication/package.json +++ b/models/communication/package.json @@ -32,6 +32,7 @@ "@hcengineering/card": "^0.6.0", "@hcengineering/communication": "^0.6.0", "@hcengineering/communication-resources": "^0.6.0", + "@hcengineering/communication-types": "^0.1.0", "@hcengineering/contact": "^0.6.24", "@hcengineering/core": "^0.6.32", "@hcengineering/model": "^0.6.11", diff --git a/models/communication/src/applets.ts b/models/communication/src/applets.ts new file mode 100644 index 0000000000..fdb23f2f92 --- /dev/null +++ b/models/communication/src/applets.ts @@ -0,0 +1,35 @@ +// Copyright © 2025 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. + +import { type Builder } from '@hcengineering/model' +import core from '@hcengineering/core' + +import communication from './plugin' + +export function buildApplets (builder: Builder): void { + builder.createDoc( + communication.class.Applet, + core.space.Model, + { + type: 'application/vnd.huly.applet.poll', + label: communication.string.Poll, + icon: communication.icon.Poll, + component: communication.poll.PollPresenter, + createLabel: communication.string.CreatePoll, + createComponent: communication.poll.CreatePoll, + previewComponent: communication.poll.PollPreview, + createFn: communication.poll.CreatePollFn + }, + communication.ids.PollApplet + ) +} diff --git a/models/communication/src/index.ts b/models/communication/src/index.ts index 1d80155ce8..c946db4ddd 100644 --- a/models/communication/src/index.ts +++ b/models/communication/src/index.ts @@ -19,6 +19,7 @@ import { MessagesNavigationAnchors } from '@hcengineering/communication' import communication from './plugin' import { buildTypes } from './types' import { buildCardActions, buildMessageActions } from './actions' +import { buildApplets } from './applets' export { communicationId } from '@hcengineering/communication' export * from './migration' @@ -27,6 +28,7 @@ export function createModel (builder: Builder): void { buildTypes(builder) buildMessageActions(builder) buildCardActions(builder) + buildApplets(builder) builder.createDoc( card.class.CardSection, diff --git a/models/communication/src/plugin.ts b/models/communication/src/plugin.ts index b3c3324de1..f78c3eb024 100644 --- a/models/communication/src/plugin.ts +++ b/models/communication/src/plugin.ts @@ -13,9 +13,9 @@ // limitations under the License. // -import { communicationId } from '@hcengineering/communication' +import { communicationId, type Poll } from '@hcengineering/communication' import communication from '@hcengineering/communication-resources/src/plugin' -import {} from '@hcengineering/core' +import { type Attribute, type Ref } from '@hcengineering/core' import {} from '@hcengineering/ui' import { mergeIds, type Resource } from '@hcengineering/platform' import { type ViewAction } from '@hcengineering/model-view' @@ -29,5 +29,8 @@ export default mergeIds(communicationId, communication, { function: { CanSubscribe: '' as Resource<(doc: Card | Card[] | undefined) => Promise>, CanUnsubscribe: '' as Resource<(doc: Card | Card[] | undefined) => Promise> + }, + ids: { + UserVotesAttribute: '' as Ref> } }) diff --git a/models/communication/src/types.ts b/models/communication/src/types.ts index c5ffc40e0a..a589bb3287 100644 --- a/models/communication/src/types.ts +++ b/models/communication/src/types.ts @@ -11,15 +11,30 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { type Builder, Model } from '@hcengineering/model' -import core, { TDoc } from '@hcengineering/model-core' -import { DOMAIN_MODEL } from '@hcengineering/core' +import { type Builder, Model, TypeAny, TypeNumber } from '@hcengineering/model' +import core, { TAttachedDoc, TDoc } from '@hcengineering/model-core' +import { type Class, type Domain, DOMAIN_MODEL, type Ref } from '@hcengineering/core' import { type Asset, type IntlString } from '@hcengineering/platform' -import communication, { +import { + type Applet, type MessageAction, type MessageActionFunctionResource, - type MessageActionVisibilityTesterResource + type MessageActionVisibilityTesterResource, + type AppletCreateFnResource, + type PollAnswer, + type Poll, + type CustomActivityPresenter } from '@hcengineering/communication' +import { PaletteColorIndexes } from '@hcengineering/ui/src/colors' +import { type AppletType } from '@hcengineering/communication-types' +import { createSystemType } from '@hcengineering/model-card' +import type { AnyComponent } from '@hcengineering/ui' +import { type PersonSpace } from '@hcengineering/contact' + +import communication from './plugin' +import { type MasterTag } from '@hcengineering/card' + +export const DOMAIN_POLL = 'poll' as Domain @Model(communication.class.MessageAction, core.class.Doc, DOMAIN_MODEL) class TMessageAction extends TDoc implements MessageAction { @@ -32,6 +47,74 @@ class TMessageAction extends TDoc implements MessageAction { menu?: boolean } -export function buildTypes (builder: Builder): void { - builder.createModel(TMessageAction) +@Model(communication.class.Applet, core.class.Doc, DOMAIN_MODEL) +class TApplet extends TDoc implements Applet { + type!: AppletType + icon!: Asset + label!: IntlString + component!: AnyComponent + createLabel!: IntlString + createComponent!: AnyComponent + previewComponent!: AnyComponent + createFn?: AppletCreateFnResource +} + +@Model(communication.class.PollAnswer, core.class.Doc, DOMAIN_POLL) +class TPollAnswer extends TAttachedDoc implements PollAnswer { + options!: string[] + declare attachedTo: Ref + declare attachedToClass: Ref> + declare space: Ref +} + +@Model(communication.class.CustomActivityPresenter, core.class.Doc, DOMAIN_MODEL) +class TCustomActivityPresenter extends TDoc implements CustomActivityPresenter { + attribute!: string + component!: AnyComponent + type!: Ref +} + +export function buildTypes (builder: Builder): void { + builder.createModel(TMessageAction, TApplet, TPollAnswer, TCustomActivityPresenter) + + createSystemType( + builder, + communication.type.Poll, + communication.icon.Poll, + communication.string.Poll, + communication.string.Polls, + undefined, + PaletteColorIndexes.Cerulean + ) + + builder.createDoc(core.class.Attribute, core.space.Model, { + name: 'totalVotes', + attributeOf: communication.type.Poll, + type: TypeNumber(), + label: communication.string.TotalVotes, + readonly: true + }) + + builder.createDoc( + core.class.Attribute, + core.space.Model, + { + name: 'userVotes', + attributeOf: communication.type.Poll, + type: TypeAny( + communication.poll.UserVotesPresenter, + communication.string.Voted, + communication.poll.UserVotesPresenter + ), + label: communication.string.Voted, + readonly: true + }, + communication.ids.UserVotesAttribute + ) + + builder.createDoc(communication.class.CustomActivityPresenter, core.space.Model, { + attribute: 'userVotes', + type: communication.type.Poll, + component: communication.poll.UserVoteActivityPresenter + }) } diff --git a/packages/presentation/src/communication.ts b/packages/presentation/src/communication.ts index bf3b5134ac..b570a3efe0 100644 --- a/packages/presentation/src/communication.ts +++ b/packages/presentation/src/communication.ts @@ -14,27 +14,23 @@ // import { initLiveQueries, refreshLiveQueries } from '@hcengineering/communication-client-query' import { + type AddAttachmentsOperation, type AddCollaboratorsEvent, - type AttachBlobsOperation, - type AttachLinkPreviewsOperation, - type BlobPatchEvent, + type AttachmentPatchEvent, type CreateMessageEvent, type CreateMessageResult, - type DetachBlobsOperation, - type UpdateBlobsOperation, - type DetachLinkPreviewsOperation, type Event, type EventResult, - type LinkPreviewPatchEvent, MessageEventType, NotificationEventType, type ReactionPatchEvent, + type RemoveAttachmentsOperation, type RemoveCollaboratorsEvent, type RemoveNotificationContextEvent, type RemovePatchEvent, - type SetBlobsOperation, - type SetLinkPreviewsOperation, + type SetAttachmentsOperation, type ThreadPatchEvent, + type UpdateAttachmentsOperation, type UpdateNotificationContextEvent, type UpdateNotificationEvent, type UpdateNotificationQuery, @@ -42,8 +38,6 @@ import { } from '@hcengineering/communication-sdk-types' import { type AccountID, - type BlobData, - type BlobID, type CardID, type CardType, type Collaborator, @@ -55,8 +49,6 @@ import { type FindNotificationContextParams, type FindNotificationsParams, type Label, - type LinkPreviewData, - type LinkPreviewID, type Markdown, type Message, type MessageID, @@ -65,7 +57,10 @@ import { type Notification, type NotificationContext, type SocialID, - type BlobUpdateData + type AttachmentID, + type AttachmentData, + type AttachmentParams, + type AttachmentUpdateData } from '@hcengineering/communication-types' import core, { generateId, @@ -78,10 +73,10 @@ import core, { AccountRole } from '@hcengineering/core' import { onDestroy } from 'svelte' -import { generateLinkPreviewId } from '@hcengineering/communication-shared' import { addNotification, NotificationSeverity, languageStore } from '@hcengineering/ui' import { translate } from '@hcengineering/platform' import view from '@hcengineering/view' +import { v4 as uuid } from 'uuid' import { getCurrentWorkspaceUuid, getFilesUrl } from './file' import { addTxListener, removeTxListener, type TxListener } from './utils' @@ -118,6 +113,13 @@ export async function setCommunicationClient (platformClient: PlatformClient): P }) } +export type AttachmentDataWithOptionalId

= Omit< +AttachmentData

, +'id' +> & { + id?: AttachmentID +} + const COMMUNICATION = 'communication' as OperationDomain class Client { @@ -223,100 +225,58 @@ class Client { await this.sendEvent(event) } - async blobPatch ( + async attachmentPatch

( cardId: CardID, messageId: MessageID, ops: { - attach?: BlobData[] - detach?: BlobID[] - set?: BlobData[] - update?: BlobUpdateData[] + add?: Array> + remove?: AttachmentID[] + set?: Array> + update?: Array> } ): Promise { - const operations: Array = [] + const operations: Array< + AddAttachmentsOperation | RemoveAttachmentsOperation | SetAttachmentsOperation | UpdateAttachmentsOperation + > = [] - if (ops.attach != null && ops.attach.length > 0) { + if (ops.add != null && ops.add.length > 0) { operations.push({ - opcode: 'attach', - blobs: ops.attach + opcode: 'add', + attachments: ops.add.map((it) => ({ + ...it, + id: it.id ?? (uuid() as AttachmentID) + })) }) } - if (ops.detach != null && ops.detach.length > 0) { + if (ops.remove != null && ops.remove.length > 0) { operations.push({ - opcode: 'detach', - blobIds: ops.detach + opcode: 'remove', + ids: ops.remove }) } if (ops.set != null && ops.set.length > 0) { operations.push({ opcode: 'set', - blobs: ops.set + attachments: ops.set.map((it) => ({ + ...it, + id: it.id ?? (uuid() as AttachmentID) + })) }) } if (ops.update != null && ops.update.length > 0) { operations.push({ opcode: 'update', - blobs: ops.update + attachments: ops.update }) } if (operations.length === 0) return - const event: BlobPatchEvent = { - type: MessageEventType.BlobPatch, - cardId, - messageId, - operations, - socialId: this.getSocialId() - } - await this.sendEvent(event) - } - - async linkPreviewPatch ( - cardId: CardID, - messageId: MessageID, - ops: { - attach?: LinkPreviewData[] - detach?: LinkPreviewID[] - set?: LinkPreviewData[] - } - ): Promise { - const operations: Array = [] - - if (ops.attach != null && ops.attach.length > 0) { - operations.push({ - opcode: 'attach', - previews: ops.attach.map((it) => ({ - ...it, - previewId: generateLinkPreviewId() - })) - }) - } - - if (ops.detach != null && ops.detach.length > 0) { - operations.push({ - opcode: 'detach', - previewIds: ops.detach - }) - } - - if (ops.set != null && ops.set.length > 0) { - operations.push({ - opcode: 'set', - previews: ops.set.map((it) => ({ - ...it, - previewId: generateLinkPreviewId() - })) - }) - } - - if (operations.length === 0) return - - const event: LinkPreviewPatchEvent = { - type: MessageEventType.LinkPreviewPatch, + const event: AttachmentPatchEvent = { + type: MessageEventType.AttachmentPatch, cardId, messageId, operations, diff --git a/packages/ui/src/components/CheckBox.svelte b/packages/ui/src/components/CheckBox.svelte index b33462f801..e1dce9db98 100644 --- a/packages/ui/src/components/CheckBox.svelte +++ b/packages/ui/src/components/CheckBox.svelte @@ -22,6 +22,7 @@ export let kind: 'default' | 'primary' | 'positive' | 'negative' | 'todo' = 'default' export let color: string | undefined = undefined export let readonly: boolean = false + export let disabled: boolean = false const dispatch = createEventDispatcher() @@ -50,9 +51,16 @@ class:circle class:readonly class:checked + class:disabled on:click|stopPropagation > - +

@@ -63,6 +71,11 @@ align-items: center; flex-shrink: 0; + &.disabled { + pointer-events: none; + cursor: default; + } + .checkSVG { position: relative; diff --git a/packages/ui/src/components/Modal.svelte b/packages/ui/src/components/Modal.svelte index b3afcf6ff8..1b63f232d9 100644 --- a/packages/ui/src/components/Modal.svelte +++ b/packages/ui/src/components/Modal.svelte @@ -19,13 +19,14 @@ import Label from './Label.svelte' import ButtonBase from './ButtonBase.svelte' import Scroller from './Scroller.svelte' - import ui from '..' + import ui, { LabelAndProps } from '..' export let type: 'type-aside' | 'type-popup' | 'type-component' export let width: 'large' | 'medium' | 'small' | 'x-small' | 'menu' | undefined = undefined export let label: IntlString | undefined = undefined export let labelProps: any | undefined = undefined export let okAction: () => Promise | void = () => {} + export let okTooltip: LabelAndProps | undefined = undefined export let onCancel: (() => void) | undefined = undefined export let canSave: boolean = false export let okLabel: IntlString = ui.string.Ok @@ -109,6 +110,7 @@ type={'type-button'} kind={'primary'} size={type === 'type-aside' ? 'large' : 'medium'} + tooltip={okTooltip} label={okLabel} on:click={okAction} disabled={!canSave} diff --git a/packages/ui/src/components/ModernEditbox.svelte b/packages/ui/src/components/ModernEditbox.svelte index bc2e4c5f9e..0c5eed4e78 100644 --- a/packages/ui/src/components/ModernEditbox.svelte +++ b/packages/ui/src/components/ModernEditbox.svelte @@ -119,6 +119,7 @@ }} /> {/if} + {#if labeled}
{/if} diff --git a/plugins/communication-assets/assets/icons.svg b/plugins/communication-assets/assets/icons.svg index eaf6a91e20..b277d94031 100644 --- a/plugins/communication-assets/assets/icons.svg +++ b/plugins/communication-assets/assets/icons.svg @@ -34,4 +34,8 @@ d="M22.2645 18.4346C23.8127 15.061 21.4542 11.3919 18.3526 10.5041C16.8345 10.0007 15.1435 10.203 13.7238 10.8708C12.2987 11.5412 11.0662 12.7196 10.5459 14.2602C9.54062 17.2366 11.4209 20.8545 14.6395 21.8814C15.9617 22.3032 17.5346 22.1569 18.1927 21.9491C18.431 22.0161 18.7158 22.1372 19.0197 22.2664C19.7954 22.5963 20.6952 22.979 21.2615 22.6468C21.5475 22.479 21.6604 22.1705 21.6471 21.8546C21.6294 21.4301 21.573 21.0054 21.5167 20.5813C21.4805 20.3088 21.4443 20.0367 21.4185 19.7651C21.7475 19.356 22.0453 18.9123 22.2645 18.4346ZM11.9671 14.7402C12.3308 13.6633 13.226 12.7627 14.3623 12.2282C15.4988 11.6936 16.7959 11.564 17.8928 11.932L17.9099 11.9377L17.9273 11.9426C20.3341 12.6228 21.9859 15.4235 20.9068 17.7968C20.633 18.3376 20.2858 18.8072 19.8658 19.2409C19.9138 19.791 19.9655 20.3406 20.03 20.889C19.7776 20.8166 19.5238 20.7496 19.2701 20.6826C18.8945 20.5833 18.5189 20.4841 18.148 20.3677L17.9034 20.4622C16.9745 20.8209 15.9738 20.7129 15.0414 20.436C12.3639 19.7416 11.2065 16.9918 11.9671 14.7402Z" /> + + + diff --git a/plugins/communication-assets/lang/cs.json b/plugins/communication-assets/lang/cs.json index e0dfbc84fa..3a1e8a55cf 100644 --- a/plugins/communication-assets/lang/cs.json +++ b/plugins/communication-assets/lang/cs.json @@ -51,6 +51,39 @@ "EditMessage": "Upravit zprávu", "RemoveMessage": "Odstranit zprávu", "CreateCard": "Vytvořit kartu", - "MessageAlreadyHasCardAttached": "Jejda! Tato zpráva již má připojenou kartu." + "MessageAlreadyHasCardAttached": "Jejda! Tato zpráva již má připojenou kartu.", + "CreatePoll": "Vytvořit anketu", + "Poll": "Anketa", + "QuestionIsRequired": "Je vyžadována otázka.", + "AnswerIsRequired": "Je vyžadována odpověď.", + "OptionIsRequired": "Je vyžadována možnost.", + "StartDateMustBeInTheFuture": "Datum zahájení musí být v budoucnosti.", + "EndDateMustBeInTheFuture": "Datum ukončení musí být v budoucnosti.", + "Question": "Otázka", + "AskQuestion": "Zadejte otázku", + "PollOptions": "Možnosti ankety", + "Option": "Možnost", + "AnonymousVoting": "Anonymní hlasování", + "MultipleChoice": "Vícenásobný výběr", + "QuizMode": "Režim testování", + "StartTime": "Čas zahájení", + "EndTime": "Čas ukončení", + "OpenPoll": "Otevřít anketu", + "RetractVote": "Zrušit hlasování", + "Quiz": "Test", + "VotesCount": "{count, plural, =0 {žádné hlasy} =1 {1 hlas} other {# hlasů}}", + "Vote": "Hlasovat", + "ShowResults": "Zobrazit výsledky", + "Ended": "Končí", + "StartsAt": "Začíná v {date}", + "EndsAt": "Končí v {date}", + "StartsTomorrow": "Začíná v zítra v {date}", + "EndsTomorrow": "Končí v zítra v {date}", + "PollResults": "Výsledky ankety", + "Polls": "Ankety", + "TotalVotes": "Celkem hlasů", + "Voted": "Hlasováno", + "VotedFor": "Hlasováno pro", + "RevokedVote": "Zrušeno hlasování" } } diff --git a/plugins/communication-assets/lang/de.json b/plugins/communication-assets/lang/de.json index 1f142816ed..1b632f1cac 100644 --- a/plugins/communication-assets/lang/de.json +++ b/plugins/communication-assets/lang/de.json @@ -51,6 +51,39 @@ "EditMessage": "Nachricht bearbeiten", "RemoveMessage": "Nachricht entfernen", "CreateCard": "Karte erstellen", - "MessageAlreadyHasCardAttached": "Oops! Diese Nachricht hat bereits eine Karte angehängt." + "MessageAlreadyHasCardAttached": "Oops! Diese Nachricht hat bereits eine Karte angehängt.", + "CreatePoll": "Umfrage erstellen", + "Poll": "Umfrage", + "QuestionIsRequired": "Eine Frage ist erforderlich.", + "AnswerIsRequired": "Eine Antwort ist erforderlich.", + "OptionIsRequired": "Eine Option ist erforderlich.", + "StartDateMustBeInTheFuture": "Das Startdatum muss in der Zukunft liegen.", + "EndDateMustBeInTheFuture": "Das Enddatum muss in der Zukunft liegen.", + "Question": "Frage", + "AskQuestion": "Frage stellen", + "PollOptions": "Umfrageoptionen", + "Option": "Option", + "AnonymousVoting": "Anonymes Abstimmen", + "MultipleChoice": "Multiple-Choice", + "QuizMode": "Quiz-Modus", + "StartTime": "Startzeit", + "EndTime": "Endzeit", + "OpenPoll": "Umfrage öffnen", + "RetractVote": "Abstimmung widerrufen", + "Quiz": "Quiz", + "VotesCount": "{count, plural, =0 {keine Stimmen} =1 {1 Stimme} other {# Stimmen}}", + "Vote": "Abstimmen", + "ShowResults": "Ergebnisse anzeigen", + "Ended": "Endet", + "StartsAt": "Beginnt um {date}", + "EndsAt": "Endet um {date}", + "StartsTomorrow": "Beginnt morgen um {date}", + "EndsTomorrow": "Endet morgen um {date}", + "PollResults": "Umfrageergebnisse", + "Polls": "Umfragen", + "TotalVotes": "Gesamtstimmen", + "Voted": "Abgestimmt", + "VotedFor": "Für abgestimmt", + "RevokedVote": "Stimme widerrufen" } } diff --git a/plugins/communication-assets/lang/en.json b/plugins/communication-assets/lang/en.json index 7c65d56bc1..9e88827462 100644 --- a/plugins/communication-assets/lang/en.json +++ b/plugins/communication-assets/lang/en.json @@ -51,6 +51,39 @@ "EditMessage": "Edit message", "RemoveMessage": "Remove message", "CreateCard": "Create card", - "MessageAlreadyHasCardAttached": "Oops! This message already has a card attached." + "MessageAlreadyHasCardAttached": "Oops! This message already has a card attached.", + "CreatePoll": "Create poll", + "Poll": "Poll", + "QuestionIsRequired": "Question is required.", + "AnswerIsRequired": "Answer is required.", + "OptionIsRequired": "Option is required.", + "StartDateMustBeInTheFuture": "Start date must be in the future.", + "EndDateMustBeInTheFuture": "End date must be in the future.", + "Question": "Question", + "AskQuestion": "Ask a question", + "PollOptions": "Poll options", + "Option": "Option", + "AnonymousVoting": "Anonymous voting", + "MultipleChoice": "Multiple choice", + "QuizMode": "Quiz mode", + "StartTime": "Start time", + "EndTime": "End time", + "OpenPoll": "Open poll", + "RetractVote": "Retract vote", + "Quiz": "Quiz", + "VotesCount": "{count, plural, =0 {no votes} =1 {1 vote} other {# votes}}", + "Vote": "Vote", + "ShowResults": "Show results", + "Ended": "Ended", + "StartsAt": "Starts at {date}", + "EndsAt": "Ends at {date}", + "StartsTomorrow": "Starts tomorrow at {date}", + "EndsTomorrow": "Ends tomorrow at {date}", + "PollResults": "Poll results", + "Polls": "Polls", + "TotalVotes": "Total votes", + "Voted": "Voted", + "VotedFor": "Voted for", + "RevokedVote": "Revoked vote" } } diff --git a/plugins/communication-assets/lang/es.json b/plugins/communication-assets/lang/es.json index 36662c40f4..c9d7d1492e 100644 --- a/plugins/communication-assets/lang/es.json +++ b/plugins/communication-assets/lang/es.json @@ -51,6 +51,39 @@ "EditMessage": "Editar mensaje", "RemoveMessage": "Eliminar mensaje", "CreateCard": "Crear tarjeta", - "MessageAlreadyHasCardAttached": "¡Vaya! Este mensaje ya tiene una tarjeta adjunta." + "MessageAlreadyHasCardAttached": "¡Vaya! Este mensaje ya tiene una tarjeta adjunta.", + "CreatePoll": "Crear encuesta", + "Poll": "Encuesta", + "QuestionIsRequired": "Se requiere una pregunta.", + "AnswerIsRequired": "Se requiere una respuesta.", + "OptionIsRequired": "Se requiere una opción.", + "StartDateMustBeInTheFuture": "La fecha de inicio debe ser en el futuro.", + "EndDateMustBeInTheFuture": "La fecha de finalización debe ser en el futuro.", + "Question": "Pregunta", + "AskQuestion": "Hacer una pregunta", + "PollOptions": "Opciones de encuesta", + "Option": "Opción", + "AnonymousVoting": "Votación anónima", + "MultipleChoice": "Elección múltiple", + "QuizMode": "Modo de prueba", + "StartTime": "Hora de inicio", + "EndTime": "Hora de finalización", + "OpenPoll": "Abrir encuesta", + "RetractVote": "Retractar voto", + "Quiz": "Prueba", + "VotesCount": "{count, plural, =0 {no hay votos} =1 {1 voto} other {# votos}}", + "Vote": "Votar", + "ShowResults": "Mostrar resultados", + "Ended": "Termina", + "StartsAt": "Empieza a las {date}", + "EndsAt": "Termina a las {date}", + "StartsTomorrow": "Empieza mañana a las {date}", + "EndsTomorrow": "Termina mañana a las {date}", + "PollResults": "Resultados de la encuesta", + "Polls": "Encuestas", + "TotalVotes": "Votos totales", + "Voted": "Votado", + "VotedFor": "Votado por", + "RevokedVote": "Revocar voto" } } diff --git a/plugins/communication-assets/lang/fr.json b/plugins/communication-assets/lang/fr.json index 5bcb0a4dbd..5cce8581e5 100644 --- a/plugins/communication-assets/lang/fr.json +++ b/plugins/communication-assets/lang/fr.json @@ -51,6 +51,39 @@ "EditMessage": "Modifier le message", "RemoveMessage": "Supprimer le message", "CreateCard": "Créer une carte", - "MessageAlreadyHasCardAttached": "Oups! Ce message a déjà une carte attachée." + "MessageAlreadyHasCardAttached": "Oups! Ce message a déjà une carte attachée.", + "CreatePoll": "Créer un sondage", + "Poll": "Sondage", + "QuestionIsRequired": "Une question est requise.", + "AnswerIsRequired": "Une réponse est requise.", + "OptionIsRequired": "Une option est requise.", + "StartDateMustBeInTheFuture": "La date de début doit être dans le futur.", + "EndDateMustBeInTheFuture": "La date de fin doit être dans le futur.", + "Question": "Question", + "AskQuestion": "Poser une question", + "PollOptions": "Options du sondage", + "Option": "Option", + "AnonymousVoting": "Vote anonyme", + "MultipleChoice": "Choix multiple", + "QuizMode": "Mode de test", + "StartTime": "Heure de début", + "EndTime": "Heure de fin", + "OpenPoll": "Ouvrir le sondage", + "RetractVote": "Retraiter le vote", + "Quiz": "Test", + "VotesCount": "{count, plural, =0 {aucun vote} =1 {1 vote} other {# votes}}", + "Vote": "Voter", + "ShowResults": "Afficher les résultats", + "Ended": "Se termine", + "StartsAt": "Commence à {date}", + "EndsAt": "Se termine à {date}", + "StartsTomorrow": "Commence demain à {date}", + "EndsTomorrow": "Se termine demain à {date}", + "PollResults": "Résultats du sondage", + "Polls": "Sondages", + "TotalVotes": "Total des votes", + "Voted": "A voté", + "VotedFor": "A voté pour", + "RevokedVote": "Révoquer le vote" } } diff --git a/plugins/communication-assets/lang/it.json b/plugins/communication-assets/lang/it.json index 6944804880..9c603551ce 100644 --- a/plugins/communication-assets/lang/it.json +++ b/plugins/communication-assets/lang/it.json @@ -51,6 +51,39 @@ "EditMessage": "Modifica messaggio", "RemoveMessage": "Rimuovi messaggio", "CreateCard": "Crea scheda", - "MessageAlreadyHasCardAttached": "Oops! Questo messaggio ha già una scheda allegata." + "MessageAlreadyHasCardAttached": "Oops! Questo messaggio ha già una scheda allegata.", + "CreatePoll": "Crea sondaggio", + "Poll": "Sondaggio", + "QuestionIsRequired": "È richiesta una domanda.", + "AnswerIsRequired": "È richiesta una risposta.", + "OptionIsRequired": "È richiesta un'opzione.", + "StartDateMustBeInTheFuture": "La data di inizio deve essere nel futuro.", + "EndDateMustBeInTheFuture": "La data di fine deve essere nel futuro.", + "Question": "Domanda", + "AskQuestion": "Fai una domanda", + "PollOptions": "Opzioni del sondaggio", + "Option": "Opzione", + "AnonymousVoting": "Votazione anonima", + "MultipleChoice": "Scelta multipla", + "QuizMode": "Modalità quiz", + "StartTime": "Ora di inizio", + "EndTime": "Ora di fine", + "OpenPoll": "Apri sondaggio", + "RetractVote": "Ritira il voto", + "Quiz": "Quiz", + "VotesCount": "{count, plural, =0 {nessun voto} =1 {1 voto} other {# voti}}", + "Vote": "Vota", + "ShowResults": "Mostra i risultati", + "Ended": "Termina", + "StartsAt": "Inizia alle {date}", + "EndsAt": "Termina alle {date}", + "StartsTomorrow": "Inizia domani alle {date}", + "EndsTomorrow": "Termina domani alle {date}", + "PollResults": "Risultati del sondaggio", + "Polls": "Sondaggi", + "TotalVotes": "Voti totali", + "Voted": "Votato", + "VotedFor": "Votato per", + "RevokedVote": "Revoca il voto" } } diff --git a/plugins/communication-assets/lang/ja.json b/plugins/communication-assets/lang/ja.json index 51d8ab8038..7d1ffec6f3 100644 --- a/plugins/communication-assets/lang/ja.json +++ b/plugins/communication-assets/lang/ja.json @@ -51,6 +51,39 @@ "EditMessage": "メッセージを編集", "RemoveMessage": "メッセージを削除", "CreateCard": "カードを作成", - "MessageAlreadyHasCardAttached": "おっと!このメッセージはすでにカードが添付されています。" + "MessageAlreadyHasCardAttached": "おっと!このメッセージはすでにカードが添付されています。", + "CreatePoll": "アンケートを作成", + "Poll": "アンケート", + "QuestionIsRequired": "質問が必要です。", + "AnswerIsRequired": "回答が必要です。", + "OptionIsRequired": "オプションが必要です。", + "StartDateMustBeInTheFuture": "開始日は未来の日付である必要があります。", + "EndDateMustBeInTheFuture": "終了日は未来の日付である必要があります。", + "Question": "質問", + "AskQuestion": "質問を投稿", + "PollOptions": "アンケートオプション", + "Option": "オプション", + "AnonymousVoting": "匿名投票", + "MultipleChoice": "複数選択", + "QuizMode": "クイズモード", + "StartTime": "開始時間", + "EndTime": "終了時間", + "OpenPoll": "アンケートを開く", + "RetractVote": "投票を取り消す", + "Quiz": "クイズ", + "VotesCount": "{count, plural, =0 {投票なし} =1 {1件の投票} other {#件の投票}}", + "Vote": "投票", + "ShowResults": "結果を表示", + "Ended": "終了", + "StartsAt": "{date}に開始", + "EndsAt": "{date}に終了", + "StartsTomorrow": "明日{date}に開始", + "EndsTomorrow": "明日{date}に終了", + "PollResults": "アンケート結果", + "Polls": "アンケート", + "TotalVotes": "合計投票数", + "Voted": "投票済み", + "VotedFor": "投票したユーザ", + "RevokedVote": "投票を取り消し" } } diff --git a/plugins/communication-assets/lang/pt.json b/plugins/communication-assets/lang/pt.json index 5b670635c4..e42fa8c1b7 100644 --- a/plugins/communication-assets/lang/pt.json +++ b/plugins/communication-assets/lang/pt.json @@ -51,6 +51,39 @@ "EditMessage": "Editar mensagem", "RemoveMessage": "Remover mensagem", "CreateCard": "Criar cartão", - "MessageAlreadyHasCardAttached": "Oops! Este mensagem já tem um cartão anexado." + "MessageAlreadyHasCardAttached": "Oops! Este mensagem já tem um cartão anexado.", + "CreatePoll": "Criar enquete", + "Poll": "Enquete", + "QuestionIsRequired": "É necessária uma pergunta.", + "AnswerIsRequired": "É necessária uma resposta.", + "OptionIsRequired": "É necessária uma opção.", + "StartDateMustBeInTheFuture": "A data de início deve estar no futuro.", + "EndDateMustBeInTheFuture": "A data de término deve estar no futuro.", + "Question": "Pergunta", + "AskQuestion": "Fazer uma pergunta", + "PollOptions": "Opções do enquete", + "Option": "Opção", + "AnonymousVoting": "Votação anônima", + "MultipleChoice": "Escolha múltipla", + "QuizMode": "Modo de quiz", + "StartTime": "Hora de início", + "EndTime": "Hora de término", + "OpenPoll": "Abrir enquete", + "RetractVote": "Retirar voto", + "Quiz": "Quiz", + "VotesCount": "{count, plural, =0 {nenhum voto} =1 {1 voto} other {# votos}}", + "Vote": "Votar", + "ShowResults": "Mostrar resultados", + "Ended": "Termina", + "StartsAt": "Começa às {date}", + "EndsAt": "Termina às {date}", + "StartsTomorrow": "Começa amanhã às {date}", + "EndsTomorrow": "Termina amanhã às {date}", + "PollResults": "Resultados do enquete", + "Polls": "Enquetes", + "TotalVotes": "Total de votos", + "Voted": "Votado", + "VotedFor": "Votado para", + "RevokedVote": "Revogar voto" } } diff --git a/plugins/communication-assets/lang/ru.json b/plugins/communication-assets/lang/ru.json index 17712a3fee..92031c9958 100644 --- a/plugins/communication-assets/lang/ru.json +++ b/plugins/communication-assets/lang/ru.json @@ -51,6 +51,39 @@ "EditMessage": "Редактировать сообщение", "RemoveMessage": "Удалить сообщение", "CreateCard": "Создать карточку", - "MessageAlreadyHasCardAttached": "Упс! Это сообщение уже имеет прикрепленную карточку." + "MessageAlreadyHasCardAttached": "Упс! Это сообщение уже имеет прикрепленную карточку.", + "CreatePoll": "Создать опрос", + "Poll": "Опрос", + "QuestionIsRequired": "Заполните вопрос.", + "AnswerIsRequired": "Выбертте правильный ответ.", + "OptionIsRequired": "Добавьте хотя бы один вариант.", + "StartDateMustBeInTheFuture": "Дата начала должна быть в будущем.", + "EndDateMustBeInTheFuture": "Дата окончания должна быть в будущем.", + "Question": "Вопрос", + "AskQuestion": "Вопрос", + "PollOptions": "Варианты опроса", + "Option": "Вариант", + "AnonymousVoting": "Анонимное голосование", + "MultipleChoice": "Множественный выбор", + "QuizMode": "Режим викторины", + "StartTime": "Время начала", + "EndTime": "Время окончания", + "OpenPoll": "Открыть опрос", + "RetractVote": "Отменить голос", + "Quiz": "Викторина", + "VotesCount": "{count, plural, =0 {нет голосов} =1 {1 голос} other {# голосов}}", + "Vote": "Голосовать", + "ShowResults": "Показать результаты", + "Ended": "Закончилось", + "StartsAt": "Начинается {date}", + "EndsAt": "Заканчивается {date}", + "StartsTomorrow": "Начинается завтра {date}", + "EndsTomorrow": "Заканчивается завтра {date}", + "PollResults": "Результаты опроса", + "Polls": "Опросы", + "TotalVotes": "Всего голосов", + "Voted": "Проголосовавшие", + "VotedFor": "Голосовал(а) за", + "RevokedVote": "Отменил(а) голос" } } diff --git a/plugins/communication-assets/lang/zh.json b/plugins/communication-assets/lang/zh.json index 7c808c3303..e107ffeb18 100644 --- a/plugins/communication-assets/lang/zh.json +++ b/plugins/communication-assets/lang/zh.json @@ -51,6 +51,39 @@ "EditMessage": "编辑消息", "RemoveMessage": "删除消息", "CreateCard": "创建卡片", - "MessageAlreadyHasCardAttached": "哎呀!此消息已经有一张卡片附加。" + "MessageAlreadyHasCardAttached": "哎呀!此消息已经有一张卡片附加。", + "CreatePoll": "创建投票", + "Poll": "投票", + "QuestionIsRequired": "问题是必需的。", + "AnswerIsRequired": "回答是必需的。", + "OptionIsRequired": "选项是必需的。", + "StartDateMustBeInTheFuture": "开始日期必须是未来的。", + "EndDateMustBeInTheFuture": "结束日期必须是未来的。", + "Question": "问题", + "AskQuestion": "提问", + "PollOptions": "投票选项", + "Option": "选项", + "AnonymousVoting": "匿名投票", + "MultipleChoice": "多选", + "QuizMode": "测验模式", + "StartTime": "开始时间", + "EndTime": "结束时间", + "OpenPoll": "开放投票", + "RetractVote": "撤销投票", + "Quiz": "测验", + "VotesCount": "{count, plural, =0 {无投票} =1 {1 次投票} other {# 次投票}}", + "Vote": "投票", + "ShowResults": "显示结果", + "Ended": "结束", + "StartsAt": "开始于 {date}", + "EndsAt": "结束于 {date}", + "StartsTomorrow": "开始明天 {date}", + "EndsTomorrow": "结束明天 {date}", + "PollResults": "投票结果", + "Polls": "投票", + "TotalVotes": "总投票数", + "Voted": "已投票", + "VotedFor": "投票于", + "RevokedVote": "撤销投票" } } diff --git a/plugins/communication-assets/src/index.ts b/plugins/communication-assets/src/index.ts index 5006385781..5d9c1e8a22 100644 --- a/plugins/communication-assets/src/index.ts +++ b/plugins/communication-assets/src/index.ts @@ -21,6 +21,7 @@ loadMetadata(communication.icon, { Bell: `${icons}#bell`, BellCrossed: `${icons}#bell-crossed`, File: `${icons}#file`, - MessageMultiple: `${icons}#message-multiple` + MessageMultiple: `${icons}#message-multiple`, + Poll: `${icons}#poll` }) addStringsLoader(communicationId, async (lang: string) => await import(`../lang/${lang}.json`)) diff --git a/plugins/communication-resources/package.json b/plugins/communication-resources/package.json index e899e63938..08bbcdccb7 100644 --- a/plugins/communication-resources/package.json +++ b/plugins/communication-resources/package.json @@ -49,6 +49,7 @@ "@hcengineering/chat": "^0.6.0", "@hcengineering/communication": "^0.6.0", "@hcengineering/communication-types": "^0.1.0", + "@hcengineering/communication-shared": "^0.1.0", "@hcengineering/contact": "^0.6.24", "@hcengineering/contact-resources": "^0.6.0", "@hcengineering/core": "^0.6.32", diff --git a/plugins/communication-resources/src/components/CreateCardFromMessagePopup.svelte b/plugins/communication-resources/src/components/CreateCardFromMessagePopup.svelte index 3965c8b1b7..8517f9aff6 100644 --- a/plugins/communication-resources/src/components/CreateCardFromMessagePopup.svelte +++ b/plugins/communication-resources/src/components/CreateCardFromMessagePopup.svelte @@ -127,12 +127,7 @@ />
- +
{#if _message.thread != null && !inProgress} diff --git a/plugins/communication-resources/src/components/MessagesList.svelte b/plugins/communication-resources/src/components/MessagesList.svelte index a675436483..4dc1594c6e 100644 --- a/plugins/communication-resources/src/components/MessagesList.svelte +++ b/plugins/communication-resources/src/components/MessagesList.svelte @@ -195,9 +195,8 @@ return { card: card._id, replies: true, - files: true, + attachments: true, reactions: true, - links: true, order: SortingOrder.Ascending, limit } @@ -209,9 +208,8 @@ return { card: card._id, replies: true, - files: true, + attachments: true, reactions: true, - links: true, order, limit, from: unread && !shouldScrollToEnd && initialLastView != null ? initialLastView : undefined diff --git a/plugins/communication-resources/src/components/message/MessageFooter.svelte b/plugins/communication-resources/src/components/message/MessageFooter.svelte index 3b51a685ca..d128e78424 100644 --- a/plugins/communication-resources/src/components/message/MessageFooter.svelte +++ b/plugins/communication-resources/src/components/message/MessageFooter.svelte @@ -18,17 +18,21 @@ import cardPlugin from '@hcengineering/card' import { getCurrentAccount } from '@hcengineering/core' import { AttachmentPreview, LinkPreview } from '@hcengineering/attachment-resources' - import { LinkPreviewID, Message, MessageType } from '@hcengineering/communication-types' + import { AttachmentID, Message, MessageType } from '@hcengineering/communication-types' import { getResource } from '@hcengineering/platform' + import { isAppletAttachment, isBlobAttachment, isLinkPreviewAttachment } from '@hcengineering/communication-shared' + import { Component } from '@hcengineering/ui' import ReactionsList from '../ReactionsList.svelte' import MessageThread from '../thread/Thread.svelte' import { toggleReaction } from '../../utils' + import communication from '../../plugin' export let message: Message const me = getCurrentAccount() const communicationClient = getCommunicationClient() + const client = getClient() function canReply (): boolean { return message.type !== MessageType.Activity && message.extra?.threadRoot !== true @@ -53,49 +57,69 @@ await r(_id, c) } - async function removeLinkPreview (id: LinkPreviewID): Promise { - await communicationClient.linkPreviewPatch(message.cardId, message.id, { - detach: [id] + async function removeLinkPreview (id: AttachmentID): Promise { + await communicationClient.attachmentPatch(message.cardId, message.id, { + remove: [id] }) } + + const appletsModels = client.getModel().findAllSync(communication.class.Applet, {}) + $: blobs = message.attachments.filter(isBlobAttachment) ?? [] + $: links = message.attachments.filter(isLinkPreviewAttachment) ?? [] + $: applets = message.attachments.filter(isAppletAttachment) ?? [] - - -{#if message.blobs.length > 0 && !message.removed} +{#if applets.length > 0 && !message.removed} +
+ {#each applets as applet (applet.id)} + {@const appletModel = appletsModels.find((it) => it.type === applet.type)} + {#if appletModel} + + {/if} + {/each} +
+{/if} + +{#if blobs.length > 0 && !message.removed}
- {#each message.blobs as blob (blob.blobId)} + {#each blobs as blob (blob.id)} {/each}
{/if} -{#if (message.linkPreviews ?? []).length > 0 && !message.removed} +{#if links.length > 0 && !message.removed}