Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UBERF-6676: Chat local state #5461

Merged
merged 2 commits into from
Apr 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 23 additions & 6 deletions plugins/chunter-resources/src/components/chat/Chat.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@
import { NavigatorModel, SpecialNavModel } from '@hcengineering/workbench'
import { InboxNotificationsClientImpl } from '@hcengineering/notification-resources'
import { onMount } from 'svelte'
import { chunterId } from '@hcengineering/chunter'
import { ActivityMessage } from '@hcengineering/activity'

import ChatNavigator from './navigator/ChatNavigator.svelte'
import ChannelView from '../ChannelView.svelte'
import { chatSpecials, loadSavedAttachments } from './utils'
import { chatSpecials, loadSavedAttachments, storeChannel, openedChannelStore, clearChannel } from './utils'
import { SelectChannelEvent } from './types'
import { decodeChannelURI, openChannel } from '../../navigation'

Expand All @@ -58,6 +60,13 @@
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)

async function loadObject (_id?: Ref<Doc>, _class?: Ref<Class<Doc>>): Promise<void> {
Expand All @@ -77,17 +86,25 @@
)
}

function syncLocation (loc: Location) {
const specialId = loc.path[3]
function syncLocation (loc: Location): void {
if (loc.path[2] !== chunterId) {
return
}

const id = loc.path[3]

if (!id) {
return
}

currentSpecial = navigatorModel?.specials?.find((special) => special.id === specialId)
currentSpecial = navigatorModel?.specials?.find((special) => special.id === id)

if (currentSpecial !== undefined) {
selectedData = undefined
clearChannel()
} else {
const [_id, _class] = decodeChannelURI(loc.path[3])

selectedData = { _id, _class }
storeChannel(_id, _class, loc.path[4] as Ref<ActivityMessage>)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@

{#each sections as section (section.id)}
<ChatNavSection
id={section.id}
objects={section.objects}
{contexts}
{objectId}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@
import { ChatNavItemModel } from '../types'
import { getObjectIcon, getChannelName } from '../../../utils'
import ChatSectionHeader from './ChatSectionHeader.svelte'
import { navigatorStateStore, toggleSections } from '../utils'

export let id: string
export let header: string
export let objects: Doc[]
export let contexts: DocNotifyContext[]
Expand All @@ -46,6 +48,8 @@
let canShowMore = false
let isShownMore = false

$: isCollapsed = $navigatorStateStore.collapsedSections.includes(id)

$: void getChatNavItems(objects).then((res) => {
items = sortFn(res, contexts)
})
Expand Down Expand Up @@ -136,7 +140,7 @@
{actions}
{isCollapsed}
on:collapse={() => {
isCollapsed = !isCollapsed
toggleSections(id)
}}
/>
{#if !isCollapsed}
Expand All @@ -155,6 +159,12 @@
/>
</div>
{/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}
</div>
{/if}
Expand Down
66 changes: 64 additions & 2 deletions plugins/chunter-resources/src/components/chat/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,83 @@
// limitations under the License.
//
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 { get, writable } from 'svelte/store'
import view from '@hcengineering/view'
import { type SpecialNavModel } from '@hcengineering/workbench'
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 { type Action, showPopup } from '@hcengineering/ui'
import contact from '@hcengineering/contact'

import { type ChatNavGroupModel, type ChatNavItemModel } from './types'
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 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[] = [
{
Expand Down
12 changes: 9 additions & 3 deletions plugins/chunter-resources/src/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ function encodeChannelURI (_id: Ref<Doc>, _class: Ref<Class<Doc>>): string {
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 id = encodeChannelURI(_id, _class)
Expand All @@ -26,9 +26,15 @@ export function openChannel (_id: Ref<Doc>, _class: Ref<Class<Doc>>): void {
}

loc.path[3] = id
loc.path[4] = ''
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)
}
Expand Down
6 changes: 4 additions & 2 deletions plugins/chunter-resources/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export async function buildDmName (client: Client, employeeAccounts: PersonAccou
})
})

const me = getCurrentAccount() as PersonAccount
const map = await promise

unsub?.()
Expand All @@ -88,7 +89,7 @@ export async function buildDmName (client: Client, employeeAccounts: PersonAccou

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))
processedPersons.push(acc.person)
}
Expand Down Expand Up @@ -156,13 +157,14 @@ async function getDmAccounts (client: Client, space?: Space): Promise<PersonAcco

export async function getDmPersons (client: Client, space: Space): Promise<Person[]> {
const personAccounts: PersonAccount[] = await getDmAccounts(client, space)
const me = getCurrentAccount() as PersonAccount
const persons: Person[] = []

const personRefs = new Set(personAccounts.map(({ person }) => person))

for (const personRef of personRefs) {
const person = await client.findOne(contact.class.Person, { _id: personRef })
if (person !== undefined) {
if (person !== undefined && me.person !== person._id) {
persons.push(person)
}
}
Expand Down