mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-23 11:31:57 +03:00
[TSK-932] Show only diffs for description changes in activity (#3062)
Signed-off-by: Ruslan Bayandinov <wazsone@ya.ru>
This commit is contained in:
parent
ad8e748d91
commit
6d99032f2d
@ -29,6 +29,7 @@ import type {
|
|||||||
AttributeEditor,
|
AttributeEditor,
|
||||||
AttributeFilter,
|
AttributeFilter,
|
||||||
AttributePresenter,
|
AttributePresenter,
|
||||||
|
ActivityAttributePresenter,
|
||||||
BuildModelKey,
|
BuildModelKey,
|
||||||
ClassFilters,
|
ClassFilters,
|
||||||
ClassSortFuncs,
|
ClassSortFuncs,
|
||||||
@ -87,7 +88,8 @@ export function classPresenter (
|
|||||||
_class: Ref<Class<Doc>>,
|
_class: Ref<Class<Doc>>,
|
||||||
presenter: AnyComponent,
|
presenter: AnyComponent,
|
||||||
editor?: AnyComponent,
|
editor?: AnyComponent,
|
||||||
popup?: AnyComponent
|
popup?: AnyComponent,
|
||||||
|
activity?: AnyComponent
|
||||||
): void {
|
): void {
|
||||||
builder.mixin(_class, core.class.Class, view.mixin.AttributePresenter, {
|
builder.mixin(_class, core.class.Class, view.mixin.AttributePresenter, {
|
||||||
presenter
|
presenter
|
||||||
@ -98,6 +100,11 @@ export function classPresenter (
|
|||||||
popup
|
popup
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if (activity !== undefined) {
|
||||||
|
builder.mixin(_class, core.class.Class, view.mixin.ActivityAttributePresenter, {
|
||||||
|
presenter: activity
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Model(view.class.FilteredView, core.class.Doc, DOMAIN_PREFERENCE)
|
@Model(view.class.FilteredView, core.class.Doc, DOMAIN_PREFERENCE)
|
||||||
@ -158,6 +165,11 @@ export class TAttributePresenter extends TClass implements AttributePresenter {
|
|||||||
presenter!: AnyComponent
|
presenter!: AnyComponent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Mixin(view.mixin.ActivityAttributePresenter, core.class.Class)
|
||||||
|
export class TActivityAttributePresenter extends TClass implements ActivityAttributePresenter {
|
||||||
|
presenter!: AnyComponent
|
||||||
|
}
|
||||||
|
|
||||||
@Mixin(view.mixin.ObjectPresenter, core.class.Class)
|
@Mixin(view.mixin.ObjectPresenter, core.class.Class)
|
||||||
export class TObjectPresenter extends TClass implements ObjectPresenter {
|
export class TObjectPresenter extends TClass implements ObjectPresenter {
|
||||||
presenter!: AnyComponent
|
presenter!: AnyComponent
|
||||||
@ -337,6 +349,7 @@ export function createModel (builder: Builder): void {
|
|||||||
TAttributeFilter,
|
TAttributeFilter,
|
||||||
TAttributeEditor,
|
TAttributeEditor,
|
||||||
TAttributePresenter,
|
TAttributePresenter,
|
||||||
|
TActivityAttributePresenter,
|
||||||
TListItemPresenter,
|
TListItemPresenter,
|
||||||
TCollectionEditor,
|
TCollectionEditor,
|
||||||
TCollectionPresenter,
|
TCollectionPresenter,
|
||||||
@ -387,7 +400,8 @@ export function createModel (builder: Builder): void {
|
|||||||
core.class.TypeMarkup,
|
core.class.TypeMarkup,
|
||||||
view.component.MarkupPresenter,
|
view.component.MarkupPresenter,
|
||||||
view.component.MarkupEditor,
|
view.component.MarkupEditor,
|
||||||
view.component.MarkupEditorPopup
|
view.component.MarkupEditorPopup,
|
||||||
|
view.component.MarkupDiffPresenter
|
||||||
)
|
)
|
||||||
|
|
||||||
builder.mixin(core.class.TypeMarkup, core.class.Class, view.mixin.InlineAttributEditor, {
|
builder.mixin(core.class.TypeMarkup, core.class.Class, view.mixin.InlineAttributEditor, {
|
||||||
|
@ -47,6 +47,7 @@ export default mergeIds(viewId, view, {
|
|||||||
IntlStringPresenter: '' as AnyComponent,
|
IntlStringPresenter: '' as AnyComponent,
|
||||||
NumberEditor: '' as AnyComponent,
|
NumberEditor: '' as AnyComponent,
|
||||||
NumberPresenter: '' as AnyComponent,
|
NumberPresenter: '' as AnyComponent,
|
||||||
|
MarkupDiffPresenter: '' as AnyComponent,
|
||||||
MarkupPresenter: '' as AnyComponent,
|
MarkupPresenter: '' as AnyComponent,
|
||||||
BooleanPresenter: '' as AnyComponent,
|
BooleanPresenter: '' as AnyComponent,
|
||||||
BooleanEditor: '' as AnyComponent,
|
BooleanEditor: '' as AnyComponent,
|
||||||
|
@ -34,6 +34,8 @@
|
|||||||
export let content: Markup
|
export let content: Markup
|
||||||
export let buttonSize: IconSize = 'small'
|
export let buttonSize: IconSize = 'small'
|
||||||
export let comparedVersion: Markup | undefined = undefined
|
export let comparedVersion: Markup | undefined = undefined
|
||||||
|
export let noButton: boolean = false
|
||||||
|
export let readonly = false
|
||||||
|
|
||||||
let element: HTMLElement
|
let element: HTMLElement
|
||||||
let editor: Editor
|
let editor: Editor
|
||||||
@ -41,8 +43,8 @@
|
|||||||
let _decoration = DecorationSet.empty
|
let _decoration = DecorationSet.empty
|
||||||
let oldContent = ''
|
let oldContent = ''
|
||||||
|
|
||||||
function updateEditor (editor?: Editor, comparedVersion?: Markup): void {
|
function updateEditor (editor?: Editor, comparedVersion?: Markup | ArrayBuffer): void {
|
||||||
const r = calculateDecorations(editor, oldContent, comparedVersion)
|
const r = calculateDecorations(editor, oldContent, undefined, comparedVersion)
|
||||||
if (r !== undefined) {
|
if (r !== undefined) {
|
||||||
oldContent = r.oldContent
|
oldContent = r.oldContent
|
||||||
_decoration = r.decorations
|
_decoration = r.decorations
|
||||||
@ -87,6 +89,7 @@
|
|||||||
editor = editor
|
editor = editor
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
editor.setEditable(!readonly)
|
||||||
})
|
})
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
@ -98,7 +101,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="ref-container">
|
<div class="ref-container">
|
||||||
{#if comparedVersion !== undefined}
|
{#if comparedVersion !== undefined && !noButton}
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<div class="flex-grow" />
|
<div class="flex-grow" />
|
||||||
<div class="formatPanel buttons-group xsmall-gap mb-4">
|
<div class="formatPanel buttons-group xsmall-gap mb-4">
|
||||||
|
@ -30,6 +30,8 @@ export { default as CollaborationDiffViewer } from './components/CollaborationDi
|
|||||||
export { default } from './plugin'
|
export { default } from './plugin'
|
||||||
export * from './types'
|
export * from './types'
|
||||||
export { default as Collaboration } from './components/Collaboration.svelte'
|
export { default as Collaboration } from './components/Collaboration.svelte'
|
||||||
|
export { default as IconObjects } from './components/icons/Objects.svelte'
|
||||||
|
export { default as StyleButton } from './components/StyleButton.svelte'
|
||||||
|
|
||||||
addStringsLoader(textEditorId, async (lang: string) => {
|
addStringsLoader(textEditorId, async (lang: string) => {
|
||||||
return await import(`../lang/${lang}.json`)
|
return await import(`../lang/${lang}.json`)
|
||||||
|
@ -301,6 +301,7 @@ class ActivityImpl implements Activity {
|
|||||||
result.collectionAttribute = collectionAttribute
|
result.collectionAttribute = collectionAttribute
|
||||||
|
|
||||||
result.doc = firstTx?.doc ?? result.doc
|
result.doc = firstTx?.doc ?? result.doc
|
||||||
|
result.prevDoc = this.hierarchy.clone(result.doc)
|
||||||
|
|
||||||
firstTx = firstTx ?? result
|
firstTx = firstTx ?? result
|
||||||
parents.set(tx.objectId, firstTx)
|
parents.set(tx.objectId, firstTx)
|
||||||
@ -351,7 +352,7 @@ class ActivityImpl implements Activity {
|
|||||||
results.push(result)
|
results.push(result)
|
||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
const newResult = results.filter((prevTx) => {
|
const newResults = results.filter((prevTx) => {
|
||||||
const prevUpdate: any = getCombineOpFromTx(prevTx)
|
const prevUpdate: any = getCombineOpFromTx(prevTx)
|
||||||
if (this.isInitTx(prevTx, result)) {
|
if (this.isInitTx(prevTx, result)) {
|
||||||
result = prevTx
|
result = prevTx
|
||||||
@ -379,8 +380,9 @@ class ActivityImpl implements Activity {
|
|||||||
|
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
newResult.push(result)
|
|
||||||
return newResult
|
newResults.push(result)
|
||||||
|
return newResults
|
||||||
}
|
}
|
||||||
|
|
||||||
isInitTx (prevTx: DisplayTx, result: DisplayTx): boolean {
|
isInitTx (prevTx: DisplayTx, result: DisplayTx): boolean {
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
import core, { AnyAttribute, Doc, getCurrentAccount, Ref, Class, TxCUD } from '@hcengineering/core'
|
import core, { AnyAttribute, Doc, getCurrentAccount, Ref, Class, TxCUD } from '@hcengineering/core'
|
||||||
import { Asset } from '@hcengineering/platform'
|
import { Asset } from '@hcengineering/platform'
|
||||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||||
import {
|
import ui, {
|
||||||
ActionIcon,
|
ActionIcon,
|
||||||
AnyComponent,
|
AnyComponent,
|
||||||
Component,
|
Component,
|
||||||
@ -37,7 +37,7 @@
|
|||||||
import { Menu, ObjectPresenter } from '@hcengineering/view-resources'
|
import { Menu, ObjectPresenter } from '@hcengineering/view-resources'
|
||||||
import { ActivityKey } from '../activity'
|
import { ActivityKey } from '../activity'
|
||||||
import activity from '../plugin'
|
import activity from '../plugin'
|
||||||
import { getValue, TxDisplayViewlet, updateViewlet } from '../utils'
|
import { getPrevValue, getValue, TxDisplayViewlet, updateViewlet } from '../utils'
|
||||||
import TxViewTx from './TxViewTx.svelte'
|
import TxViewTx from './TxViewTx.svelte'
|
||||||
import Edit from './icons/Edit.svelte'
|
import Edit from './icons/Edit.svelte'
|
||||||
import { tick } from 'svelte'
|
import { tick } from 'svelte'
|
||||||
@ -61,7 +61,8 @@
|
|||||||
let modelIcon: Asset | undefined = undefined
|
let modelIcon: Asset | undefined = undefined
|
||||||
let iconComponent: AnyComponent | undefined = undefined
|
let iconComponent: AnyComponent | undefined = undefined
|
||||||
|
|
||||||
let edit = false
|
let edit: boolean = false
|
||||||
|
let showDiff: boolean = false
|
||||||
|
|
||||||
$: if (tx.tx._id !== ptx?.tx._id) {
|
$: if (tx.tx._id !== ptx?.tx._id) {
|
||||||
if (tx.tx.modifiedBy !== account?._id) {
|
if (tx.tx.modifiedBy !== account?._id) {
|
||||||
@ -266,9 +267,9 @@
|
|||||||
{:else}
|
{:else}
|
||||||
<span class="lower"><Label label={activity.string.Changed} /></span>
|
<span class="lower"><Label label={activity.string.Changed} /></span>
|
||||||
<span class="lower"><Label label={m.label} /></span>
|
<span class="lower"><Label label={m.label} /></span>
|
||||||
<span class="lower"><Label label={activity.string.To} /></span>
|
|
||||||
|
|
||||||
{#if !hasMessageType}
|
{#if !hasMessageType}
|
||||||
|
<span class="lower"><Label label={activity.string.To} /></span>
|
||||||
<span class="strong overflow-label">
|
<span class="strong overflow-label">
|
||||||
{#if value.isObjectSet}
|
{#if value.isObjectSet}
|
||||||
<ObjectPresenter value={value.set} inline />
|
<ObjectPresenter value={value.set} inline />
|
||||||
@ -276,6 +277,11 @@
|
|||||||
<svelte:component this={m.presenter} value={value.set} inline />
|
<svelte:component this={m.presenter} value={value.set} inline />
|
||||||
{/if}
|
{/if}
|
||||||
</span>
|
</span>
|
||||||
|
{:else}
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<span class="show-diff" on:click={() => (showDiff = !showDiff)}>
|
||||||
|
<Label label={showDiff ? ui.string.ShowLess : ui.string.ShowMore} />
|
||||||
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
{/await}
|
{/await}
|
||||||
@ -346,11 +352,18 @@
|
|||||||
{:else if hasMessageType && model.length > 0 && (tx.updateTx || tx.mixinTx)}
|
{:else if hasMessageType && model.length > 0 && (tx.updateTx || tx.mixinTx)}
|
||||||
{#await getValue(client, model[0], tx) then value}
|
{#await getValue(client, model[0], tx) then value}
|
||||||
<div class="activity-content content" class:indent={isAttached} class:contentHidden>
|
<div class="activity-content content" class:indent={isAttached} class:contentHidden>
|
||||||
<ShowMore ignore={edit}>
|
<ShowMore ignore={edit || showDiff}>
|
||||||
{#if value.isObjectSet}
|
{#if value.isObjectSet}
|
||||||
<ObjectPresenter value={value.set} inline />
|
<ObjectPresenter value={value.set} inline />
|
||||||
{:else}
|
{:else if showDiff}
|
||||||
<svelte:component this={model[0].presenter} value={value.set} inline />
|
<svelte:component
|
||||||
|
this={model[0].presenter}
|
||||||
|
value={value.set}
|
||||||
|
inline
|
||||||
|
prevValue
|
||||||
|
compareValue={getPrevValue(client, model[0], tx)}
|
||||||
|
showOnlyDiff
|
||||||
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
</ShowMore>
|
</ShowMore>
|
||||||
</div>
|
</div>
|
||||||
@ -511,4 +524,15 @@
|
|||||||
margin-top: 0.5rem;
|
margin-top: 0.5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.show-diff {
|
||||||
|
color: var(--accent-color);
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
color: var(--caption-color);
|
||||||
|
}
|
||||||
|
&:active {
|
||||||
|
color: var(--accent-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -2,8 +2,11 @@ import type { DisplayTx, TxViewlet } from '@hcengineering/activity'
|
|||||||
import core, {
|
import core, {
|
||||||
AttachedDoc,
|
AttachedDoc,
|
||||||
Class,
|
Class,
|
||||||
|
Client,
|
||||||
Collection,
|
Collection,
|
||||||
Doc,
|
Doc,
|
||||||
|
getObjectValue,
|
||||||
|
Obj,
|
||||||
Ref,
|
Ref,
|
||||||
TxCollectionCUD,
|
TxCollectionCUD,
|
||||||
TxCreateDoc,
|
TxCreateDoc,
|
||||||
@ -13,12 +16,13 @@ import core, {
|
|||||||
TxProcessor,
|
TxProcessor,
|
||||||
TxUpdateDoc
|
TxUpdateDoc
|
||||||
} from '@hcengineering/core'
|
} from '@hcengineering/core'
|
||||||
import { Asset, IntlString, translate } from '@hcengineering/platform'
|
import { Asset, IntlString, getResource, translate } from '@hcengineering/platform'
|
||||||
import { AnyComponent, AnySvelteComponent } from '@hcengineering/ui'
|
import { AnyComponent, AnySvelteComponent, ErrorPresenter } from '@hcengineering/ui'
|
||||||
import { AttributeModel } from '@hcengineering/view'
|
import view, { AttributeModel, BuildModelKey, BuildModelOptions } from '@hcengineering/view'
|
||||||
import { buildModel, getObjectPresenter } from '@hcengineering/view-resources'
|
import { getObjectPresenter } from '@hcengineering/view-resources'
|
||||||
import { ActivityKey, activityKey } from './activity'
|
import { ActivityKey, activityKey } from './activity'
|
||||||
import activity from './plugin'
|
import activity from './plugin'
|
||||||
|
import { getAttributePresenterClass } from '@hcengineering/presentation'
|
||||||
|
|
||||||
const valueTypes: ReadonlyArray<Ref<Class<Doc>>> = [
|
const valueTypes: ReadonlyArray<Ref<Class<Doc>>> = [
|
||||||
core.class.TypeString,
|
core.class.TypeString,
|
||||||
@ -150,6 +154,77 @@ async function checkInlineViewlets (
|
|||||||
return { viewlet, model }
|
return { viewlet, model }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getAttributePresenter (
|
||||||
|
client: Client,
|
||||||
|
_class: Ref<Class<Obj>>,
|
||||||
|
key: string,
|
||||||
|
preserveKey: BuildModelKey
|
||||||
|
): Promise<AttributeModel> {
|
||||||
|
const hierarchy = client.getHierarchy()
|
||||||
|
const attribute = hierarchy.getAttribute(_class, key)
|
||||||
|
const presenterClass = getAttributePresenterClass(hierarchy, attribute)
|
||||||
|
const isCollectionAttr = presenterClass.category === 'collection'
|
||||||
|
const mixin = isCollectionAttr ? view.mixin.CollectionPresenter : view.mixin.ActivityAttributePresenter
|
||||||
|
let presenterMixin = hierarchy.classHierarchyMixin(presenterClass.attrClass, mixin)
|
||||||
|
if (presenterMixin?.presenter === undefined && mixin === view.mixin.ActivityAttributePresenter) {
|
||||||
|
presenterMixin = hierarchy.classHierarchyMixin(presenterClass.attrClass, view.mixin.AttributePresenter)
|
||||||
|
if (presenterMixin?.presenter === undefined) {
|
||||||
|
throw new Error('attribute presenter not found for ' + JSON.stringify(preserveKey))
|
||||||
|
}
|
||||||
|
} else if (presenterMixin?.presenter === undefined) {
|
||||||
|
throw new Error('attribute presenter not found for ' + JSON.stringify(preserveKey))
|
||||||
|
}
|
||||||
|
const resultKey = preserveKey.sortingKey ?? preserveKey.key
|
||||||
|
const sortingKey = Array.isArray(resultKey)
|
||||||
|
? resultKey
|
||||||
|
: attribute.type._class === core.class.ArrOf
|
||||||
|
? resultKey + '.length'
|
||||||
|
: resultKey
|
||||||
|
const presenter = await getResource(presenterMixin.presenter)
|
||||||
|
|
||||||
|
return {
|
||||||
|
key: preserveKey.key,
|
||||||
|
sortingKey,
|
||||||
|
_class: presenterClass.attrClass,
|
||||||
|
label: preserveKey.label ?? attribute.shortLabel ?? attribute.label,
|
||||||
|
presenter,
|
||||||
|
props: preserveKey.props,
|
||||||
|
icon: presenterMixin.icon,
|
||||||
|
attribute,
|
||||||
|
collectionAttr: isCollectionAttr,
|
||||||
|
isLookup: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function buildModel (options: BuildModelOptions): Promise<AttributeModel[]> {
|
||||||
|
// eslint-disable-next-line array-callback-return
|
||||||
|
const model = options.keys
|
||||||
|
.map((key) => (typeof key === 'string' ? { key } : key))
|
||||||
|
.map(async (key) => {
|
||||||
|
try {
|
||||||
|
return await getAttributePresenter(options.client, options._class, key.key, key)
|
||||||
|
} catch (err: any) {
|
||||||
|
if (options.ignoreMissing ?? false) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
const stringKey = key.label ?? key.key
|
||||||
|
console.error('Failed to find presenter for', key, err)
|
||||||
|
const errorPresenter: AttributeModel = {
|
||||||
|
key: '',
|
||||||
|
sortingKey: '',
|
||||||
|
presenter: ErrorPresenter,
|
||||||
|
label: stringKey as IntlString,
|
||||||
|
_class: core.class.TypeString,
|
||||||
|
props: { error: err },
|
||||||
|
collectionAttr: false,
|
||||||
|
isLookup: false
|
||||||
|
}
|
||||||
|
return errorPresenter
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return (await Promise.all(model)).filter((a) => a !== undefined) as AttributeModel[]
|
||||||
|
}
|
||||||
|
|
||||||
async function createUpdateModel (
|
async function createUpdateModel (
|
||||||
dtx: DisplayTx,
|
dtx: DisplayTx,
|
||||||
client: TxOperations,
|
client: TxOperations,
|
||||||
@ -301,6 +376,13 @@ export async function getValue (client: TxOperations, m: AttributeModel, tx: Dis
|
|||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getPrevValue (client: TxOperations, m: AttributeModel, tx: DisplayTx): any {
|
||||||
|
if (tx.prevDoc !== undefined) {
|
||||||
|
return getObjectValue(m.key, tx.prevDoc)
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
export function filterCollectionTxes (txes: DisplayTx[]): DisplayTx[] {
|
export function filterCollectionTxes (txes: DisplayTx[]): DisplayTx[] {
|
||||||
return txes.map(filterCollectionTx).filter(Boolean) as DisplayTx[]
|
return txes.map(filterCollectionTx).filter(Boolean) as DisplayTx[]
|
||||||
}
|
}
|
||||||
|
@ -84,6 +84,8 @@ export interface DisplayTx {
|
|||||||
|
|
||||||
// Document in case it is required.
|
// Document in case it is required.
|
||||||
doc?: Doc
|
doc?: Doc
|
||||||
|
// Previous document in case it is required.
|
||||||
|
prevDoc?: Doc
|
||||||
|
|
||||||
updated: boolean
|
updated: boolean
|
||||||
mixin: boolean
|
mixin: boolean
|
||||||
|
@ -0,0 +1,49 @@
|
|||||||
|
<!--
|
||||||
|
// Copyright © 2023 Anticrm Platform Contributors.
|
||||||
|
// 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 { CollaborationDiffViewer } from '@hcengineering/text-editor'
|
||||||
|
import { ShowMore } from '@hcengineering/ui'
|
||||||
|
|
||||||
|
export let value: string | undefined
|
||||||
|
export let compareValue: string | undefined = undefined
|
||||||
|
export let showOnlyDiff: boolean = false
|
||||||
|
|
||||||
|
function removeSimilarLines (str1: string | undefined, str2: string | undefined) {
|
||||||
|
if (str1 === undefined || str2 === undefined) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const lines1 = str1.split('</p>')
|
||||||
|
const lines2 = str2.split('</p>')
|
||||||
|
let result1 = ''
|
||||||
|
let result2 = ''
|
||||||
|
for (let i = 0; i < lines1.length; i++) {
|
||||||
|
if (lines1[i] !== lines2[i]) {
|
||||||
|
result1 += lines1[i] ?? '' + '</p>'
|
||||||
|
result2 += lines2[i] ?? '' + '</p>'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value = result1
|
||||||
|
compareValue = result2
|
||||||
|
}
|
||||||
|
|
||||||
|
$: showOnlyDiff && removeSimilarLines(value, compareValue)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ShowMore>
|
||||||
|
{#key [value, compareValue]}
|
||||||
|
<CollaborationDiffViewer content={value ?? ''} comparedVersion={compareValue} noButton readonly />
|
||||||
|
{/key}
|
||||||
|
</ShowMore>
|
@ -49,6 +49,7 @@ import DividerPresenter from './components/list/DividerPresenter.svelte'
|
|||||||
import ListView from './components/list/ListView.svelte'
|
import ListView from './components/list/ListView.svelte'
|
||||||
import SortableList from './components/list/SortableList.svelte'
|
import SortableList from './components/list/SortableList.svelte'
|
||||||
import SortableListItem from './components/list/SortableListItem.svelte'
|
import SortableListItem from './components/list/SortableListItem.svelte'
|
||||||
|
import MarkupDiffPresenter from './components/MarkupDiffPresenter.svelte'
|
||||||
import MarkupEditor from './components/MarkupEditor.svelte'
|
import MarkupEditor from './components/MarkupEditor.svelte'
|
||||||
import MarkupEditorPopup from './components/MarkupEditorPopup.svelte'
|
import MarkupEditorPopup from './components/MarkupEditorPopup.svelte'
|
||||||
import MarkupPresenter from './components/MarkupPresenter.svelte'
|
import MarkupPresenter from './components/MarkupPresenter.svelte'
|
||||||
@ -94,6 +95,7 @@ export { default as FixedColumn } from './components/FixedColumn.svelte'
|
|||||||
export { default as SourcePresenter } from './components/inference/SourcePresenter.svelte'
|
export { default as SourcePresenter } from './components/inference/SourcePresenter.svelte'
|
||||||
export { default as LinkPresenter } from './components/LinkPresenter.svelte'
|
export { default as LinkPresenter } from './components/LinkPresenter.svelte'
|
||||||
export { default as List } from './components/list/List.svelte'
|
export { default as List } from './components/list/List.svelte'
|
||||||
|
export { default as MarkupDiffPresenter } from './components/MarkupDiffPresenter.svelte'
|
||||||
export { default as MarkupPresenter } from './components/MarkupPresenter.svelte'
|
export { default as MarkupPresenter } from './components/MarkupPresenter.svelte'
|
||||||
export { default as MarkupPreviewPopup } from './components/MarkupPreviewPopup.svelte'
|
export { default as MarkupPreviewPopup } from './components/MarkupPreviewPopup.svelte'
|
||||||
export { default as ContextMenu } from './components/Menu.svelte'
|
export { default as ContextMenu } from './components/Menu.svelte'
|
||||||
@ -189,6 +191,7 @@ export default async (): Promise<Resources> => ({
|
|||||||
ActionsPopup,
|
ActionsPopup,
|
||||||
StringEditorPopup: EditBoxPopup,
|
StringEditorPopup: EditBoxPopup,
|
||||||
MarkupPresenter,
|
MarkupPresenter,
|
||||||
|
MarkupDiffPresenter,
|
||||||
MarkupEditor,
|
MarkupEditor,
|
||||||
MarkupEditorPopup,
|
MarkupEditorPopup,
|
||||||
BooleanTruePresenter,
|
BooleanTruePresenter,
|
||||||
|
@ -159,6 +159,13 @@ export interface AttributePresenter extends Class<Doc> {
|
|||||||
presenter: AnyComponent
|
presenter: AnyComponent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface ActivityAttributePresenter extends Class<Doc> {
|
||||||
|
presenter: AnyComponent
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
@ -603,6 +610,7 @@ const view = plugin(viewId, {
|
|||||||
InlineAttributEditor: '' as Ref<Mixin<InlineAttributEditor>>,
|
InlineAttributEditor: '' as Ref<Mixin<InlineAttributEditor>>,
|
||||||
ArrayEditor: '' as Ref<Mixin<ArrayEditor>>,
|
ArrayEditor: '' as Ref<Mixin<ArrayEditor>>,
|
||||||
AttributePresenter: '' as Ref<Mixin<AttributePresenter>>,
|
AttributePresenter: '' as Ref<Mixin<AttributePresenter>>,
|
||||||
|
ActivityAttributePresenter: '' as Ref<Mixin<ActivityAttributePresenter>>,
|
||||||
ListItemPresenter: '' as Ref<Mixin<ListItemPresenter>>,
|
ListItemPresenter: '' as Ref<Mixin<ListItemPresenter>>,
|
||||||
ObjectEditor: '' as Ref<Mixin<ObjectEditor>>,
|
ObjectEditor: '' as Ref<Mixin<ObjectEditor>>,
|
||||||
ObjectPresenter: '' as Ref<Mixin<ObjectPresenter>>,
|
ObjectPresenter: '' as Ref<Mixin<ObjectPresenter>>,
|
||||||
|
Loading…
Reference in New Issue
Block a user