Merge remote-tracking branch 'origin/develop' into staging

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev 2024-10-11 18:34:46 +07:00
commit bcf0901c0f
No known key found for this signature in database
GPG Key ID: BD80F68D68D8F7F2
6 changed files with 137 additions and 33 deletions

View File

@ -13,7 +13,7 @@
// limitations under the License.
-->
<script lang="ts">
import { Location, location, locationToUrl, navigate } from '@hcengineering/ui'
import { Location, location, locationStorageKeyId, locationToUrl, navigate } from '@hcengineering/ui'
import { setFilters } from '../../filter'
export let app: string | undefined = undefined
@ -21,6 +21,7 @@
export let special: string | undefined = undefined
export let disabled = false
export let shrink: number | undefined = undefined
export let restoreLastLocation = false
$: loc = createLocation($location, app, space, special)
@ -32,6 +33,20 @@
space: string | undefined,
special: string | undefined
): Location {
if (restoreLastLocation) {
const last = localStorage.getItem(`${locationStorageKeyId}_${app}`)
if (last != null) {
try {
const newLocation: Location = JSON.parse(last)
if (newLocation.path[2] === app && newLocation.path[3] != null) {
return newLocation
}
} catch (e) {
// ignore
}
}
}
const location: Location = {
path: [...loc.path]
}
@ -50,7 +65,7 @@
return location
}
function clickHandler (e: MouseEvent) {
function clickHandler (e: MouseEvent): void {
if (e.metaKey || e.ctrlKey) return
e.preventDefault()
setFilters([])

View File

@ -58,13 +58,13 @@
buttons={'union'}
>
{#each topApps as app}
<NavLink app={app.alias} shrink={0}>
<NavLink app={app.alias} shrink={0} disabled={app._id === active}>
<AppItem selected={app._id === active} icon={app.icon} label={app.label} />
</NavLink>
{/each}
<div class="divider" />
{#each bottomdApps as app}
<NavLink app={app.alias} shrink={0}>
<NavLink app={app.alias} shrink={0} disabled={app._id === active}>
<AppItem selected={app._id === active} icon={app.icon} label={app.label} />
</NavLink>
{/each}

View File

@ -29,7 +29,7 @@
import login from '@hcengineering/login'
import notification, { DocNotifyContext, InboxNotification, notificationId } from '@hcengineering/notification'
import { BrowserNotificatator, InboxNotificationsClientImpl } from '@hcengineering/notification-resources'
import { broadcastEvent, getMetadata, getResource, IntlString } from '@hcengineering/platform'
import { broadcastEvent, getMetadata, getResource, IntlString, translate } from '@hcengineering/platform'
import {
ActionContext,
ComponentExtensions,
@ -56,6 +56,7 @@
getLocation,
IconSettings,
Label,
languageStore,
Location,
location,
locationStorageKeyId,
@ -112,7 +113,16 @@
import TopMenu from './icons/TopMenu.svelte'
import WidgetsBar from './sidebar/Sidebar.svelte'
import { sidebarStore, SidebarVariant, syncSidebarState } from '../sidebar'
import { getTabLocation, selectTab, syncWorkbenchTab, tabIdStore, tabsStore } from '../workbench'
import {
getTabDataByLocation,
getTabLocation,
prevTabIdStore,
selectTab,
syncWorkbenchTab,
tabIdStore,
tabsStore
} from '../workbench'
import { get } from 'svelte/store'
let contentPanel: HTMLElement
@ -161,7 +171,6 @@
let tabs: WorkbenchTab[] = []
let areTabsLoaded = false
let prevTab: Ref<WorkbenchTab> | undefined
const query = createQuery()
$: query.query(
@ -190,26 +199,35 @@
const isLocEqual = tabLoc ? areLocationsEqual(loc, tabLoc) : false
if (!isLocEqual) {
const url = locationToUrl(loc)
const tabByUrl = tabs.find((t) => t.location === url)
if (tabByUrl !== undefined) {
prevTab = tabByUrl._id
selectTab(tabByUrl._id)
const data = await getTabDataByLocation(loc)
const name = data.name ?? (await translate(data.label, {}, get(languageStore)))
const tabByName = get(tabsStore).find((t) => {
if (t.location === url) return true
if (t.name !== name) return false
const tabLoc = getTabLocation(t)
return tabLoc.path[2] === loc.path[2] && tabLoc.path[3] === loc.path[3]
})
if (tabByName !== undefined) {
selectTab(tabByName._id)
prevTabIdStore.set(tabByName._id)
} else {
const tabToReplace = tabs.findLast((t) => !t.isPinned)
if (tabToReplace !== undefined) {
await client.update(tabToReplace, {
location: url
})
prevTab = tabToReplace._id
selectTab(tabToReplace._id)
prevTabIdStore.set(tabToReplace._id)
} else {
const _id = await client.createDoc(workbench.class.WorkbenchTab, core.space.Workspace, {
attachedTo: account._id,
location: url,
isPinned: false
})
prevTab = _id
selectTab(_id)
prevTabIdStore.set(_id)
}
}
}
@ -372,12 +390,16 @@
async function syncLoc (loc: Location): Promise<void> {
accessDeniedStore.set(false)
const originalLoc = JSON.stringify(loc)
if ($tabIdStore !== prevTab) {
if (prevTab) {
clear(1)
clear(2)
if ($tabIdStore !== $prevTabIdStore) {
if ($prevTabIdStore) {
const prevTab = tabs.find((t) => t._id === $prevTabIdStore)
const prevTabLoc = prevTab ? getTabLocation(prevTab) : undefined
if (prevTabLoc === undefined || prevTabLoc.path[2] !== loc.path[2]) {
clear(1)
clear(2)
}
}
prevTab = $tabIdStore
prevTabIdStore.set($tabIdStore)
}
if (loc.path.length > 3 && getSpecialComponent(loc.path[3]) === undefined) {
// resolve short links
@ -766,7 +788,7 @@
/>
</div>
<!-- <ActivityStatus status="active" /> -->
<NavLink app={notificationId} shrink={0}>
<NavLink app={notificationId} shrink={0} restoreLastLocation>
<AppItem
icon={notification.icon.Notifications}
label={notification.string.Inbox}

View File

@ -100,6 +100,8 @@
<svelte:fragment slot="postfix">
{#if tab.isPinned}
<Icon icon={view.icon.PinTack} size="x-small" />
{:else if $tabsStore.length === 1}
<div />
{/if}
</svelte:fragment>
</ModernTab>

View File

@ -13,7 +13,15 @@
// limitations under the License.
//
import { derived, get, writable } from 'svelte/store'
import core, { type Class, concatLink, type Doc, getCurrentAccount, type Ref } from '@hcengineering/core'
import core, {
type Class,
concatLink,
type Doc,
getCurrentAccount,
type Ref,
RateLimiter,
generateId
} from '@hcengineering/core'
import { type Application, workbenchId, type WorkbenchTab } from '@hcengineering/workbench'
import {
location as locationStore,
@ -23,29 +31,40 @@ import {
navigate,
getCurrentLocation,
languageStore,
type AnyComponent
type AnyComponent,
locationStorageKeyId
} from '@hcengineering/ui'
import presentation, { getClient } from '@hcengineering/presentation'
import view from '@hcengineering/view'
import { type Asset, type IntlString, getMetadata, getResource, translate } from '@hcengineering/platform'
import { parseLinkId } from '@hcengineering/view-resources'
import { notificationId } from '@hcengineering/notification'
import notification, { notificationId } from '@hcengineering/notification'
import { workspaceStore } from './utils'
import workbench from './plugin'
export const tabIdStore = writable<Ref<WorkbenchTab> | undefined>()
export const prevTabIdStore = writable<Ref<WorkbenchTab> | undefined>()
export const tabsStore = writable<WorkbenchTab[]>([])
export const currentTabStore = derived([tabIdStore, tabsStore], ([tabId, tabs]) => {
return tabs.find((tab) => tab._id === tabId)
})
let prevTabId: Ref<WorkbenchTab> | undefined
tabIdStore.subscribe((value) => {
prevTabIdStore.set(prevTabId)
prevTabId = value
})
// Use rate limiter to control tab creation, preventing multiple tabs during fast location changing
const limiter = new RateLimiter(1)
workspaceStore.subscribe((workspace) => {
tabIdStore.set(getTabFromLocalStorage(workspace ?? ''))
})
locationStore.subscribe(() => {
void syncTabLoc()
locationStore.subscribe((l: Location) => {
void limiter.add(syncTabLoc)
})
tabIdStore.subscribe(saveTabToLocalStorage)
@ -64,20 +83,51 @@ async function syncTabLoc (): Promise<void> {
return
}
if (loc.path[2] === '' || loc.path[2] == null) return
const url = locationToUrl(loc)
const data = await getTabDataByLocation(loc)
const name = data.name ?? (await translate(data.label, {}, get(languageStore)))
const tabByName = get(tabsStore).find((t) => {
if (t.name !== name) return false
const tabLoc = getTabLocation(t)
return tabLoc.path[2] === loc.path[2] && tabLoc.path[3] === loc.path[3]
})
if (tab.name !== undefined && name !== tab.name && tab.isPinned) {
if (tabByName !== undefined) {
selectTab(tabByName._id)
return
}
const me = getCurrentAccount()
const _id = await getClient().createDoc(workbench.class.WorkbenchTab, core.space.Workspace, {
location: locationToUrl(loc),
const tab: WorkbenchTab = {
_id: generateId(),
_class: workbench.class.WorkbenchTab,
space: core.space.Workspace,
location: url,
name,
attachedTo: me._id,
isPinned: false
})
selectTab(_id)
isPinned: false,
modifiedOn: Date.now(),
modifiedBy: me._id
}
await getClient().createDoc(workbench.class.WorkbenchTab, core.space.Workspace, tab, tab._id)
tabsStore.update((tabs) => [...tabs, tab])
selectTab(tab._id)
} else {
await getClient().update(tab, { location: locationToUrl(loc), name })
// TODO: Fix this
// if (
// tabByName !== undefined &&
// tabByName._id !== tab._id &&
// (loc.path[2] !== tabLoc.path[2] || loc.path[3] !== tabLoc.path[3])
// ) {
// selectTab(tabByName._id)
// return
// }
await getClient().update(tab, { location: url, name })
}
}
export function syncWorkbenchTab (): void {
@ -144,11 +194,24 @@ export async function createTab (): Promise<void> {
const loc = getCurrentLocation()
const client = getClient()
const me = getCurrentAccount()
const defaultUrl = `${workbenchId}/${loc.path[1]}/${notificationId}`
let defaultUrl = `${workbenchId}/${loc.path[1]}/${notificationId}`
try {
const last = localStorage.getItem(`${locationStorageKeyId}_${notificationId}`)
const lastLocation: Location | undefined = last != null ? JSON.parse(last) : undefined
if (lastLocation != null && lastLocation.path[2] === notificationId) {
defaultUrl = locationToUrl(lastLocation)
}
} catch (e) {
// ignore
}
const name = await translate(notification.string.Inbox, {}, get(languageStore))
const tab = await client.createDoc(workbench.class.WorkbenchTab, core.space.Workspace, {
attachedTo: me._id,
location: defaultUrl,
isPinned: false
isPinned: false,
name
})
selectTab(tab)

View File

@ -28,7 +28,9 @@ export class ChannelPage extends CommonPage {
.locator('xpath=following-sibling::div[1]')
.locator('button', { hasText: channel })
readonly chooseChannel = (channel: string): Locator => this.page.getByRole('button', { name: channel })
readonly chooseChannel = (channel: string): Locator =>
this.page.locator('div.antiPanel-navigator').getByRole('button', { name: channel })
readonly closePopupWindow = (): Locator => this.page.locator('.notifyPopup button[data-id="btnNotifyClose"]')
readonly openAddMemberToChannel = (userName: string): Locator => this.page.getByRole('button', { name: userName })
readonly addMemberToChannelTableButton = (userName: string): Locator =>