mirror of
https://github.com/hcengineering/platform.git
synced 2025-01-03 00:43:59 +03:00
UBERF-6676: Chat local state (#5461)
Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
This commit is contained in:
parent
608ea96cc8
commit
b282edfc65
@ -28,10 +28,12 @@
|
|||||||
import { NavigatorModel, SpecialNavModel } from '@hcengineering/workbench'
|
import { NavigatorModel, SpecialNavModel } from '@hcengineering/workbench'
|
||||||
import { InboxNotificationsClientImpl } from '@hcengineering/notification-resources'
|
import { InboxNotificationsClientImpl } from '@hcengineering/notification-resources'
|
||||||
import { onMount } from 'svelte'
|
import { onMount } from 'svelte'
|
||||||
|
import { chunterId } from '@hcengineering/chunter'
|
||||||
|
import { ActivityMessage } from '@hcengineering/activity'
|
||||||
|
|
||||||
import ChatNavigator from './navigator/ChatNavigator.svelte'
|
import ChatNavigator from './navigator/ChatNavigator.svelte'
|
||||||
import ChannelView from '../ChannelView.svelte'
|
import ChannelView from '../ChannelView.svelte'
|
||||||
import { chatSpecials, loadSavedAttachments } from './utils'
|
import { chatSpecials, loadSavedAttachments, storeChannel, openedChannelStore, clearChannel } from './utils'
|
||||||
import { SelectChannelEvent } from './types'
|
import { SelectChannelEvent } from './types'
|
||||||
import { decodeChannelURI, openChannel } from '../../navigation'
|
import { decodeChannelURI, openChannel } from '../../navigation'
|
||||||
|
|
||||||
@ -58,6 +60,13 @@
|
|||||||
syncLocation(loc)
|
syncLocation(loc)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
openedChannelStore.subscribe((data) => {
|
||||||
|
if (data && selectedData?._id !== data._id) {
|
||||||
|
selectedData = data
|
||||||
|
openChannel(data._id, data._class, data.thread)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
$: void loadObject(selectedData?._id, selectedData?._class)
|
$: void loadObject(selectedData?._id, selectedData?._class)
|
||||||
|
|
||||||
async function loadObject (_id?: Ref<Doc>, _class?: Ref<Class<Doc>>): Promise<void> {
|
async function loadObject (_id?: Ref<Doc>, _class?: Ref<Class<Doc>>): Promise<void> {
|
||||||
@ -77,17 +86,25 @@
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function syncLocation (loc: Location) {
|
function syncLocation (loc: Location): void {
|
||||||
const specialId = loc.path[3]
|
if (loc.path[2] !== chunterId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
currentSpecial = navigatorModel?.specials?.find((special) => special.id === specialId)
|
const id = loc.path[3]
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
currentSpecial = navigatorModel?.specials?.find((special) => special.id === id)
|
||||||
|
|
||||||
if (currentSpecial !== undefined) {
|
if (currentSpecial !== undefined) {
|
||||||
selectedData = undefined
|
clearChannel()
|
||||||
} else {
|
} else {
|
||||||
const [_id, _class] = decodeChannelURI(loc.path[3])
|
const [_id, _class] = decodeChannelURI(loc.path[3])
|
||||||
|
|
||||||
selectedData = { _id, _class }
|
storeChannel(_id, _class, loc.path[4] as Ref<ActivityMessage>)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,6 +188,7 @@
|
|||||||
|
|
||||||
{#each sections as section (section.id)}
|
{#each sections as section (section.id)}
|
||||||
<ChatNavSection
|
<ChatNavSection
|
||||||
|
id={section.id}
|
||||||
objects={section.objects}
|
objects={section.objects}
|
||||||
{contexts}
|
{contexts}
|
||||||
{objectId}
|
{objectId}
|
||||||
|
@ -27,7 +27,9 @@
|
|||||||
import { ChatNavItemModel } from '../types'
|
import { ChatNavItemModel } from '../types'
|
||||||
import { getObjectIcon, getChannelName } from '../../../utils'
|
import { getObjectIcon, getChannelName } from '../../../utils'
|
||||||
import ChatSectionHeader from './ChatSectionHeader.svelte'
|
import ChatSectionHeader from './ChatSectionHeader.svelte'
|
||||||
|
import { navigatorStateStore, toggleSections } from '../utils'
|
||||||
|
|
||||||
|
export let id: string
|
||||||
export let header: string
|
export let header: string
|
||||||
export let objects: Doc[]
|
export let objects: Doc[]
|
||||||
export let contexts: DocNotifyContext[]
|
export let contexts: DocNotifyContext[]
|
||||||
@ -46,6 +48,8 @@
|
|||||||
let canShowMore = false
|
let canShowMore = false
|
||||||
let isShownMore = false
|
let isShownMore = false
|
||||||
|
|
||||||
|
$: isCollapsed = $navigatorStateStore.collapsedSections.includes(id)
|
||||||
|
|
||||||
$: void getChatNavItems(objects).then((res) => {
|
$: void getChatNavItems(objects).then((res) => {
|
||||||
items = sortFn(res, contexts)
|
items = sortFn(res, contexts)
|
||||||
})
|
})
|
||||||
@ -136,7 +140,7 @@
|
|||||||
{actions}
|
{actions}
|
||||||
{isCollapsed}
|
{isCollapsed}
|
||||||
on:collapse={() => {
|
on:collapse={() => {
|
||||||
isCollapsed = !isCollapsed
|
toggleSections(id)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{#if !isCollapsed}
|
{#if !isCollapsed}
|
||||||
@ -155,6 +159,12 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
{:else if objectId}
|
||||||
|
{@const item = items.find(({ id }) => id === objectId)}
|
||||||
|
{#if item}
|
||||||
|
{@const context = contexts.find(({ attachedTo }) => attachedTo === item.id)}
|
||||||
|
<ChatNavItem {context} isSelected {item} on:select />
|
||||||
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -13,13 +13,13 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
import notification, { type DocNotifyContext } from '@hcengineering/notification'
|
import notification, { type DocNotifyContext } from '@hcengineering/notification'
|
||||||
import { generateId, SortingOrder, type WithLookup } from '@hcengineering/core'
|
import { type Class, type Doc, generateId, type Ref, SortingOrder, type WithLookup } from '@hcengineering/core'
|
||||||
import { createQuery, getClient, MessageBox } from '@hcengineering/presentation'
|
import { createQuery, getClient, MessageBox } from '@hcengineering/presentation'
|
||||||
import { get, writable } from 'svelte/store'
|
import { get, writable } from 'svelte/store'
|
||||||
import view from '@hcengineering/view'
|
import view from '@hcengineering/view'
|
||||||
import { type SpecialNavModel } from '@hcengineering/workbench'
|
import { type SpecialNavModel } from '@hcengineering/workbench'
|
||||||
import attachment, { type SavedAttachments } from '@hcengineering/attachment'
|
import attachment, { type SavedAttachments } from '@hcengineering/attachment'
|
||||||
import activity from '@hcengineering/activity'
|
import activity, { type ActivityMessage } from '@hcengineering/activity'
|
||||||
import { InboxNotificationsClientImpl } from '@hcengineering/notification-resources'
|
import { InboxNotificationsClientImpl } from '@hcengineering/notification-resources'
|
||||||
import { type Action, showPopup } from '@hcengineering/ui'
|
import { type Action, showPopup } from '@hcengineering/ui'
|
||||||
import contact from '@hcengineering/contact'
|
import contact from '@hcengineering/contact'
|
||||||
@ -27,7 +27,69 @@ import contact from '@hcengineering/contact'
|
|||||||
import { type ChatNavGroupModel, type ChatNavItemModel } from './types'
|
import { type ChatNavGroupModel, type ChatNavItemModel } from './types'
|
||||||
import chunter from '../../plugin'
|
import chunter from '../../plugin'
|
||||||
|
|
||||||
|
const channelStorageKey = 'chunter.openedChannel'
|
||||||
|
const navigatorStateStorageKey = 'chunter.navigatorState'
|
||||||
|
|
||||||
|
interface ChannelMetadata {
|
||||||
|
_id: Ref<Doc>
|
||||||
|
_class: Ref<Class<Doc>>
|
||||||
|
thread?: Ref<ActivityMessage>
|
||||||
|
}
|
||||||
|
interface NavigatorState {
|
||||||
|
collapsedSections: string[]
|
||||||
|
}
|
||||||
|
|
||||||
export const savedAttachmentsStore = writable<Array<WithLookup<SavedAttachments>>>([])
|
export const savedAttachmentsStore = writable<Array<WithLookup<SavedAttachments>>>([])
|
||||||
|
export const openedChannelStore = writable<ChannelMetadata | undefined>(restoreChannel())
|
||||||
|
export const navigatorStateStore = writable<NavigatorState>(restoreNavigatorState())
|
||||||
|
|
||||||
|
function restoreChannel (): ChannelMetadata | undefined {
|
||||||
|
const raw = localStorage.getItem(channelStorageKey)
|
||||||
|
|
||||||
|
if (raw == null) return undefined
|
||||||
|
|
||||||
|
try {
|
||||||
|
return JSON.parse(raw) as ChannelMetadata
|
||||||
|
} catch (e) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function restoreNavigatorState (): NavigatorState {
|
||||||
|
const raw = localStorage.getItem(navigatorStateStorageKey)
|
||||||
|
|
||||||
|
if (raw == null) return { collapsedSections: [] }
|
||||||
|
|
||||||
|
try {
|
||||||
|
return JSON.parse(raw) as NavigatorState
|
||||||
|
} catch (e) {
|
||||||
|
return { collapsedSections: [] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toggleSections (_id: string): void {
|
||||||
|
const navState = get(navigatorStateStore)
|
||||||
|
const result: NavigatorState = navState.collapsedSections.includes(_id)
|
||||||
|
? {
|
||||||
|
collapsedSections: navState.collapsedSections.filter((id) => id !== _id)
|
||||||
|
}
|
||||||
|
: { collapsedSections: [...navState.collapsedSections, _id] }
|
||||||
|
|
||||||
|
localStorage.setItem(navigatorStateStorageKey, JSON.stringify(result))
|
||||||
|
navigatorStateStore.set(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clearChannel (): void {
|
||||||
|
localStorage.removeItem(channelStorageKey)
|
||||||
|
openedChannelStore.set(undefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function storeChannel (_id: Ref<Doc>, _class: Ref<Class<Doc>>, thread?: Ref<ActivityMessage>): void {
|
||||||
|
const data: ChannelMetadata = { _id, _class, thread }
|
||||||
|
|
||||||
|
localStorage.setItem(channelStorageKey, JSON.stringify(data))
|
||||||
|
openedChannelStore.set(data)
|
||||||
|
}
|
||||||
|
|
||||||
export const chatSpecials: SpecialNavModel[] = [
|
export const chatSpecials: SpecialNavModel[] = [
|
||||||
{
|
{
|
||||||
|
@ -16,7 +16,7 @@ function encodeChannelURI (_id: Ref<Doc>, _class: Ref<Class<Doc>>): string {
|
|||||||
return [_id, _class].join('|')
|
return [_id, _class].join('|')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function openChannel (_id: Ref<Doc>, _class: Ref<Class<Doc>>): void {
|
export function openChannel (_id: Ref<Doc>, _class: Ref<Class<Doc>>, thread?: Ref<ActivityMessage>): void {
|
||||||
const loc = getCurrentLocation()
|
const loc = getCurrentLocation()
|
||||||
|
|
||||||
const id = encodeChannelURI(_id, _class)
|
const id = encodeChannelURI(_id, _class)
|
||||||
@ -26,9 +26,15 @@ export function openChannel (_id: Ref<Doc>, _class: Ref<Class<Doc>>): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loc.path[3] = id
|
loc.path[3] = id
|
||||||
loc.path[4] = ''
|
|
||||||
loc.query = { ...loc.query, message: null }
|
loc.query = { ...loc.query, message: null }
|
||||||
loc.path.length = 4
|
|
||||||
|
if (thread !== undefined) {
|
||||||
|
loc.path[4] = thread
|
||||||
|
loc.path.length = 5
|
||||||
|
} else {
|
||||||
|
loc.path[4] = ''
|
||||||
|
loc.path.length = 4
|
||||||
|
}
|
||||||
|
|
||||||
navigate(loc)
|
navigate(loc)
|
||||||
}
|
}
|
||||||
|
@ -74,6 +74,7 @@ export async function buildDmName (client: Client, employeeAccounts: PersonAccou
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const me = getCurrentAccount() as PersonAccount
|
||||||
const map = await promise
|
const map = await promise
|
||||||
|
|
||||||
unsub?.()
|
unsub?.()
|
||||||
@ -88,7 +89,7 @@ export async function buildDmName (client: Client, employeeAccounts: PersonAccou
|
|||||||
|
|
||||||
const employee = map.get(acc.person as unknown as Ref<Employee>)
|
const employee = map.get(acc.person as unknown as Ref<Employee>)
|
||||||
|
|
||||||
if (employee !== undefined) {
|
if (employee !== undefined && me.person !== employee._id) {
|
||||||
names.push(getName(client.getHierarchy(), employee))
|
names.push(getName(client.getHierarchy(), employee))
|
||||||
processedPersons.push(acc.person)
|
processedPersons.push(acc.person)
|
||||||
}
|
}
|
||||||
@ -156,13 +157,14 @@ async function getDmAccounts (client: Client, space?: Space): Promise<PersonAcco
|
|||||||
|
|
||||||
export async function getDmPersons (client: Client, space: Space): Promise<Person[]> {
|
export async function getDmPersons (client: Client, space: Space): Promise<Person[]> {
|
||||||
const personAccounts: PersonAccount[] = await getDmAccounts(client, space)
|
const personAccounts: PersonAccount[] = await getDmAccounts(client, space)
|
||||||
|
const me = getCurrentAccount() as PersonAccount
|
||||||
const persons: Person[] = []
|
const persons: Person[] = []
|
||||||
|
|
||||||
const personRefs = new Set(personAccounts.map(({ person }) => person))
|
const personRefs = new Set(personAccounts.map(({ person }) => person))
|
||||||
|
|
||||||
for (const personRef of personRefs) {
|
for (const personRef of personRefs) {
|
||||||
const person = await client.findOne(contact.class.Person, { _id: personRef })
|
const person = await client.findOne(contact.class.Person, { _id: personRef })
|
||||||
if (person !== undefined) {
|
if (person !== undefined && me.person !== person._id) {
|
||||||
persons.push(person)
|
persons.push(person)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user