From 14d52421529836c175c5f0ed489d52a24265b12b Mon Sep 17 00:00:00 2001 From: Andrey Sobolev Date: Wed, 29 Oct 2025 16:27:46 +0700 Subject: [PATCH 1/2] Fxi rating control Signed-off-by: Andrey Sobolev --- .../src/components/reactions/Reactions.svelte | 2 +- .../src/components/ReactionPresenter.svelte | 2 +- plugins/rating-assets/assets/icon.svg | 71 +++++++---- .../src/components/RatingEditor.svelte | 120 ++++++++++++------ .../src/components/ReactionPresenter.svelte | 2 +- server-plugins/rating/src/index.ts | 32 ++--- 6 files changed, 144 insertions(+), 85 deletions(-) diff --git a/plugins/activity-resources/src/components/reactions/Reactions.svelte b/plugins/activity-resources/src/components/reactions/Reactions.svelte index 848049af284..186c7dca1ab 100644 --- a/plugins/activity-resources/src/components/reactions/Reactions.svelte +++ b/plugins/activity-resources/src/components/reactions/Reactions.svelte @@ -136,7 +136,7 @@ } &.highlight { background: var(--global-ui-highlight-BackgroundColor); - border-color: var(--global-accent-BackgroundColor); + border-color: var(--global-accent-SkyText); } &:hover { diff --git a/plugins/communication-resources/src/components/ReactionPresenter.svelte b/plugins/communication-resources/src/components/ReactionPresenter.svelte index 22b3c99d79b..926db6ab5f5 100644 --- a/plugins/communication-resources/src/components/ReactionPresenter.svelte +++ b/plugins/communication-resources/src/components/ReactionPresenter.svelte @@ -65,7 +65,7 @@ } &.selected { - background: var(--global-accent-BackgroundColor); + background: var(--global-accent-SkyText); } } diff --git a/plugins/rating-assets/assets/icon.svg b/plugins/rating-assets/assets/icon.svg index 36a11cefb65..98b5e28760d 100644 --- a/plugins/rating-assets/assets/icon.svg +++ b/plugins/rating-assets/assets/icon.svg @@ -1,35 +1,62 @@ - - - - - + + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - + \ No newline at end of file diff --git a/plugins/rating-resources/src/components/RatingEditor.svelte b/plugins/rating-resources/src/components/RatingEditor.svelte index 2fba5aa770f..4dd15d75862 100644 --- a/plugins/rating-resources/src/components/RatingEditor.svelte +++ b/plugins/rating-resources/src/components/RatingEditor.svelte @@ -2,12 +2,12 @@ import type { Class, Doc, Ref } from '@hcengineering/core' import core, { getCurrentAccount, groupByArray } from '@hcengineering/core' import emojiPlugin from '@hcengineering/emoji' + import { translateCB, getEmbeddedLabel } from '@hcengineering/platform' import { createQuery, getClient } from '@hcengineering/presentation' import type { DocReaction } from '@hcengineering/rating' import ratingPlugin, { ReactionKind } from '@hcengineering/rating' - import { Icon, showPopup, tooltip } from '@hcengineering/ui' + import { Button, showPopup } from '@hcengineering/ui' import ReactionPresenter from './ReactionPresenter.svelte' - import { translateCB } from '@hcengineering/platform' export let _id: Ref export let _class: Ref> @@ -30,11 +30,15 @@ const socialIds = new Set(account.fullSocialIds.map((it) => it._id)) const client = getClient() + let loading = true + let operating = false const reactionsQuery = createQuery() $: if (reactions.length === 0) { + loading = true reactionsQuery.query(ratingPlugin.class.DocReaction, { attachedTo: _id, attachedToClass: _class }, (result) => { _reactions = result + loading = false }) } else { reactionsQuery.unsubscribe() @@ -42,20 +46,32 @@ } async function addStarReaction (): Promise { - const existing = _reactions.filter((it) => it.reactionType === ReactionKind.Star && socialIds.has(it.modifiedBy)) - if (existing.length > 0) { - for (const e of existing) { - if (e.value === 0) { - await client.update(e, { value: 1 }) - } else { - await client.update(e, { value: 0 }) + operating = true + try { + const r = !loading + ? _reactions + : await client.findAll(ratingPlugin.class.DocReaction, { + attachedTo: _id, + attachedToClass: _class, + reactionType: ReactionKind.Star + }) + const existing = r.filter((it) => it.reactionType === ReactionKind.Star && socialIds.has(it.modifiedBy)) + if (existing.length > 0) { + for (const e of existing) { + if (e.value === 0) { + await client.update(e, { value: 1 }) + } else { + await client.update(e, { value: 0 }) + } } + } else { + await client.addCollection(ratingPlugin.class.DocReaction, core.space.Workspace, _id, _class, '_reactions', { + reactionType: ReactionKind.Star, + value: 1 + }) } - } else { - await client.addCollection(ratingPlugin.class.DocReaction, core.space.Workspace, _id, _class, '_reactions', { - reactionType: ReactionKind.Star, - value: 1 - }) + } finally { + operating = false } } @@ -63,30 +79,55 @@ showPopup(emojiPlugin.component.EmojiPopup, {}, event.target as HTMLElement, async (emoji) => { if (emoji?.text === undefined) return - const existing = _reactions.filter( - (it) => it.reactionType === ReactionKind.Emoji && it.emoji === emoji.text && socialIds.has(it.modifiedBy) - ) - if (existing.length > 0) { - for (const e of existing) { - await client.remove(e) + operating = true + try { + const r = !loading + ? _reactions + : await client.findAll(ratingPlugin.class.DocReaction, { + attachedTo: _id, + attachedToClass: _class, + reactionType: ReactionKind.Emoji, + text: emoji.text + }) + + const existing = r.filter( + (it) => it.reactionType === ReactionKind.Emoji && it.emoji === emoji.text && socialIds.has(it.modifiedBy) + ) + if (existing.length > 0) { + for (const e of existing) { + await client.remove(e) + } + } else { + await client.addCollection(ratingPlugin.class.DocReaction, core.space.Workspace, _id, _class, '_reactions', { + reactionType: ReactionKind.Emoji, + value: 0, + emoji: emoji.text, + image: emoji.image + }) } - } else { - await client.addCollection(ratingPlugin.class.DocReaction, core.space.Workspace, _id, _class, '_reactions', { - reactionType: ReactionKind.Emoji, - value: 0, - emoji: emoji.text, - image: emoji.image - }) + } finally { + operating = false } }) } - async function removeReaction (reactions: DocReaction[]): Promise { + async function existingReactionClick (reactions: DocReaction[]): Promise { + let removed = false for (const reaction of reactions) { if (account.fullSocialIds.some((it) => it._id === reaction.modifiedBy)) { await client.removeDoc(reaction._class, reaction.space, reaction._id) + removed = true } } + if (!removed && reactions.length > 0) { + // I just want to add my reaction + await client.addCollection(ratingPlugin.class.DocReaction, core.space.Workspace, _id, _class, '_reactions', { + reactionType: ReactionKind.Emoji, + value: 0, + emoji: reactions[0].emoji, + image: reactions[0].image + }) + } } $: hasSelectedStar = _reactions.some( @@ -101,20 +142,15 @@
- - + kind={'ghost'} + size={'small'} + disabled={operating} + showTooltip={{ label: ratingPlugin.string.AddStar }} + label={showMy && starCount > 0 ? getEmbeddedLabel(starCount.toString()) : undefined} + /> {#each otherReactions as oreact (oreact[0])} {@const emoji = oreact[1][0].emoji} @@ -125,7 +161,7 @@ selected={showMy && data.some((it) => socialIds.has(it.modifiedBy))} socialIds={data.map((it) => it.modifiedBy)} count={showMy ? data.length : 0} - on:click={() => removeReaction(oreact[1])} + on:click={() => existingReactionClick(oreact[1])} /> {/each} diff --git a/plugins/rating-resources/src/components/ReactionPresenter.svelte b/plugins/rating-resources/src/components/ReactionPresenter.svelte index 6d44dde17f2..ddaa9dea138 100644 --- a/plugins/rating-resources/src/components/ReactionPresenter.svelte +++ b/plugins/rating-resources/src/components/ReactionPresenter.svelte @@ -65,7 +65,7 @@ } &.selected { - background: var(--global-accent-BackgroundColor); + background: var(--global-accent-SkyText); } } diff --git a/server-plugins/rating/src/index.ts b/server-plugins/rating/src/index.ts index b5a07b94845..33f9dd0c3f5 100644 --- a/server-plugins/rating/src/index.ts +++ b/server-plugins/rating/src/index.ts @@ -76,24 +76,20 @@ export class RatingMiddleware extends BaseMiddleware { } private async validateNewReaction (ctx: MeasureContext, create: TxCreateDoc): Promise { - if (create.attributes.reactionType !== ReactionKind.Star) { - // Should allow only one reaction per document per user for Emoji, Like, Usefull - const current = await this.provideFindAll(ctx, rating.class.DocReaction, { - attachedTo: create.attachedTo, - attachedToClass: create.objectClass - }) - if ( - current.some( - (it) => - it.reactionType === create.attributes.reactionType && - (it.value === create.attributes.value || - (create.attributes.reactionType === ReactionKind.Emoji && it.emoji === create.attributes.emoji)) - ) - ) { - throw new Error('Duplicate emoji reaction is not allowed.') - } - } else { - // For stars, we probable need to merge them into one value. + // Should allow only one reaction per document per user for Emoji, Like, Usefull + const current = await this.provideFindAll(ctx, rating.class.DocReaction, { + attachedTo: create.attachedTo, + attachedToClass: create.objectClass + }) + if ( + current.some( + (it) => + it.reactionType === create.attributes.reactionType && + (it.value === create.attributes.value || + (create.attributes.reactionType === ReactionKind.Emoji && it.emoji === create.attributes.emoji)) + ) + ) { + throw new Error('Duplicate emoji reaction is not allowed.') } } From abfe912053896aeb13bd313cc3cf1f1020f85387 Mon Sep 17 00:00:00 2001 From: Andrey Sobolev Date: Wed, 29 Oct 2025 16:40:22 +0700 Subject: [PATCH 2/2] Move card stars to right Signed-off-by: Andrey Sobolev --- plugins/card-resources/src/components/EditCardNew.svelte | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/card-resources/src/components/EditCardNew.svelte b/plugins/card-resources/src/components/EditCardNew.svelte index 5897af03aa1..55f398f9123 100644 --- a/plugins/card-resources/src/components/EditCardNew.svelte +++ b/plugins/card-resources/src/components/EditCardNew.svelte @@ -193,10 +193,6 @@ {doc.title} {/if}
- @@ -211,6 +207,10 @@ +