mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-11 10:47:24 +00:00
Merge remote-tracking branch 'origin/develop' into staging
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
commit
b0fb5469af
@ -352,7 +352,9 @@ async function processMigrateJsonForDoc (
|
|||||||
|
|
||||||
if (value.startsWith('{')) {
|
if (value.startsWith('{')) {
|
||||||
// For some reason we have documents that are already markups
|
// For some reason we have documents that are already markups
|
||||||
const jsonId = await saveCollabJson(ctx, storageAdapter, workspaceId, collabId, value)
|
const jsonId = await retry(5, async () => {
|
||||||
|
return await saveCollabJson(ctx, storageAdapter, workspaceId, collabId, value)
|
||||||
|
})
|
||||||
update[attributeName] = jsonId
|
update[attributeName] = jsonId
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -371,23 +373,28 @@ async function processMigrateJsonForDoc (
|
|||||||
// If document id has changed, save it with new name to ensure we will be able to load it later
|
// If document id has changed, save it with new name to ensure we will be able to load it later
|
||||||
const ydocId = makeCollabYdocId(collabId)
|
const ydocId = makeCollabYdocId(collabId)
|
||||||
if (ydocId !== currentYdocId) {
|
if (ydocId !== currentYdocId) {
|
||||||
ctx.info('saving collaborative doc with new name', { collabId, ydocId, currentYdocId })
|
await retry(5, async () => {
|
||||||
const buffer = await storageAdapter.read(ctx, workspaceId, currentYdocId)
|
const stat = await storageAdapter.stat(ctx, workspaceId, currentYdocId)
|
||||||
await storageAdapter.put(
|
if (stat !== undefined) {
|
||||||
ctx,
|
const buffer = await storageAdapter.read(ctx, workspaceId, currentYdocId)
|
||||||
workspaceId,
|
await storageAdapter.put(
|
||||||
ydocId,
|
ctx,
|
||||||
Buffer.concat(buffer as any),
|
workspaceId,
|
||||||
'application/ydoc',
|
ydocId,
|
||||||
buffer.length
|
Buffer.concat(buffer as any),
|
||||||
)
|
'application/ydoc',
|
||||||
|
buffer.length
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
} catch (err) {
|
||||||
const unset = update.$unset ?? {}
|
const error = err instanceof Error ? err.message : String(err)
|
||||||
update.$unset = { ...unset, [attribute.name]: 1 }
|
ctx.warn('failed to process collaborative doc', { workspaceId, collabId, currentYdocId, error })
|
||||||
} catch (err: any) {
|
|
||||||
ctx.warn('failed to process collaborative doc', { workspaceId, collabId, currentYdocId, err: err.message })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const unset = update.$unset ?? {}
|
||||||
|
update.$unset = { ...unset, [attribute.name]: 1 }
|
||||||
}
|
}
|
||||||
|
|
||||||
return update
|
return update
|
||||||
@ -510,3 +517,19 @@ export const coreOperation: MigrateOperation = {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function retry<T> (retries: number, op: () => Promise<T>): Promise<T> {
|
||||||
|
let error: any
|
||||||
|
while (retries > 0) {
|
||||||
|
retries--
|
||||||
|
try {
|
||||||
|
return await op()
|
||||||
|
} catch (err: any) {
|
||||||
|
error = err
|
||||||
|
if (retries !== 0) {
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 50))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
@ -275,8 +275,7 @@ export function createModel (builder: Builder): void {
|
|||||||
label: love.string.Office,
|
label: love.string.Office,
|
||||||
type: WidgetType.Fixed,
|
type: WidgetType.Fixed,
|
||||||
icon: love.icon.Love,
|
icon: love.icon.Love,
|
||||||
component: love.component.LoveWidget,
|
component: love.component.LoveWidget
|
||||||
headerLabel: love.string.Office
|
|
||||||
},
|
},
|
||||||
love.ids.LoveWidget
|
love.ids.LoveWidget
|
||||||
)
|
)
|
||||||
|
@ -86,14 +86,7 @@
|
|||||||
let pressed: boolean = false
|
let pressed: boolean = false
|
||||||
const clickMore = (e: MouseEvent): void => {
|
const clickMore = (e: MouseEvent): void => {
|
||||||
pressed = true
|
pressed = true
|
||||||
const value: SelectPopupValueType[] = [
|
const value: SelectPopupValueType[] = [{ id: 'rename', icon: IconEdit, label: plugin.string.RenameAFloor }]
|
||||||
{
|
|
||||||
id: 'configure',
|
|
||||||
icon: IconSettings,
|
|
||||||
label: configure ? plugin.string.FinalizeEditing : plugin.string.EditOffice
|
|
||||||
},
|
|
||||||
{ id: 'rename', icon: IconEdit, label: plugin.string.RenameAFloor }
|
|
||||||
]
|
|
||||||
showPopup(SelectPopup, { value }, eventToHTMLElement(e), (result) => {
|
showPopup(SelectPopup, { value }, eventToHTMLElement(e), (result) => {
|
||||||
if (result === 'configure') {
|
if (result === 'configure') {
|
||||||
dispatch('configure', floor)
|
dispatch('configure', floor)
|
||||||
|
@ -1,105 +0,0 @@
|
|||||||
<!--
|
|
||||||
// Copyright © 2024 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 { AccountRole, Ref, getCurrentAccount, hasAccountRole } from '@hcengineering/core'
|
|
||||||
import {
|
|
||||||
ButtonIcon,
|
|
||||||
IconAdd,
|
|
||||||
Label,
|
|
||||||
Scroller,
|
|
||||||
Separator,
|
|
||||||
defineSeparators,
|
|
||||||
eventToHTMLElement,
|
|
||||||
showPopup,
|
|
||||||
deviceOptionsStore as deviceInfo
|
|
||||||
} from '@hcengineering/ui'
|
|
||||||
import { Floor as FloorType, Room } from '@hcengineering/love'
|
|
||||||
import love from '../plugin'
|
|
||||||
import { floors, rooms } from '../stores'
|
|
||||||
import { loveSeparators } from '../types'
|
|
||||||
import EditFloorPopup from './EditFloorPopup.svelte'
|
|
||||||
import FloorPreview from './FloorPreview.svelte'
|
|
||||||
|
|
||||||
export let floor: Ref<FloorType>
|
|
||||||
export let configure: boolean
|
|
||||||
|
|
||||||
const me = getCurrentAccount()
|
|
||||||
|
|
||||||
function getRooms (rooms: Room[], floor: Ref<FloorType>): Room[] {
|
|
||||||
return rooms.filter((p) => p.floor === floor)
|
|
||||||
}
|
|
||||||
|
|
||||||
function addFloor (e: MouseEvent): void {
|
|
||||||
showPopup(EditFloorPopup, {}, eventToHTMLElement(e))
|
|
||||||
}
|
|
||||||
|
|
||||||
let editable: boolean = false
|
|
||||||
$: editable = hasAccountRole(me, AccountRole.Maintainer)
|
|
||||||
|
|
||||||
defineSeparators('love', loveSeparators)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{#if $deviceInfo.navigator.visible}
|
|
||||||
<div
|
|
||||||
class="antiPanel-navigator {$deviceInfo.navigator.direction === 'horizontal'
|
|
||||||
? 'portrait'
|
|
||||||
: 'landscape'} border-left will-change-opacity"
|
|
||||||
class:fly={$deviceInfo.navigator.float}
|
|
||||||
>
|
|
||||||
<div class="antiPanel-wrap__content hulyNavPanel-container">
|
|
||||||
<div class="hulyNavPanel-header" class:withButton={editable}>
|
|
||||||
<span class="overflow-label">
|
|
||||||
<Label label={love.string.Floors} />
|
|
||||||
</span>
|
|
||||||
{#if editable}
|
|
||||||
<ButtonIcon icon={IconAdd} kind={'primary'} size={'small'} on:click={addFloor} />
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
<Scroller fade={{ multipler: { top: 4, bottom: 0 } }} shrink>
|
|
||||||
{#each $floors as _floor}
|
|
||||||
<FloorPreview
|
|
||||||
showRoomName
|
|
||||||
floor={_floor}
|
|
||||||
configurable
|
|
||||||
configure={configure && floor === _floor._id}
|
|
||||||
rooms={getRooms($rooms, _floor._id)}
|
|
||||||
selected={floor === _floor._id}
|
|
||||||
background={'var(--theme-navpanel-color)'}
|
|
||||||
on:configure={() => {
|
|
||||||
if (floor === _floor._id) {
|
|
||||||
configure = !configure
|
|
||||||
} else {
|
|
||||||
floor = _floor._id
|
|
||||||
configure = true
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
on:select={() => {
|
|
||||||
floor = _floor._id
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{/each}
|
|
||||||
</Scroller>
|
|
||||||
</div>
|
|
||||||
<Separator name={'love'} float={$deviceInfo.navigator.float ? 'navigator' : true} index={0} color={'transparent'} />
|
|
||||||
</div>
|
|
||||||
<Separator
|
|
||||||
name={'love'}
|
|
||||||
float={$deviceInfo.navigator.float}
|
|
||||||
index={0}
|
|
||||||
color={'transparent'}
|
|
||||||
separatorSize={0}
|
|
||||||
short
|
|
||||||
/>
|
|
||||||
{/if}
|
|
@ -20,17 +20,26 @@
|
|||||||
import { getClient } from '@hcengineering/presentation'
|
import { getClient } from '@hcengineering/presentation'
|
||||||
import { deviceOptionsStore as deviceInfo, getCurrentLocation, navigate } from '@hcengineering/ui'
|
import { deviceOptionsStore as deviceInfo, getCurrentLocation, navigate } from '@hcengineering/ui'
|
||||||
import { onDestroy, onMount } from 'svelte'
|
import { onDestroy, onMount } from 'svelte'
|
||||||
import { activeFloor, floors, infos, invites, myInfo, myRequests, rooms, storePromise } from '../stores'
|
import {
|
||||||
|
activeFloor,
|
||||||
|
floors,
|
||||||
|
infos,
|
||||||
|
invites,
|
||||||
|
myInfo,
|
||||||
|
myRequests,
|
||||||
|
rooms,
|
||||||
|
selectedFloor,
|
||||||
|
storePromise
|
||||||
|
} from '../stores'
|
||||||
import { connectToMeeting, tryConnect } from '../utils'
|
import { connectToMeeting, tryConnect } from '../utils'
|
||||||
import Floor from './Floor.svelte'
|
import Floor from './Floor.svelte'
|
||||||
import FloorConfigure from './FloorConfigure.svelte'
|
import FloorConfigure from './FloorConfigure.svelte'
|
||||||
import Floors from './Floors.svelte'
|
|
||||||
|
|
||||||
function getRooms (rooms: Room[], floor: Ref<FloorType>): Room[] {
|
function getRooms (rooms: Room[], floor: Ref<FloorType>): Room[] {
|
||||||
return rooms.filter((p) => p.floor === floor)
|
return rooms.filter((p) => p.floor === floor)
|
||||||
}
|
}
|
||||||
|
|
||||||
let selectedFloor = $activeFloor === '' ? $floors[0]?._id : $activeFloor
|
$: floor = $selectedFloor ?? ($activeFloor === '' ? $floors[0]?._id : $activeFloor)
|
||||||
let configure: boolean = false
|
let configure: boolean = false
|
||||||
let replacedPanel: HTMLElement
|
let replacedPanel: HTMLElement
|
||||||
|
|
||||||
@ -73,21 +82,15 @@
|
|||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Floors bind:floor={selectedFloor} bind:configure />
|
|
||||||
<div class="antiPanel-component filledNav" bind:this={replacedPanel}>
|
<div class="antiPanel-component filledNav" bind:this={replacedPanel}>
|
||||||
{#if configure}
|
{#if configure}
|
||||||
<FloorConfigure
|
<FloorConfigure
|
||||||
rooms={getRooms($rooms, selectedFloor)}
|
rooms={getRooms($rooms, floor)}
|
||||||
floor={selectedFloor}
|
{floor}
|
||||||
{excludedPersons}
|
{excludedPersons}
|
||||||
on:configure={() => (configure = false)}
|
on:configure={() => (configure = false)}
|
||||||
/>
|
/>
|
||||||
{:else}
|
{:else}
|
||||||
<Floor
|
<Floor rooms={getRooms($rooms, floor)} {floor} on:configure={() => (configure = true)} on:open />
|
||||||
rooms={getRooms($rooms, selectedFloor)}
|
|
||||||
floor={selectedFloor}
|
|
||||||
on:configure={() => (configure = true)}
|
|
||||||
on:open
|
|
||||||
/>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
@ -13,74 +13,78 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Floor, Room } from '@hcengineering/love'
|
import { AccountRole, getCurrentAccount, hasAccountRole, Ref } from '@hcengineering/core'
|
||||||
import { Ref } from '@hcengineering/core'
|
import love, { Floor, Room } from '@hcengineering/love'
|
||||||
import ui, { IconChevronLeft, ModernButton, Scroller } from '@hcengineering/ui'
|
import { Breadcrumbs, ButtonIcon, eventToHTMLElement, Header, IconAdd, Scroller, showPopup } from '@hcengineering/ui'
|
||||||
|
import { floors, rooms, selectedFloor } from '../stores'
|
||||||
import FloorPreview from './FloorPreview.svelte'
|
import FloorPreview from './FloorPreview.svelte'
|
||||||
import { floors, rooms } from '../stores'
|
import EditFloorPopup from './EditFloorPopup.svelte'
|
||||||
import IconLayers from './icons/Layers.svelte'
|
|
||||||
import love from '../plugin'
|
|
||||||
|
|
||||||
let selectedFloor: Floor | undefined
|
let configure: boolean
|
||||||
let floorsSelector: boolean = false
|
|
||||||
|
|
||||||
$: if (selectedFloor === undefined && $floors.length > 0) {
|
const me = getCurrentAccount()
|
||||||
selectedFloor = $floors[0]
|
let floor: Floor | undefined
|
||||||
|
|
||||||
|
$: if (floor === undefined && $floors.length > 0) {
|
||||||
|
floor = $floors[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRooms (rooms: Room[], floor: Ref<Floor>): Room[] {
|
function getRooms (rooms: Room[], floor: Ref<Floor>): Room[] {
|
||||||
return rooms.filter((p) => p.floor === floor)
|
return rooms.filter((p) => p.floor === floor)
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeMode (): void {
|
function addFloor (e: MouseEvent): void {
|
||||||
floorsSelector = !floorsSelector
|
showPopup(EditFloorPopup, {}, eventToHTMLElement(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectFloor (_id: Ref<Floor>): void {
|
let editable: boolean = false
|
||||||
selectedFloor = $floors.find((p) => p._id === _id)
|
$: editable = hasAccountRole(me, AccountRole.Maintainer)
|
||||||
floorsSelector = false
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<Header
|
||||||
|
allowFullsize={false}
|
||||||
|
type="type-aside"
|
||||||
|
hideBefore={true}
|
||||||
|
hideActions={false}
|
||||||
|
hideDescription={true}
|
||||||
|
adaptive="disabled"
|
||||||
|
closeOnEscape={false}
|
||||||
|
on:close
|
||||||
|
>
|
||||||
|
<Breadcrumbs items={[{ label: love.string.Office }]} currentOnly />
|
||||||
|
<svelte:fragment slot="extra">
|
||||||
|
{#if editable}
|
||||||
|
<ButtonIcon icon={IconAdd} kind={'primary'} size={'small'} on:click={addFloor} />
|
||||||
|
{/if}
|
||||||
|
</svelte:fragment>
|
||||||
|
</Header>
|
||||||
<div class="hulyModal-container noTopIndent type-aside">
|
<div class="hulyModal-container noTopIndent type-aside">
|
||||||
<div class="hulyModal-content">
|
<div class="hulyModal-content">
|
||||||
<Scroller>
|
<Scroller>
|
||||||
{#if floorsSelector}
|
{#each $floors as _floor}
|
||||||
{#each $floors as _floor}
|
|
||||||
<FloorPreview
|
|
||||||
showRoomName
|
|
||||||
floor={_floor}
|
|
||||||
rooms={getRooms($rooms, _floor._id)}
|
|
||||||
selected={selectedFloor?._id === _floor._id}
|
|
||||||
kind={'no-border'}
|
|
||||||
background={'var(--theme-panel-color)'}
|
|
||||||
on:select={() => {
|
|
||||||
selectFloor(_floor._id)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{/each}
|
|
||||||
{:else if selectedFloor}
|
|
||||||
<FloorPreview
|
<FloorPreview
|
||||||
floor={selectedFloor}
|
|
||||||
showRoomName
|
showRoomName
|
||||||
rooms={getRooms($rooms, selectedFloor._id)}
|
floor={_floor}
|
||||||
selected
|
configurable
|
||||||
isOpen
|
configure={configure && floor?._id === _floor._id}
|
||||||
disabled
|
rooms={getRooms($rooms, _floor._id)}
|
||||||
cropped
|
selected={floor?._id === _floor._id}
|
||||||
kind={'no-border'}
|
background={'var(--theme-navpanel-color)'}
|
||||||
background={'var(--theme-panel-color)'}
|
on:configure={() => {
|
||||||
|
if (floor?._id === _floor._id) {
|
||||||
|
configure = !configure
|
||||||
|
} else {
|
||||||
|
selectedFloor.set(_floor?._id)
|
||||||
|
floor = _floor
|
||||||
|
configure = true
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
on:select={() => {
|
||||||
|
selectedFloor.set(_floor?._id)
|
||||||
|
floor = _floor
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/each}
|
||||||
</Scroller>
|
</Scroller>
|
||||||
</div>
|
</div>
|
||||||
{#if floorsSelector || $floors.length > 1}
|
|
||||||
<div class="hulyModal-footer">
|
|
||||||
<ModernButton
|
|
||||||
on:click={changeMode}
|
|
||||||
icon={floorsSelector ? IconChevronLeft : IconLayers}
|
|
||||||
label={floorsSelector ? ui.string.Back : love.string.ChangeFloor}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -34,6 +34,7 @@ export const currentRoom = derived([rooms, myInfo], ([rooms, myInfo]) => {
|
|||||||
return myInfo !== undefined ? rooms.find((p) => p._id === myInfo.room) : undefined
|
return myInfo !== undefined ? rooms.find((p) => p._id === myInfo.room) : undefined
|
||||||
})
|
})
|
||||||
export const floors = writable<Floor[]>([])
|
export const floors = writable<Floor[]>([])
|
||||||
|
export const selectedFloor = writable<Ref<Floor> | undefined>(undefined)
|
||||||
export const activeFloor = derived([rooms, myInfo, myOffice], ([rooms, myInfo, myOffice]) => {
|
export const activeFloor = derived([rooms, myInfo, myOffice], ([rooms, myInfo, myOffice]) => {
|
||||||
let res: Ref<Floor> | undefined
|
let res: Ref<Floor> | undefined
|
||||||
if (myInfo !== undefined) {
|
if (myInfo !== undefined) {
|
||||||
|
Loading…
Reference in New Issue
Block a user