From 9c1db9dc5bef1cbc15fd04d69406e1d5ebe2415a Mon Sep 17 00:00:00 2001
From: Denis Bykhov <bykhov.denis@gmail.com>
Date: Wed, 12 Apr 2023 14:21:49 +0600
Subject: [PATCH] Activity icons (#2955)

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
---
 models/tracker/src/index.ts                   |  9 ++-
 models/tracker/src/plugin.ts                  |  4 +-
 .../src/components/TxView.svelte              |  7 +-
 plugins/activity-resources/src/utils.ts       | 14 ++--
 .../components/activity/PriorityIcon.svelte   | 31 +++++++++
 .../src/components/activity/StatusIcon.svelte | 31 +++++++++
 .../issues/PriorityRefPresenter.svelte        | 65 ++++---------------
 .../components/issues/StatusPresenter.svelte  |  2 +-
 plugins/tracker-resources/src/index.ts        |  6 +-
 9 files changed, 107 insertions(+), 62 deletions(-)
 create mode 100644 plugins/tracker-resources/src/components/activity/PriorityIcon.svelte
 create mode 100644 plugins/tracker-resources/src/components/activity/StatusIcon.svelte

diff --git a/models/tracker/src/index.ts b/models/tracker/src/index.ts
index da9b04001b..83e3768585 100644
--- a/models/tracker/src/index.ts
+++ b/models/tracker/src/index.ts
@@ -181,11 +181,16 @@ export class TIssue extends TAttachedDoc implements Issue {
   @Index(IndexKind.FullText)
     description!: Markup
 
-  @Prop(TypeRef(tracker.class.IssueStatus), tracker.string.Status, { _id: tracker.attribute.IssueStatus })
+  @Prop(TypeRef(tracker.class.IssueStatus), tracker.string.Status, {
+    _id: tracker.attribute.IssueStatus,
+    iconComponent: tracker.activity.StatusIcon
+  })
   @Index(IndexKind.Indexed)
     status!: Ref<IssueStatus>
 
-  @Prop(TypeIssuePriority(), tracker.string.Priority)
+  @Prop(TypeIssuePriority(), tracker.string.Priority, {
+    iconComponent: tracker.activity.PriorityIcon
+  })
   @Index(IndexKind.Indexed)
     priority!: IssuePriority
 
diff --git a/models/tracker/src/plugin.ts b/models/tracker/src/plugin.ts
index b2a497a52a..c10b96b072 100644
--- a/models/tracker/src/plugin.ts
+++ b/models/tracker/src/plugin.ts
@@ -39,7 +39,9 @@ export default mergeIds(trackerId, tracker, {
     CreatedOn: '' as IntlString
   },
   activity: {
-    TxIssueCreated: '' as AnyComponent
+    TxIssueCreated: '' as AnyComponent,
+    StatusIcon: '' as AnyComponent,
+    PriorityIcon: '' as AnyComponent
   },
   component: {
     SprintSelector: '' as AnyComponent,
diff --git a/plugins/activity-resources/src/components/TxView.svelte b/plugins/activity-resources/src/components/TxView.svelte
index 49441d9c3a..338a4ddd43 100644
--- a/plugins/activity-resources/src/components/TxView.svelte
+++ b/plugins/activity-resources/src/components/TxView.svelte
@@ -21,6 +21,7 @@
   import { createQuery, getClient } from '@hcengineering/presentation'
   import {
     ActionIcon,
+    AnyComponent,
     Component,
     Icon,
     IconEdit,
@@ -56,6 +57,7 @@
   let employee: Employee | undefined
   let model: AttributeModel[] = []
   let modelIcon: Asset | undefined = undefined
+  let iconComponent: AnyComponent | undefined = undefined
 
   let edit = false
 
@@ -83,6 +85,7 @@
       viewlet = result.viewlet
       model = result.model
       modelIcon = result.modelIcon
+      iconComponent = result.iconComponent
       props = getProps(result.props, edit)
     }
   })
@@ -172,7 +175,9 @@
         </div>
       {:else}
         <div class="msgactivity-icon">
-          {#if viewlet}
+          {#if iconComponent}
+            <Component is={iconComponent} {props} />
+          {:else if viewlet}
             <Icon icon={viewlet.icon} size="small" />
           {:else if viewlet === undefined && model.length > 0}
             <Icon icon={modelIcon !== undefined ? modelIcon : Edit} size="small" />
diff --git a/plugins/activity-resources/src/utils.ts b/plugins/activity-resources/src/utils.ts
index bf0552a760..de44544add 100644
--- a/plugins/activity-resources/src/utils.ts
+++ b/plugins/activity-resources/src/utils.ts
@@ -99,17 +99,17 @@ export async function updateViewlet (
     model: AttributeModel[]
     props: any
     modelIcon: Asset | undefined
+    iconComponent: AnyComponent | undefined
   }> {
   let viewlet = getViewlet(viewlets, dtx)
 
-  let props = getDTxProps(dtx)
+  const props = getDTxProps(dtx)
   let model: AttributeModel[] = []
   let modelIcon: Asset | undefined
+  let iconComponent: AnyComponent | undefined
 
   if (viewlet === undefined) {
     ;({ viewlet, model } = await checkInlineViewlets(dtx, viewlet, client, model))
-    // Only value is necessary for inline viewlets
-    props = { value: dtx.doc }
     if (model !== undefined) {
       // Check for State attribute
       for (const a of model) {
@@ -118,9 +118,15 @@ export async function updateViewlet (
           break
         }
       }
+      for (const a of model) {
+        if (a.attribute?.iconComponent !== undefined) {
+          iconComponent = a.attribute?.iconComponent
+          break
+        }
+      }
     }
   }
-  return { viewlet, id: dtx.tx._id, model, props, modelIcon }
+  return { viewlet, id: dtx.tx._id, model, props, modelIcon, iconComponent }
 }
 
 async function checkInlineViewlets (
diff --git a/plugins/tracker-resources/src/components/activity/PriorityIcon.svelte b/plugins/tracker-resources/src/components/activity/PriorityIcon.svelte
new file mode 100644
index 0000000000..97fad4f956
--- /dev/null
+++ b/plugins/tracker-resources/src/components/activity/PriorityIcon.svelte
@@ -0,0 +1,31 @@
+<!--
+// Copyright © 2023 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 { TxUpdateDoc } from '@hcengineering/core'
+  import { Asset } from '@hcengineering/platform'
+  import { Issue } from '@hcengineering/tracker'
+  import { Icon } from '@hcengineering/ui'
+  import { issuePriorities } from '../../utils'
+
+  export let tx: TxUpdateDoc<Issue>
+  $: value = tx.operations.priority
+
+  let icon: Asset
+  $: if (value !== undefined) ({ icon } = issuePriorities[value])
+</script>
+
+<div class="icon">
+  <Icon {icon} size={'small'} />
+</div>
diff --git a/plugins/tracker-resources/src/components/activity/StatusIcon.svelte b/plugins/tracker-resources/src/components/activity/StatusIcon.svelte
new file mode 100644
index 0000000000..3a227d61e8
--- /dev/null
+++ b/plugins/tracker-resources/src/components/activity/StatusIcon.svelte
@@ -0,0 +1,31 @@
+<!--
+// Copyright © 2023 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 { TxUpdateDoc } from '@hcengineering/core'
+  import { statusStore } from '@hcengineering/presentation'
+  import { Issue } from '@hcengineering/tracker'
+  import IssueStatusIcon from '../issues/IssueStatusIcon.svelte'
+
+  export let tx: TxUpdateDoc<Issue>
+  $: value = tx.operations.status
+
+  $: status = value && $statusStore.byId.get(value)
+</script>
+
+<div class="icon">
+  {#if status}
+    <IssueStatusIcon value={status} size="small" />
+  {/if}
+</div>
diff --git a/plugins/tracker-resources/src/components/issues/PriorityRefPresenter.svelte b/plugins/tracker-resources/src/components/issues/PriorityRefPresenter.svelte
index 72ed7e7e8d..d3cbc2b07e 100644
--- a/plugins/tracker-resources/src/components/issues/PriorityRefPresenter.svelte
+++ b/plugins/tracker-resources/src/components/issues/PriorityRefPresenter.svelte
@@ -14,61 +14,22 @@
 -->
 <script lang="ts">
   import { IssuePriority } from '@hcengineering/tracker'
-  import { Button, ButtonKind, ButtonSize, Icon, Label } from '@hcengineering/ui'
+  import { Icon, Label } from '@hcengineering/ui'
   import { issuePriorities } from '../../utils'
 
   export let value: IssuePriority
+  export let size: 'small' | 'medium' = 'small'
+  export let inline: boolean = false
 
-  export let kind: ButtonKind = 'link'
-  export let size: ButtonSize = 'large'
-  export let justify: 'left' | 'center' = 'left'
-  export let width: string | undefined = undefined
+  $: icon = issuePriorities[value]?.icon
+  $: label = issuePriorities[value]?.label
 </script>
 
-{#if kind === 'list' || kind === 'list-header'}
-  <div class="priority-container">
-    <div class="icon">
-      {#if issuePriorities[value]?.icon}<Icon icon={issuePriorities[value]?.icon} {size} />{/if}
-    </div>
-    <span
-      class="{kind === 'list' ? 'ml-2 text-md' : 'ml-3 text-base'} overflow-label disabled fs-bold content-accent-color"
-    >
-      <Label label={issuePriorities[value]?.label} />
-    </span>
-  </div>
-{:else}
-  <Button
-    label={issuePriorities[value]?.label}
-    icon={issuePriorities[value]?.icon}
-    {justify}
-    {width}
-    {size}
-    {kind}
-    disabled
-  />
-{/if}
-
-<style lang="scss">
-  .priority-container {
-    display: flex;
-    align-items: center;
-    flex-shrink: 0;
-    min-width: 0;
-    cursor: pointer;
-
-    .icon {
-      display: flex;
-      justify-content: center;
-      align-items: center;
-      flex-shrink: 0;
-      width: 1rem;
-      height: 1rem;
-      color: var(--content-color);
-    }
-    &:hover {
-      .icon {
-        color: var(--caption-color) !important;
-      }
-    }
-  }
-</style>
+<div class="flex-presenter cursor-default">
+  {#if !inline && icon}
+    <Icon {icon} {size} />
+  {/if}
+  <span class="overflow-label" class:ml-2={!inline && icon}>
+    <Label {label} />
+  </span>
+</div>
diff --git a/plugins/tracker-resources/src/components/issues/StatusPresenter.svelte b/plugins/tracker-resources/src/components/issues/StatusPresenter.svelte
index 4c96461e35..40927657f8 100644
--- a/plugins/tracker-resources/src/components/issues/StatusPresenter.svelte
+++ b/plugins/tracker-resources/src/components/issues/StatusPresenter.svelte
@@ -22,7 +22,7 @@
 </script>
 
 {#if value}
-  <div class="flex-presenter">
+  <div class="flex-presenter cursor-default">
     {#if !inline}
       <IssueStatusIcon {value} {size} />
     {/if}
diff --git a/plugins/tracker-resources/src/index.ts b/plugins/tracker-resources/src/index.ts
index 413b980ed4..a8523cc887 100644
--- a/plugins/tracker-resources/src/index.ts
+++ b/plugins/tracker-resources/src/index.ts
@@ -134,6 +134,8 @@ import MoveIssues from './components/issues/Move.svelte'
 import IssueStatistics from './components/sprints/IssueStatistics.svelte'
 import SprintRefPresenter from './components/sprints/SprintRefPresenter.svelte'
 import TxIssueCreated from './components/activity/TxIssueCreated.svelte'
+import PriorityIcon from './components/activity/PriorityIcon.svelte'
+import StatusIcon from './components/activity/StatusIcon.svelte'
 
 export { default as SubIssueList } from './components/issues/edit/SubIssueList.svelte'
 
@@ -352,7 +354,9 @@ export async function handleRecordingScrum (
 
 export default async (): Promise<Resources> => ({
   activity: {
-    TxIssueCreated
+    TxIssueCreated,
+    PriorityIcon,
+    StatusIcon
   },
   component: {
     NopeComponent,