diff --git a/models/notification/package.json b/models/notification/package.json
index 94a5d85f6c..12610b0698 100644
--- a/models/notification/package.json
+++ b/models/notification/package.json
@@ -34,6 +34,7 @@
"@hcengineering/model-view": "^0.6.0",
"@hcengineering/view": "^0.6.6",
"@hcengineering/workbench": "^0.6.6",
+ "@hcengineering/model-workbench": "^0.6.1",
"@hcengineering/notification": "^0.6.12",
"@hcengineering/setting": "^0.6.7"
}
diff --git a/models/notification/src/index.ts b/models/notification/src/index.ts
index 40784297b6..2e9008a95b 100644
--- a/models/notification/src/index.ts
+++ b/models/notification/src/index.ts
@@ -34,6 +34,7 @@ import { ArrOf, Builder, Index, Mixin, Model, Prop, TypeRef, TypeString, UX } fr
import core, { TAttachedDoc, TClass, TDoc } from '@hcengineering/model-core'
import preference, { TPreference } from '@hcengineering/model-preference'
import view, { createAction } from '@hcengineering/model-view'
+import workbench from '@hcengineering/model-workbench'
import {
DocUpdates,
EmailNotification,
@@ -50,7 +51,6 @@ import {
import type { Asset, IntlString } from '@hcengineering/platform'
import setting from '@hcengineering/setting'
import { AnyComponent } from '@hcengineering/ui'
-import workbench from '@hcengineering/workbench'
import notification from './plugin'
export { notificationId } from '@hcengineering/notification'
@@ -284,6 +284,23 @@ export function createModel (builder: Builder): void {
builder.mixin(notification.class.DocUpdates, core.class.Class, view.mixin.IgnoreActions, {
actions: [view.action.Delete, view.action.Open]
})
+
+ createAction(builder, {
+ action: workbench.actionImpl.Navigate,
+ actionProps: {
+ mode: 'app',
+ application: notificationId,
+ special: notificationId
+ },
+ label: notification.string.Inbox,
+ icon: view.icon.ArrowRight,
+ input: 'none',
+ category: view.category.Navigation,
+ target: core.class.Doc,
+ context: {
+ mode: ['workbench', 'browser', 'editor', 'panel', 'popup']
+ }
+ })
}
export function generateClassNotificationTypes (
diff --git a/packages/text-editor/src/components/ReferenceInput.svelte b/packages/text-editor/src/components/ReferenceInput.svelte
index b0f1c604ad..4a43026d25 100644
--- a/packages/text-editor/src/components/ReferenceInput.svelte
+++ b/packages/text-editor/src/components/ReferenceInput.svelte
@@ -213,15 +213,17 @@
export let focusIndex = -1
const { idx, focusManager } = registerFocus(focusIndex, {
focus: () => {
- focused = true
- textEditor.focus()
- return textEditor.isEditable()
+ const editable = textEditor.isEditable()
+ if (editable) {
+ focused = true
+ textEditor.focus()
+ }
+ return editable
},
isFocus: () => focused
})
const updateFocus = () => {
if (focusIndex !== -1) {
- console.trace('focuse')
focusManager?.setFocus(idx)
}
}
diff --git a/packages/text-editor/src/components/StyledTextBox.svelte b/packages/text-editor/src/components/StyledTextBox.svelte
index 5bd6cfba44..f171fd24bb 100644
--- a/packages/text-editor/src/components/StyledTextBox.svelte
+++ b/packages/text-editor/src/components/StyledTextBox.svelte
@@ -104,9 +104,12 @@
export let focusIndex = -1
const { idx, focusManager } = registerFocus(focusIndex, {
focus: () => {
- focused = true
- focus()
- return textEditor.isEditable()
+ const editable = textEditor.isEditable()
+ if (editable) {
+ focused = true
+ focus()
+ }
+ return editable
},
isFocus: () => focused
})
diff --git a/packages/ui/src/components/FocusHandler.svelte b/packages/ui/src/components/FocusHandler.svelte
index 1272aaaf46..e31bb1f286 100644
--- a/packages/ui/src/components/FocusHandler.svelte
+++ b/packages/ui/src/components/FocusHandler.svelte
@@ -2,9 +2,10 @@
import { FocusManager } from '../focus'
export let manager: FocusManager
+ export let isEnabled: boolean = true
function handleKey (evt: KeyboardEvent): void {
- if (evt.code === 'Tab') {
+ if (evt.code === 'Tab' && isEnabled) {
evt.preventDefault()
evt.stopPropagation()
manager.next(evt.shiftKey ? -1 : 1)
diff --git a/packages/ui/src/components/Scroller.svelte b/packages/ui/src/components/Scroller.svelte
index af6b89b17d..0f8a095bff 100644
--- a/packages/ui/src/components/Scroller.svelte
+++ b/packages/ui/src/components/Scroller.svelte
@@ -22,6 +22,7 @@
import IconUpOutline from './icons/UpOutline.svelte'
import IconDownOutline from './icons/DownOutline.svelte'
import HalfUpDown from './icons/HalfUpDown.svelte'
+ import { isSafari } from '../utils'
export let padding: string | undefined = undefined
export let autoscroll: boolean = false
@@ -291,7 +292,9 @@
}
const scrollDown = (): void => {
- if (divScroll) divScroll.scrollTop = divScroll.scrollHeight - divHeight + 2
+ if (divScroll) {
+ divScroll.scrollTop = divScroll.scrollHeight - divHeight + 2
+ }
}
$: if (scrolling && belowContent && belowContent > 0) scrollDown()
@@ -444,6 +447,7 @@
(orientir === 'horizontal' && (maskH === 'left' || maskH === 'both'))
? 'visible'
: 'hidden'
+ let scrollY: number = 0
@@ -467,8 +471,17 @@
}}
class="scroll relative flex-shrink"
class:overflow-x={horizontal ? 'auto' : 'hidden'}
- on:scroll={() => {
+ on:scroll={(evt) => {
if ($tooltipstore.label !== undefined) closeTooltip()
+ const newPos = divScroll?.scrollTop ?? 0
+
+ // TODO: Workaround: https://front.hc.engineering/workbench/platform/tracker/TSK-760
+ // In Safari scroll could jump on click, with no particular reason.
+
+ if (scrollY !== 0 && Math.abs(newPos - scrollY) > 100 && divScroll !== undefined && isSafari()) {
+ divScroll.scrollTop = scrollY
+ }
+ scrollY = divScroll?.scrollTop ?? 0
}}
>
it.order === order)
if (idx !== undefined) {
this.current = idx
diff --git a/packages/ui/src/utils.ts b/packages/ui/src/utils.ts
index ac99ce0875..ae5b1b65c6 100644
--- a/packages/ui/src/utils.ts
+++ b/packages/ui/src/utils.ts
@@ -16,11 +16,14 @@
import { generateId } from '@hcengineering/core'
import type { Metadata } from '@hcengineering/platform'
import { setMetadata } from '@hcengineering/platform'
-import { writable } from 'svelte/store'
import autolinker from 'autolinker'
+import { writable } from 'svelte/store'
import { Notification, NotificationPosition, NotificationSeverity, notificationsStore } from '.'
import { AnyComponent, AnySvelteComponent } from './types'
+/**
+ * @public
+ */
export function setMetadataLocalStorage
(id: Metadata, value: T | null): void {
if (value != null) {
localStorage.setItem(id, typeof value === 'string' ? value : JSON.stringify(value))
@@ -30,6 +33,9 @@ export function setMetadataLocalStorage (id: Metadata, value: T | null): v
setMetadata(id, value)
}
+/**
+ * @public
+ */
export function fetchMetadataLocalStorage (id: Metadata): T | null {
const data = localStorage.getItem(id)
if (data === null) {
@@ -45,14 +51,27 @@ export function fetchMetadataLocalStorage (id: Metadata): T | null {
}
}
+/**
+ * @public
+ */
export function checkMobile (): boolean {
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|Mobile|Opera Mini/i.test(navigator.userAgent)
}
+/**
+ * @public
+ */
+export function isSafari (): boolean {
+ return navigator.userAgent.toLowerCase().includes('safari/')
+}
+
export function floorFractionDigits (n: number | string, amount: number): number {
return Number(Number(n).toFixed(amount))
}
+/**
+ * @public
+ */
export function addNotification (
title: string,
subTitle: string,
diff --git a/plugins/contact-resources/src/components/Avatar.svelte b/plugins/contact-resources/src/components/Avatar.svelte
index 3dc83d4ede..c64b07f15c 100644
--- a/plugins/contact-resources/src/components/Avatar.svelte
+++ b/plugins/contact-resources/src/components/Avatar.svelte
@@ -79,10 +79,12 @@
} else {
const uri = avatar.split('://')[1]
- const color = (await getResource(avatarProvider.getUrl))(uri, size)
- style = `background-color: ${color}`
- accentColor = hexToRgb(color)
- dispatch('accent-color', accentColor)
+ const color: string | undefined = (await getResource(avatarProvider.getUrl))(uri, size)
+ if (color != null) {
+ style = `background-color: ${color}`
+ accentColor = hexToRgb(color)
+ dispatch('accent-color', accentColor)
+ }
}
}
$: updateStyle(avatar, avatarProvider)
diff --git a/plugins/notification-resources/src/components/Inbox.svelte b/plugins/notification-resources/src/components/Inbox.svelte
index 9ba121d77e..064ba2ac08 100644
--- a/plugins/notification-resources/src/components/Inbox.svelte
+++ b/plugins/notification-resources/src/components/Inbox.svelte
@@ -18,7 +18,7 @@
import { Class, Doc, getCurrentAccount, Ref } from '@hcengineering/core'
import notification, { DocUpdates } from '@hcengineering/notification'
import { createQuery, getClient } from '@hcengineering/presentation'
- import { AnyComponent, Component, Label, Loading, Scroller } from '@hcengineering/ui'
+ import { AnyComponent, Component, Label, ListView, Loading, Scroller } from '@hcengineering/ui'
import view from '@hcengineering/view'
import { ActionContext, ListSelectionProvider, SelectDirection } from '@hcengineering/view-resources'
import NotificationView from './NotificationView.svelte'
@@ -94,6 +94,7 @@
const value = selected + offset
if (docs[value] !== undefined) {
selected = value
+ listView?.select(selected)
}
}
})
@@ -104,6 +105,7 @@
})
let selected = 0
+ let listView: ListView
{:else}
- {#each docs as doc, i}
- {
- selected = i
- }}
- />
- {/each}
+
+
+ {
+ selected = item
+ }}
+ />
+
+
{/if}
diff --git a/plugins/tracker-resources/src/components/issues/edit/EditIssue.svelte b/plugins/tracker-resources/src/components/issues/edit/EditIssue.svelte
index 0ab573e816..7af383e574 100644
--- a/plugins/tracker-resources/src/components/issues/edit/EditIssue.svelte
+++ b/plugins/tracker-resources/src/components/issues/edit/EditIssue.svelte
@@ -34,7 +34,7 @@
navigate,
showPopup
} from '@hcengineering/ui'
- import { ActionContext, ContextMenu, DocNavLink, UpDownNavigator } from '@hcengineering/view-resources'
+ import { ActionContext, ContextMenu, DocNavLink, UpDownNavigator, contextStore } from '@hcengineering/view-resources'
import { createEventDispatcher, onDestroy, onMount } from 'svelte'
import { generateIssueShortLink, getIssueId } from '../../../issues'
import tracker from '../../../plugin'
@@ -149,14 +149,20 @@
}
return true
}
+
+ // If it is embedded
+ $: lastCtx = $contextStore.getLastContext()
+ $: isContextEnabled = lastCtx?.mode === 'editor' || lastCtx?.mode === 'browser'
-
-
+{#if !embedded}
+
+
+{/if}
{#if issue !== undefined}
{#key issue._id}
diff --git a/plugins/view-resources/src/actionImpl.ts b/plugins/view-resources/src/actionImpl.ts
index e8deebbe4f..78c53b9c63 100644
--- a/plugins/view-resources/src/actionImpl.ts
+++ b/plugins/view-resources/src/actionImpl.ts
@@ -1,22 +1,21 @@
import { Class, Doc, DocumentQuery, Hierarchy, Ref, Space, TxResult } from '@hcengineering/core'
-import { Asset, getResource, IntlString, Resource } from '@hcengineering/platform'
-import { getClient, MessageBox, updateAttribute } from '@hcengineering/presentation'
+import { Asset, IntlString, Resource, getResource } from '@hcengineering/platform'
+import { MessageBox, getClient, updateAttribute } from '@hcengineering/presentation'
import {
AnyComponent,
AnySvelteComponent,
+ PopupAlignment,
+ PopupPosAlignment,
closeTooltip,
isPopupPosAlignment,
navigate,
- PopupAlignment,
- PopupPosAlignment,
showPanel,
showPopup
} from '@hcengineering/ui'
-import { ViewContext } from '@hcengineering/view'
import MoveView from './components/Move.svelte'
-import { contextStore } from './context'
+import { ContextStore, contextStore } from './context'
import view from './plugin'
-import { FocusSelection, focusStore, previewDocument, SelectDirection, selectionStore } from './selection'
+import { FocusSelection, SelectDirection, focusStore, previewDocument, selectionStore } from './selection'
import { deleteObjects, getObjectLinkFragment } from './utils'
/**
@@ -100,7 +99,7 @@ focusStore.subscribe((it) => {
$focusStore = it
})
-let $contextStore: ViewContext[]
+let $contextStore: ContextStore
contextStore.subscribe((it) => {
$contextStore = it
})
@@ -153,7 +152,7 @@ const MoveRight = (doc: Doc | undefined, evt: Event): void => select(evt, 1, $fo
function ShowActions (doc: Doc | Doc[] | undefined, evt: Event): void {
evt.preventDefault()
- showPopup(view.component.ActionsPopup, { viewContext: $contextStore[$contextStore.length - 1] }, 'top')
+ showPopup(view.component.ActionsPopup, { viewContext: $contextStore.getLastContext() }, 'top')
}
function ShowPreview (doc: Doc | Doc[] | undefined, evt: Event): void {
diff --git a/plugins/view-resources/src/components/ActionContext.svelte b/plugins/view-resources/src/components/ActionContext.svelte
index 49dafd60a6..d1246dd9bf 100644
--- a/plugins/view-resources/src/components/ActionContext.svelte
+++ b/plugins/view-resources/src/components/ActionContext.svelte
@@ -16,34 +16,34 @@
import { generateId } from '@hcengineering/core'
import { ViewContext } from '@hcengineering/view'
import { onDestroy } from 'svelte'
- import { contextStore } from '../context'
+ import { ContextStore, contextStore } from '../context'
export let context: ViewContext
const id = generateId()
- $: len = $contextStore.findIndex((it) => (it as any).id === id)
+ $: len = $contextStore.contexts.findIndex((it) => (it as any).id === id)
onDestroy(() => {
contextStore.update((t) => {
- return t.slice(0, len ?? 0)
+ return new ContextStore(t.contexts.slice(0, len ?? 0))
})
})
$: {
contextStore.update((cur) => {
- const pos = cur.findIndex((it) => (it as any).id === id)
+ const pos = cur.contexts.findIndex((it) => (it as any).id === id)
const newCur = {
id,
mode: context.mode,
- application: context.application ?? cur[(pos !== -1 ? pos : cur.length) - 1]?.application
+ application: context.application ?? cur.contexts[(pos !== -1 ? pos : cur.contexts.length) - 1]?.application
}
if (pos === -1) {
- len = cur.length
- return [...cur, newCur]
+ len = cur.contexts.length
+ return new ContextStore([...cur.contexts, newCur])
}
len = pos
- return [...cur.slice(0, pos), newCur]
+ return new ContextStore([...cur.contexts.slice(0, pos), newCur])
})
}
diff --git a/plugins/view-resources/src/components/ActionHandler.svelte b/plugins/view-resources/src/components/ActionHandler.svelte
index 71e2412404..039486e25a 100644
--- a/plugins/view-resources/src/components/ActionHandler.svelte
+++ b/plugins/view-resources/src/components/ActionHandler.svelte
@@ -60,9 +60,9 @@
return await getContextActions(client, docs, context)
}
- $: ctx = $contextStore[$contextStore.length - 1]
- $: mode = $contextStore[$contextStore.length - 1]?.mode
- $: application = $contextStore[$contextStore.length - 1]?.application
+ $: ctx = $contextStore.getLastContext()
+ $: mode = $contextStore.getLastContext()?.mode
+ $: application = $contextStore.getLastContext()?.application
function keyPrefix (key: KeyboardEvent): string {
return (
@@ -158,7 +158,7 @@
}
// For none we ignore all actions.
- if (ctx.mode === 'none') {
+ if (ctx?.mode === 'none') {
return
}
clearTimeout(timer)
diff --git a/plugins/view-resources/src/context.ts b/plugins/view-resources/src/context.ts
index 132e969a13..ffa7ddbb54 100644
--- a/plugins/view-resources/src/context.ts
+++ b/plugins/view-resources/src/context.ts
@@ -1,7 +1,23 @@
-import { ViewContext } from '@hcengineering/view'
+import { ViewContext, ViewContextType } from '@hcengineering/view'
import { writable } from 'svelte/store'
/**
* @public
*/
-export const contextStore = writable([])
+export class ContextStore {
+ constructor (readonly contexts: ViewContext[]) {}
+
+ getLastContext (): ViewContext | undefined {
+ return this.contexts[this.contexts.length - 1]
+ }
+
+ isIncludes (type: ViewContextType): boolean {
+ return (
+ this.contexts.find((it) => it.mode === type || (Array.isArray(it.mode) && it.mode.includes(type))) !== undefined
+ )
+ }
+}
+/**
+ * @public
+ */
+export const contextStore = writable(new ContextStore([]))