mirror of
https://github.com/hcengineering/platform.git
synced 2024-11-22 03:14:40 +03:00
UBERF-6205: add real archive for notifications (#5385)
Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
This commit is contained in:
parent
55c1e745b7
commit
a29e1701be
@ -243,6 +243,9 @@ export class TInboxNotification extends TDoc implements InboxNotification {
|
||||
// @Index(IndexKind.Indexed)
|
||||
isViewed!: boolean
|
||||
|
||||
@Prop(TypeBoolean(), core.string.Boolean)
|
||||
archived?: boolean
|
||||
|
||||
title?: IntlString
|
||||
body?: IntlString
|
||||
intlParams?: Record<string, string | number>
|
||||
@ -537,15 +540,29 @@ export function createModel (builder: Builder): void {
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: notification.actionImpl.DeleteContextNotifications,
|
||||
label: notification.string.Archive,
|
||||
action: notification.actionImpl.ArchiveContextNotifications,
|
||||
label: view.string.Archive,
|
||||
icon: view.icon.CheckCircle,
|
||||
input: 'focus',
|
||||
category: notification.category.Notification,
|
||||
target: notification.class.DocNotifyContext,
|
||||
context: { mode: ['panel'], application: notification.app.Notification, group: 'remove' }
|
||||
},
|
||||
notification.action.DeleteContextNotifications
|
||||
notification.action.ArchiveContextNotifications
|
||||
)
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: notification.actionImpl.UnarchiveContextNotifications,
|
||||
label: view.string.UnArchive,
|
||||
icon: view.icon.Circle,
|
||||
input: 'focus',
|
||||
category: notification.category.Notification,
|
||||
target: notification.class.DocNotifyContext,
|
||||
context: { mode: ['panel'], application: notification.app.Notification, group: 'remove' }
|
||||
},
|
||||
notification.action.UnarchiveContextNotifications
|
||||
)
|
||||
|
||||
createAction(
|
||||
@ -669,6 +686,7 @@ export function createModel (builder: Builder): void {
|
||||
})
|
||||
builder.createDoc(core.class.DomainIndexConfiguration, core.space.Model, {
|
||||
domain: DOMAIN_NOTIFICATION,
|
||||
indexes: [{ user: 1, archived: 1 }],
|
||||
disabled: [{ modifiedOn: 1 }, { modifiedBy: 1 }, { createdBy: 1 }, { isViewed: 1 }, { hidden: 1 }]
|
||||
})
|
||||
}
|
||||
|
@ -76,7 +76,8 @@ export default mergeIds(notificationId, notification, {
|
||||
PinDocNotifyContext: '' as ViewAction,
|
||||
UnReadNotifyContext: '' as ViewAction,
|
||||
ReadNotifyContext: '' as ViewAction,
|
||||
DeleteContextNotifications: '' as ViewAction,
|
||||
ArchiveContextNotifications: '' as ViewAction,
|
||||
UnarchiveContextNotifications: '' as ViewAction,
|
||||
ArchiveAll: '' as ViewAction,
|
||||
ReadAll: '' as ViewAction,
|
||||
UnreadAll: '' as ViewAction
|
||||
|
@ -31,6 +31,7 @@
|
||||
export let disabled: boolean = false
|
||||
export let loading: boolean = false
|
||||
export let inheritColor: boolean = false
|
||||
export let noSelection: boolean = false
|
||||
|
||||
export let items: DropdownIntlItem[]
|
||||
export let params: Record<string, any> = {}
|
||||
@ -46,7 +47,7 @@
|
||||
function openPopup () {
|
||||
if (!opened) {
|
||||
opened = true
|
||||
showPopup(ModernPopup, { items, selected, params }, element, (result) => {
|
||||
showPopup(ModernPopup, { items, selected: noSelection ? undefined : selected, params }, element, (result) => {
|
||||
if (result) {
|
||||
selected = result
|
||||
dispatch('selected', result)
|
||||
|
@ -3,7 +3,7 @@
|
||||
// © 2023 Hardcore Engineering, Inc. All Rights Reserved.
|
||||
// Licensed under the Eclipse Public License v2.0 (SPDX: EPL-2.0).
|
||||
//
|
||||
import type { Asset, IntlString } from '@hcengineering/platform'
|
||||
import type { IntlString } from '@hcengineering/platform'
|
||||
import Label from './Label.svelte'
|
||||
|
||||
export let title: string | undefined = undefined
|
||||
|
@ -38,7 +38,7 @@
|
||||
"Mentioned": "Mentioned",
|
||||
"You": "You",
|
||||
"Mentions": "Mentions",
|
||||
"MentionedYouIn": "Mentioned you in {title}",
|
||||
"MentionedYouIn": "Mentioned you in",
|
||||
"Messages": "Messages",
|
||||
"Thread": "Thread",
|
||||
"AddReaction": "Add reaction",
|
||||
|
@ -37,6 +37,6 @@
|
||||
"Mentioned": "Mencionado",
|
||||
"You": "Tú",
|
||||
"Mentions": "Menciones",
|
||||
"MentionedYouIn": "Has sido Mencionado en {title}"
|
||||
"MentionedYouIn": "Has sido Mencionado en"
|
||||
}
|
||||
}
|
@ -37,6 +37,6 @@
|
||||
"Mentioned": "Mencionado",
|
||||
"You": "Tu",
|
||||
"Mentions": "Menções",
|
||||
"MentionedYouIn": "Foste Mencionado em {title}"
|
||||
"MentionedYouIn": "Foste Mencionado em"
|
||||
}
|
||||
}
|
@ -38,7 +38,7 @@
|
||||
"Mentioned": "Упомянул(а)",
|
||||
"You": "Вы",
|
||||
"Mentions": "Упоминания",
|
||||
"MentionedYouIn": "Упомянул(а) вас в {title}",
|
||||
"MentionedYouIn": "Упомянул(а) вас в",
|
||||
"Messages": "Cообщения",
|
||||
"Thread": "Обсуждение",
|
||||
"AddReaction": "Добавить реакцию",
|
||||
|
@ -83,7 +83,7 @@
|
||||
})
|
||||
|
||||
const excludedActions = [
|
||||
notification.action.DeleteContextNotifications,
|
||||
notification.action.ArchiveContextNotifications,
|
||||
notification.action.UnReadNotifyContext,
|
||||
notification.action.ReadNotifyContext
|
||||
]
|
||||
|
@ -272,7 +272,7 @@ export async function removeActivityChannels (contexts: DocNotifyContext[]): Pro
|
||||
try {
|
||||
for (const context of contexts) {
|
||||
const notifications = notificationsByContext.get(context._id) ?? []
|
||||
await client.deleteNotifications(
|
||||
await client.archiveNotifications(
|
||||
ops,
|
||||
notifications.map(({ _id }) => _id)
|
||||
)
|
||||
@ -293,7 +293,7 @@ export async function readActivityChannels (contexts: DocNotifyContext[]): Promi
|
||||
try {
|
||||
for (const context of contexts) {
|
||||
const notifications = notificationsByContext.get(context._id) ?? []
|
||||
await client.deleteNotifications(
|
||||
await client.archiveNotifications(
|
||||
ops,
|
||||
notifications
|
||||
.filter(({ _class }) => _class === notification.class.ActivityInboxNotification)
|
||||
|
@ -39,7 +39,7 @@ import activity, {
|
||||
type DocUpdateMessage
|
||||
} from '@hcengineering/activity'
|
||||
import {
|
||||
deleteContextNotifications,
|
||||
archiveContextNotifications,
|
||||
InboxNotificationsClientImpl,
|
||||
isMentionNotification
|
||||
} from '@hcengineering/notification-resources'
|
||||
@ -396,7 +396,7 @@ export async function removeChannelAction (context?: DocNotifyContext): Promise<
|
||||
|
||||
const client = getClient()
|
||||
|
||||
await deleteContextNotifications(context)
|
||||
await archiveContextNotifications(context)
|
||||
await client.remove(context)
|
||||
}
|
||||
|
||||
|
@ -45,6 +45,7 @@
|
||||
"StarDocument": "Star document",
|
||||
"UnstarDocument": "Unstar document",
|
||||
"Unsubscribe": "Unsubscribe",
|
||||
"Push": "Push"
|
||||
"Push": "Push",
|
||||
"Unreads": "Unreads"
|
||||
}
|
||||
}
|
||||
|
@ -45,6 +45,7 @@
|
||||
"StarDocument": "Добавить в избранное",
|
||||
"UnstarDocument": "Удалить из избранного",
|
||||
"Unsubscribe": "Отписаться",
|
||||
"Push": "Push"
|
||||
"Push": "Push",
|
||||
"Unreads": "Непрочитанные"
|
||||
}
|
||||
}
|
||||
|
@ -26,11 +26,12 @@
|
||||
|
||||
import InboxNotificationPresenter from './inbox/InboxNotificationPresenter.svelte'
|
||||
import NotifyContextIcon from './NotifyContextIcon.svelte'
|
||||
import { deleteContextNotifications } from '../utils'
|
||||
import { archiveContextNotifications, unarchiveContextNotifications } from '../utils'
|
||||
|
||||
export let value: DocNotifyContext
|
||||
export let notifications: WithLookup<DisplayInboxNotification>[]
|
||||
export let viewlets: ActivityNotificationViewlet[] = []
|
||||
export let archived = false
|
||||
|
||||
const maxNotifications = 3
|
||||
|
||||
@ -67,6 +68,13 @@
|
||||
{
|
||||
object: value,
|
||||
baseMenuClass: notification.class.DocNotifyContext,
|
||||
excludedActions: archived
|
||||
? [
|
||||
notification.action.ArchiveContextNotifications,
|
||||
notification.action.ReadNotifyContext,
|
||||
notification.action.UnReadNotifyContext
|
||||
]
|
||||
: [notification.action.UnarchiveContextNotifications],
|
||||
mode: 'panel'
|
||||
},
|
||||
ev.target as HTMLElement,
|
||||
@ -83,13 +91,13 @@
|
||||
isActionMenuOpened = false
|
||||
}
|
||||
|
||||
let deletingPromise: Promise<any> | undefined = undefined
|
||||
let archivingPromise: Promise<any> | undefined = undefined
|
||||
|
||||
async function checkContext (): Promise<void> {
|
||||
await deletingPromise
|
||||
deletingPromise = deleteContextNotifications(value)
|
||||
await deletingPromise
|
||||
deletingPromise = undefined
|
||||
await archivingPromise
|
||||
archivingPromise = archived ? unarchiveContextNotifications(value) : archiveContextNotifications(value)
|
||||
await archivingPromise
|
||||
archivingPromise = undefined
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -125,10 +133,10 @@
|
||||
|
||||
<div class="actions clear-mins">
|
||||
<div class="flex-center">
|
||||
{#if deletingPromise !== undefined}
|
||||
{#if archivingPromise !== undefined}
|
||||
<Spinner size="small" />
|
||||
{:else}
|
||||
<CheckBox checked={false} kind="todo" size="medium" on:value={checkContext} />
|
||||
<CheckBox checked={archived} kind="todo" size="medium" on:value={checkContext} />
|
||||
{/if}
|
||||
</div>
|
||||
<ButtonIcon
|
||||
|
@ -1,48 +0,0 @@
|
||||
<!--
|
||||
// Copyright © 2023 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 { IconFilter, SelectPopup, eventToHTMLElement, showPopup, ButtonIcon } from '@hcengineering/ui'
|
||||
import notification from '../plugin'
|
||||
|
||||
export let filter: 'all' | 'read' | 'unread' = 'all'
|
||||
|
||||
$: filters = [
|
||||
{
|
||||
id: 'all',
|
||||
isSelected: filter === 'all',
|
||||
label: notification.string.All
|
||||
},
|
||||
{
|
||||
id: 'read',
|
||||
isSelected: filter === 'read',
|
||||
label: notification.string.Read
|
||||
},
|
||||
{
|
||||
id: 'unread',
|
||||
isSelected: filter === 'unread',
|
||||
label: notification.string.Unread
|
||||
}
|
||||
]
|
||||
|
||||
function click (e: MouseEvent) {
|
||||
showPopup(SelectPopup, { value: filters }, eventToHTMLElement(e), (res) => {
|
||||
if (res) {
|
||||
filter = res
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<ButtonIcon icon={IconFilter} size="small" on:click={click} />
|
@ -30,25 +30,16 @@
|
||||
import { getActions } from '@hcengineering/view-resources'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
|
||||
import { InboxNotificationsClientImpl } from '../../inboxNotificationsClient'
|
||||
|
||||
export let value: DisplayActivityInboxNotification
|
||||
export let viewlets: ActivityNotificationViewlet[] = []
|
||||
|
||||
const client = getClient()
|
||||
const inboxClient = InboxNotificationsClientImpl.getClient()
|
||||
const activityNotificationsStore = inboxClient.activityInboxNotifications
|
||||
|
||||
let viewlet: ActivityNotificationViewlet | undefined = undefined
|
||||
let displayMessage: DisplayActivityMessage | undefined = undefined
|
||||
let actions: Action[] = []
|
||||
|
||||
$: combinedNotifications = $activityNotificationsStore.filter(({ _id }) => value.combinedIds.includes(_id))
|
||||
$: messages = combinedNotifications
|
||||
.map((it) => it.$lookup?.attachedTo)
|
||||
.filter((it): it is ActivityMessage => it !== undefined)
|
||||
|
||||
$: void updateDisplayMessage(messages)
|
||||
$: void updateDisplayMessage(value.combinedMessages)
|
||||
|
||||
async function updateDisplayMessage (messages: ActivityMessage[]): Promise<void> {
|
||||
const combinedMessages = await combineActivityMessages(sortActivityMessages(messages))
|
||||
|
@ -13,20 +13,18 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import notification, {
|
||||
import {
|
||||
ActivityInboxNotification,
|
||||
decodeObjectURI,
|
||||
DocNotifyContext,
|
||||
InboxNotification,
|
||||
decodeObjectURI
|
||||
InboxNotification
|
||||
} from '@hcengineering/notification'
|
||||
import { ActionContext, getClient } from '@hcengineering/presentation'
|
||||
import { ActionContext, createQuery, getClient } from '@hcengineering/presentation'
|
||||
import view from '@hcengineering/view'
|
||||
import {
|
||||
AnyComponent,
|
||||
ButtonWithDropdown,
|
||||
Component,
|
||||
defineSeparators,
|
||||
IconDropdown,
|
||||
Label,
|
||||
location as locationStore,
|
||||
Location,
|
||||
@ -36,25 +34,19 @@
|
||||
TabList
|
||||
} from '@hcengineering/ui'
|
||||
import chunter, { ThreadMessage } from '@hcengineering/chunter'
|
||||
import { IdMap, Ref } from '@hcengineering/core'
|
||||
import activity, { ActivityMessage } from '@hcengineering/activity'
|
||||
import { isActivityMessageClass, isReactionMessage } from '@hcengineering/activity-resources'
|
||||
import { get } from 'svelte/store'
|
||||
import { translate } from '@hcengineering/platform'
|
||||
import { getCurrentAccount, groupByArray, IdMap, Ref, SortingOrder } from '@hcengineering/core'
|
||||
|
||||
import { InboxNotificationsClientImpl } from '../../inboxNotificationsClient'
|
||||
import Filter from '../Filter.svelte'
|
||||
import {
|
||||
archiveAll,
|
||||
getDisplayInboxData,
|
||||
isMentionNotification,
|
||||
openInboxDoc,
|
||||
readAll,
|
||||
resolveLocation,
|
||||
unreadAll
|
||||
} from '../../utils'
|
||||
import SettingsButton from './SettingsButton.svelte'
|
||||
import { getDisplayInboxData, isMentionNotification, openInboxDoc, resolveLocation } from '../../utils'
|
||||
import { InboxData, InboxNotificationsFilter } from '../../types'
|
||||
import InboxGroupedListView from './InboxGroupedListView.svelte'
|
||||
import notification from '../../plugin'
|
||||
import InboxMenuButton from './InboxMenuButton.svelte'
|
||||
|
||||
export let visibleNav: boolean = true
|
||||
export let navFloat: boolean = false
|
||||
@ -62,6 +54,7 @@
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
const me = getCurrentAccount()
|
||||
|
||||
const inboxClient = InboxNotificationsClientImpl.getClient()
|
||||
const notificationsByContextStore = inboxClient.inboxNotificationsByContext
|
||||
@ -69,15 +62,23 @@
|
||||
const contextByDocStore = inboxClient.contextByDoc
|
||||
const contextsStore = inboxClient.contexts
|
||||
|
||||
const archivedActivityNotificationsQuery = createQuery()
|
||||
const archivedOtherNotificationsQuery = createQuery()
|
||||
|
||||
const allTab: TabItem = {
|
||||
id: 'all',
|
||||
labelIntl: notification.string.All
|
||||
}
|
||||
|
||||
let showArchive = false
|
||||
let archivedActivityNotifications: InboxNotification[] = []
|
||||
let archivedOtherNotifications: InboxNotification[] = []
|
||||
let archivedNotifications: InboxNotification[] = []
|
||||
|
||||
let inboxData: InboxData = new Map()
|
||||
|
||||
let filteredData: InboxData = new Map()
|
||||
let filter: InboxNotificationsFilter = 'all'
|
||||
let filter: InboxNotificationsFilter = (localStorage.getItem('inbox-filter') as InboxNotificationsFilter) ?? 'all'
|
||||
|
||||
let tabItems: TabItem[] = []
|
||||
let selectedTabId: string = allTab.id
|
||||
@ -88,9 +89,55 @@
|
||||
|
||||
let selectedMessage: ActivityMessage | undefined = undefined
|
||||
|
||||
$: void getDisplayInboxData($notificationsByContextStore).then((res) => {
|
||||
inboxData = res
|
||||
})
|
||||
$: if (showArchive) {
|
||||
archivedActivityNotificationsQuery.query(
|
||||
notification.class.ActivityInboxNotification,
|
||||
{ archived: true, user: me._id },
|
||||
(res) => {
|
||||
archivedActivityNotifications = res
|
||||
},
|
||||
{
|
||||
lookup: {
|
||||
attachedTo: activity.class.ActivityMessage
|
||||
},
|
||||
sort: {
|
||||
createdOn: SortingOrder.Descending
|
||||
},
|
||||
limit: 1000
|
||||
}
|
||||
)
|
||||
|
||||
archivedOtherNotificationsQuery.query(
|
||||
notification.class.InboxNotification,
|
||||
{ _class: { $nin: [notification.class.ActivityInboxNotification] }, archived: true, user: me._id },
|
||||
(res) => {
|
||||
archivedOtherNotifications = res
|
||||
},
|
||||
{
|
||||
sort: {
|
||||
createdOn: SortingOrder.Descending
|
||||
},
|
||||
limit: 500
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
$: archivedNotifications = [...archivedActivityNotifications, ...archivedOtherNotifications].sort(
|
||||
(n1, n2) => (n2.createdOn ?? n2.modifiedOn) - (n1.createdOn ?? n1.modifiedOn)
|
||||
)
|
||||
$: void updateInboxData($notificationsByContextStore, archivedNotifications, showArchive)
|
||||
|
||||
async function updateInboxData (
|
||||
notificationsByContext: Map<Ref<DocNotifyContext>, InboxNotification[]>,
|
||||
archivedNotifications: InboxNotification[],
|
||||
showArchive: boolean
|
||||
): Promise<void> {
|
||||
if (showArchive) {
|
||||
inboxData = await getDisplayInboxData(groupByArray(archivedNotifications, (it) => it.docNotifyContext))
|
||||
} else {
|
||||
inboxData = await getDisplayInboxData(notificationsByContext)
|
||||
}
|
||||
}
|
||||
|
||||
$: filteredData = filterData(filter, selectedTabId, inboxData, $contextByIdStore)
|
||||
|
||||
@ -262,8 +309,6 @@
|
||||
switch (filter) {
|
||||
case 'unread':
|
||||
return notifications.filter(({ isViewed }) => !isViewed)
|
||||
case 'read':
|
||||
return notifications.filter(({ isViewed }) => isViewed)
|
||||
case 'all':
|
||||
return notifications
|
||||
}
|
||||
@ -317,21 +362,30 @@
|
||||
{ size: 'auto', minSize: 30, maxSize: 'auto', float: undefined }
|
||||
])
|
||||
|
||||
async function dropdownItemSelected (id: 'archive' | 'read' | 'unread'): Promise<void> {
|
||||
if (id == null) return
|
||||
|
||||
if (id === 'archive') {
|
||||
void archiveAll()
|
||||
}
|
||||
|
||||
if (id === 'read') {
|
||||
void readAll()
|
||||
}
|
||||
|
||||
if (id === 'unread') {
|
||||
void unreadAll()
|
||||
}
|
||||
function onArchiveToggled (): void {
|
||||
showArchive = !showArchive
|
||||
selectedTabId = allTab.id
|
||||
}
|
||||
|
||||
function onUnreadsToggled (): void {
|
||||
filter = filter === 'unread' ? 'all' : 'unread'
|
||||
localStorage.setItem('inbox-filter', filter)
|
||||
}
|
||||
|
||||
$: items = [
|
||||
{
|
||||
id: 'unread',
|
||||
on: filter === 'unread',
|
||||
label: notification.string.Unreads,
|
||||
onToggle: onUnreadsToggled
|
||||
},
|
||||
{
|
||||
id: 'archive',
|
||||
on: showArchive,
|
||||
label: view.string.Archived,
|
||||
onToggle: onArchiveToggled
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<ActionContext
|
||||
@ -353,35 +407,8 @@
|
||||
<span class="title"><Label label={notification.string.Inbox} /></span>
|
||||
</div>
|
||||
<div class="flex flex-gap-2">
|
||||
{#if inboxData.size > 0}
|
||||
<ButtonWithDropdown
|
||||
justify="left"
|
||||
kind="regular"
|
||||
label={notification.string.MarkReadAll}
|
||||
icon={view.icon.Eye}
|
||||
on:click={readAll}
|
||||
dropdownItems={[
|
||||
{
|
||||
id: 'read',
|
||||
icon: view.icon.Eye,
|
||||
label: notification.string.MarkReadAll
|
||||
},
|
||||
{
|
||||
id: 'unread',
|
||||
icon: view.icon.EyeCrossed,
|
||||
label: notification.string.MarkUnreadAll
|
||||
},
|
||||
{
|
||||
id: 'archive',
|
||||
icon: view.icon.CheckCircle,
|
||||
label: notification.string.ArchiveAll
|
||||
}
|
||||
]}
|
||||
dropdownIcon={IconDropdown}
|
||||
on:dropdown-selected={(ev) => dropdownItemSelected(ev.detail)}
|
||||
/>
|
||||
{/if}
|
||||
<Filter bind:filter />
|
||||
<SettingsButton {items} />
|
||||
<InboxMenuButton />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -390,7 +417,12 @@
|
||||
</div>
|
||||
|
||||
<Scroller padding="0">
|
||||
<InboxGroupedListView data={filteredData} selectedContext={selectedContextId} on:click={selectContext} />
|
||||
<InboxGroupedListView
|
||||
data={filteredData}
|
||||
selectedContext={selectedContextId}
|
||||
archived={showArchive}
|
||||
on:click={selectContext}
|
||||
/>
|
||||
</Scroller>
|
||||
</div>
|
||||
<Separator name="inbox" float={navFloat ? 'navigator' : true} index={0} />
|
||||
|
@ -25,11 +25,12 @@
|
||||
|
||||
import { InboxNotificationsClientImpl } from '../../inboxNotificationsClient'
|
||||
import DocNotifyContextCard from '../DocNotifyContextCard.svelte'
|
||||
import { deleteContextNotifications } from '../../utils'
|
||||
import { archiveContextNotifications, unarchiveContextNotifications } from '../../utils'
|
||||
import { InboxData } from '../../types'
|
||||
|
||||
export let data: InboxData
|
||||
export let selectedContext: Ref<DocNotifyContext> | undefined
|
||||
export let archived = false
|
||||
|
||||
const client = getClient()
|
||||
const dispatch = createEventDispatcher()
|
||||
@ -83,7 +84,11 @@
|
||||
const contextId = displayData[listSelection]?.[0]
|
||||
const context = $contextByIdStore.get(contextId)
|
||||
|
||||
void deleteContextNotifications(context)
|
||||
if (archived) {
|
||||
void unarchiveContextNotifications(context)
|
||||
} else {
|
||||
void archiveContextNotifications(context)
|
||||
}
|
||||
}
|
||||
if (key.code === 'Enter') {
|
||||
key.preventDefault()
|
||||
@ -121,6 +126,7 @@
|
||||
<DocNotifyContextCard
|
||||
value={context}
|
||||
notifications={contextNotifications}
|
||||
{archived}
|
||||
{viewlets}
|
||||
on:click={(event) => {
|
||||
dispatch('click', event.detail)
|
||||
|
@ -0,0 +1,61 @@
|
||||
<!--
|
||||
// Copyright © 2023 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 view from '@hcengineering/view'
|
||||
import { ButtonMenu, IconMoreV } from '@hcengineering/ui'
|
||||
|
||||
import notification from '../../plugin'
|
||||
import { archiveAll, readAll, unreadAll } from '../../utils'
|
||||
|
||||
async function onSelect (id?: 'archive' | 'read' | 'unread'): Promise<void> {
|
||||
if (id == null) return
|
||||
|
||||
if (id === 'archive') {
|
||||
void archiveAll()
|
||||
}
|
||||
|
||||
if (id === 'read') {
|
||||
void readAll()
|
||||
}
|
||||
|
||||
if (id === 'unread') {
|
||||
void unreadAll()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<ButtonMenu
|
||||
size="small"
|
||||
noSelection
|
||||
icon={IconMoreV}
|
||||
items={[
|
||||
{
|
||||
id: 'read',
|
||||
icon: view.icon.Eye,
|
||||
label: notification.string.MarkReadAll
|
||||
},
|
||||
{
|
||||
id: 'unread',
|
||||
icon: view.icon.EyeCrossed,
|
||||
label: notification.string.MarkUnreadAll
|
||||
},
|
||||
{
|
||||
id: 'archive',
|
||||
icon: view.icon.CheckCircle,
|
||||
label: notification.string.ArchiveAll
|
||||
}
|
||||
]}
|
||||
on:selected={(ev) => onSelect(ev.detail)}
|
||||
/>
|
@ -0,0 +1,28 @@
|
||||
<!--
|
||||
// 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 { eventToHTMLElement, showPopup, ButtonIcon, IconSettings } from '@hcengineering/ui'
|
||||
|
||||
import SettingsPopup from './SettingsPopup.svelte'
|
||||
import { SettingItem } from '../../types'
|
||||
|
||||
export let items: SettingItem[] = []
|
||||
|
||||
function click (e: MouseEvent): void {
|
||||
showPopup(SettingsPopup, { items }, eventToHTMLElement(e))
|
||||
}
|
||||
</script>
|
||||
|
||||
<ButtonIcon icon={IconSettings} size="small" on:click={click} />
|
@ -0,0 +1,107 @@
|
||||
<!--
|
||||
// Copyright © 2023 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 { createEventDispatcher } from 'svelte'
|
||||
import { createFocusManager, FocusHandler, Label, ListView, ModernToggle, resizeObserver } from '@hcengineering/ui'
|
||||
import { SettingItem } from '../../types'
|
||||
|
||||
export let items: SettingItem[] = []
|
||||
|
||||
let popupElement: HTMLDivElement | undefined = undefined
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let selection = 0
|
||||
let list: ListView
|
||||
|
||||
export function onKeydown (key: KeyboardEvent): boolean {
|
||||
if (key.code === 'Tab') {
|
||||
dispatch('close')
|
||||
key.preventDefault()
|
||||
key.stopPropagation()
|
||||
return true
|
||||
}
|
||||
if (key.code === 'ArrowUp') {
|
||||
key.stopPropagation()
|
||||
key.preventDefault()
|
||||
list.select(selection - 1)
|
||||
return true
|
||||
}
|
||||
if (key.code === 'ArrowDown') {
|
||||
key.stopPropagation()
|
||||
key.preventDefault()
|
||||
list.select(selection + 1)
|
||||
return true
|
||||
}
|
||||
if (key.code === 'Enter') {
|
||||
key.preventDefault()
|
||||
key.stopPropagation()
|
||||
items[selection]?.onToggle()
|
||||
items = items.map((item, index) => {
|
||||
if (index === selection) {
|
||||
return { ...item, on: !item.on }
|
||||
}
|
||||
|
||||
return item
|
||||
})
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const manager = createFocusManager()
|
||||
|
||||
$: if (popupElement) {
|
||||
popupElement.focus()
|
||||
}
|
||||
</script>
|
||||
|
||||
<FocusHandler {manager} />
|
||||
|
||||
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div
|
||||
class="selectPopup"
|
||||
bind:this={popupElement}
|
||||
tabindex="0"
|
||||
use:resizeObserver={() => {
|
||||
dispatch('changeContent')
|
||||
}}
|
||||
on:keydown={onKeydown}
|
||||
>
|
||||
<div class="menu-space" />
|
||||
<div class="scroll">
|
||||
<div class="box">
|
||||
<ListView bind:this={list} count={items.length} bind:selection on:changeContent={() => dispatch('changeContent')}>
|
||||
<svelte:fragment slot="item" let:item={itemId}>
|
||||
{@const item = items[itemId]}
|
||||
<div class="menu-item withList w-full container">
|
||||
<Label label={item.label} />
|
||||
<ModernToggle checked={item.on} size="small" on:change={item.onToggle} />
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</ListView>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu-space" />
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
</style>
|
@ -34,6 +34,7 @@ import notification, {
|
||||
} from '@hcengineering/notification'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { derived, get, writable } from 'svelte/store'
|
||||
import { isActivityNotification } from './utils'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@ -103,6 +104,7 @@ export class InboxNotificationsClientImpl implements InboxNotificationsClient {
|
||||
notification.class.InboxNotification,
|
||||
{
|
||||
_class: { $nin: [notification.class.ActivityInboxNotification] },
|
||||
archived: { $ne: true },
|
||||
user: getCurrentAccount()._id
|
||||
},
|
||||
(result: InboxNotification[]) => {
|
||||
@ -118,6 +120,7 @@ export class InboxNotificationsClientImpl implements InboxNotificationsClient {
|
||||
this.activityInboxNotificationsQuery.query(
|
||||
notification.class.ActivityInboxNotification,
|
||||
{
|
||||
archived: { $ne: true },
|
||||
user: getCurrentAccount()._id
|
||||
},
|
||||
(result: ActivityInboxNotification[]) => {
|
||||
@ -249,28 +252,29 @@ export class InboxNotificationsClientImpl implements InboxNotificationsClient {
|
||||
}
|
||||
}
|
||||
|
||||
async deleteNotifications (client: TxOperations, ids: Array<Ref<InboxNotification>>): Promise<void> {
|
||||
async archiveNotifications (client: TxOperations, ids: Array<Ref<InboxNotification>>): Promise<void> {
|
||||
const inboxNotifications = (get(this.inboxNotifications) ?? []).filter(({ _id }) => ids.includes(_id))
|
||||
for (const notification of inboxNotifications) {
|
||||
await client.remove(notification)
|
||||
await client.update(notification, { archived: true })
|
||||
}
|
||||
}
|
||||
|
||||
async deleteAllNotifications (): Promise<void> {
|
||||
const doneOp = await getClient().measure('deleteAllNotifications')
|
||||
async archiveAllNotifications (): Promise<void> {
|
||||
const doneOp = await getClient().measure('archiveAllNotifications')
|
||||
const ops = getClient().apply(generateId())
|
||||
|
||||
try {
|
||||
const inboxNotifications = await ops.findAll(
|
||||
notification.class.InboxNotification,
|
||||
{
|
||||
user: getCurrentAccount()._id
|
||||
user: getCurrentAccount()._id,
|
||||
archived: { $ne: true }
|
||||
},
|
||||
{ projection: { _id: 1, _class: 1, space: 1 } }
|
||||
)
|
||||
const contexts = get(this.contexts) ?? []
|
||||
for (const notification of inboxNotifications) {
|
||||
await ops.removeDoc(notification._class, notification.space, notification._id)
|
||||
await ops.updateDoc(notification._class, notification.space, notification._id, { archived: true })
|
||||
}
|
||||
|
||||
for (const context of contexts) {
|
||||
@ -291,7 +295,8 @@ export class InboxNotificationsClientImpl implements InboxNotificationsClient {
|
||||
notification.class.InboxNotification,
|
||||
{
|
||||
user: getCurrentAccount()._id,
|
||||
isViewed: { $ne: true }
|
||||
isViewed: { $ne: true },
|
||||
archived: { $ne: true }
|
||||
},
|
||||
{ projection: { _id: 1, _class: 1, space: 1 } }
|
||||
)
|
||||
@ -317,9 +322,13 @@ export class InboxNotificationsClientImpl implements InboxNotificationsClient {
|
||||
notification.class.InboxNotification,
|
||||
{
|
||||
user: getCurrentAccount()._id,
|
||||
isViewed: true
|
||||
isViewed: true,
|
||||
archived: { $ne: true }
|
||||
},
|
||||
{ projection: { _id: 1, _class: 1, space: 1 } }
|
||||
{
|
||||
projection: { _id: 1, _class: 1, space: 1, docNotifyContext: 1 },
|
||||
sort: { createdOn: SortingOrder.Ascending }
|
||||
}
|
||||
)
|
||||
const contexts = get(this.contexts) ?? []
|
||||
|
||||
@ -327,7 +336,17 @@ export class InboxNotificationsClientImpl implements InboxNotificationsClient {
|
||||
await ops.updateDoc(notification._class, notification.space, notification._id, { isViewed: false })
|
||||
}
|
||||
for (const context of contexts) {
|
||||
await ops.update(context, { lastViewedTimestamp: 0 })
|
||||
const firstUnread = inboxNotifications.find(
|
||||
(it) => it.docNotifyContext === context._id && isActivityNotification(it)
|
||||
)
|
||||
|
||||
if (firstUnread === undefined) {
|
||||
continue
|
||||
}
|
||||
|
||||
const lastViewedTimestamp = (firstUnread.createdOn ?? firstUnread.modifiedOn) - 1
|
||||
|
||||
await ops.update(context, { lastViewedTimestamp })
|
||||
}
|
||||
} finally {
|
||||
await ops.commit()
|
||||
|
@ -38,12 +38,13 @@ import {
|
||||
canUnReadNotifyContext,
|
||||
readNotifyContext,
|
||||
unReadNotifyContext,
|
||||
deleteContextNotifications,
|
||||
archiveContextNotifications,
|
||||
hasInboxNotifications,
|
||||
archiveAll,
|
||||
readAll,
|
||||
unreadAll,
|
||||
checkPermission
|
||||
checkPermission,
|
||||
unarchiveContextNotifications
|
||||
} from './utils'
|
||||
|
||||
import { InboxNotificationsClientImpl } from './inboxNotificationsClient'
|
||||
@ -86,7 +87,8 @@ export default async (): Promise<Resources> => ({
|
||||
UnpinDocNotifyContext: unpinDocNotifyContext,
|
||||
ReadNotifyContext: readNotifyContext,
|
||||
UnReadNotifyContext: unReadNotifyContext,
|
||||
DeleteContextNotifications: deleteContextNotifications,
|
||||
ArchiveContextNotifications: archiveContextNotifications,
|
||||
UnarchiveContextNotifications: unarchiveContextNotifications,
|
||||
ArchiveAll: archiveAll,
|
||||
ReadAll: readAll,
|
||||
UnreadAll: unreadAll
|
||||
|
@ -34,6 +34,7 @@ export default mergeIds(notificationId, notification, {
|
||||
Activity: '' as IntlString,
|
||||
People: '' as IntlString,
|
||||
Read: '' as IntlString,
|
||||
Unread: '' as IntlString
|
||||
Unread: '' as IntlString,
|
||||
Unreads: '' as IntlString
|
||||
}
|
||||
})
|
||||
|
@ -1,6 +1,14 @@
|
||||
import type { Ref } from '@hcengineering/core'
|
||||
import type { DisplayInboxNotification, DocNotifyContext } from '@hcengineering/notification'
|
||||
import type { IntlString } from '@hcengineering/platform'
|
||||
|
||||
export type InboxNotificationsFilter = 'all' | 'read' | 'unread'
|
||||
export type InboxNotificationsFilter = 'all' | 'unread'
|
||||
|
||||
export type InboxData = Map<Ref<DocNotifyContext>, DisplayInboxNotification[]>
|
||||
|
||||
export interface SettingItem {
|
||||
id: string
|
||||
on: boolean
|
||||
label: IntlString
|
||||
onToggle: () => void
|
||||
}
|
||||
|
@ -143,23 +143,23 @@ export async function unReadNotifyContext (doc: DocNotifyContext): Promise<void>
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function deleteContextNotifications (doc?: DocNotifyContext): Promise<void> {
|
||||
export async function archiveContextNotifications (doc?: DocNotifyContext): Promise<void> {
|
||||
if (doc === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
const doneOp = await getClient().measure('deleteContextNotifications')
|
||||
const doneOp = await getClient().measure('archiveContextNotifications')
|
||||
const ops = getClient().apply(doc._id)
|
||||
|
||||
try {
|
||||
const notifications = await ops.findAll(
|
||||
notification.class.InboxNotification,
|
||||
{ docNotifyContext: doc._id },
|
||||
{ docNotifyContext: doc._id, archived: { $ne: true } },
|
||||
{ projection: { _id: 1, _class: 1, space: 1 } }
|
||||
)
|
||||
|
||||
for (const notification of notifications) {
|
||||
await ops.removeDoc(notification._class, notification.space, notification._id)
|
||||
await ops.updateDoc(notification._class, notification.space, notification._id, { archived: true, isViewed: true })
|
||||
}
|
||||
await ops.update(doc, { lastViewedTimestamp: Date.now() })
|
||||
} finally {
|
||||
@ -168,6 +168,33 @@ export async function deleteContextNotifications (doc?: DocNotifyContext): Promi
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function unarchiveContextNotifications (doc?: DocNotifyContext): Promise<void> {
|
||||
if (doc === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
const doneOp = await getClient().measure('unarchiveContextNotifications')
|
||||
const ops = getClient().apply(doc._id)
|
||||
|
||||
try {
|
||||
const notifications = await ops.findAll(
|
||||
notification.class.InboxNotification,
|
||||
{ docNotifyContext: doc._id, archived: true },
|
||||
{ projection: { _id: 1, _class: 1, space: 1 } }
|
||||
)
|
||||
|
||||
for (const notification of notifications) {
|
||||
await ops.updateDoc(notification._class, notification.space, notification._id, { archived: false })
|
||||
}
|
||||
} finally {
|
||||
await ops.commit()
|
||||
await doneOp()
|
||||
}
|
||||
}
|
||||
|
||||
enum OpWithMe {
|
||||
Add = 'add',
|
||||
Remove = 'remove'
|
||||
@ -259,7 +286,7 @@ export async function archiveAll (): Promise<void> {
|
||||
'top',
|
||||
(result?: boolean) => {
|
||||
if (result === true) {
|
||||
void client.deleteAllNotifications()
|
||||
void client.archiveAllNotifications()
|
||||
}
|
||||
}
|
||||
)
|
||||
@ -300,10 +327,6 @@ export async function getDisplayInboxNotifications (
|
||||
continue
|
||||
}
|
||||
|
||||
if (filter === 'read' && !notification.isViewed) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (isActivityNotification(notification)) {
|
||||
activityNotifications.push(notification)
|
||||
} else {
|
||||
@ -344,7 +367,10 @@ export async function getDisplayInboxNotifications (
|
||||
|
||||
const displayNotification = {
|
||||
...activityNotification,
|
||||
combinedIds: activityNotifications.filter(({ attachedTo }) => ids.includes(attachedTo)).map(({ _id }) => _id)
|
||||
combinedIds: activityNotifications.filter(({ attachedTo }) => ids.includes(attachedTo)).map(({ _id }) => _id),
|
||||
combinedMessages: activityNotifications
|
||||
.map((a) => a.$lookup?.attachedTo)
|
||||
.filter((m): m is ActivityMessage => m !== undefined)
|
||||
}
|
||||
|
||||
result.push(displayNotification)
|
||||
@ -353,7 +379,8 @@ export async function getDisplayInboxNotifications (
|
||||
if (activityNotification !== undefined) {
|
||||
result.push({
|
||||
...activityNotification,
|
||||
combinedIds: [activityNotification._id]
|
||||
combinedIds: [activityNotification._id],
|
||||
combinedMessages: [message]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -252,6 +252,7 @@ export interface InboxNotification extends Doc {
|
||||
body?: IntlString
|
||||
intlParams?: Record<string, string | number>
|
||||
intlParamsNotLocalized?: Record<string, IntlString>
|
||||
archived?: boolean
|
||||
}
|
||||
|
||||
export interface ActivityInboxNotification extends InboxNotification {
|
||||
@ -278,6 +279,7 @@ export interface MentionInboxNotification extends CommonInboxNotification {
|
||||
|
||||
export interface DisplayActivityInboxNotification extends ActivityInboxNotification {
|
||||
combinedIds: Ref<ActivityInboxNotification>[]
|
||||
combinedMessages: ActivityMessage[]
|
||||
}
|
||||
|
||||
export type DisplayInboxNotification = DisplayActivityInboxNotification | InboxNotification
|
||||
@ -315,8 +317,8 @@ export interface InboxNotificationsClient {
|
||||
readMessages: (client: TxOperations, ids: Ref<ActivityMessage>[]) => Promise<void>
|
||||
readNotifications: (client: TxOperations, ids: Array<Ref<InboxNotification>>) => Promise<void>
|
||||
unreadNotifications: (client: TxOperations, ids: Array<Ref<InboxNotification>>) => Promise<void>
|
||||
deleteNotifications: (client: TxOperations, ids: Array<Ref<InboxNotification>>) => Promise<void>
|
||||
deleteAllNotifications: () => Promise<void>
|
||||
archiveNotifications: (client: TxOperations, ids: Array<Ref<InboxNotification>>) => Promise<void>
|
||||
archiveAllNotifications: () => Promise<void>
|
||||
readAllNotifications: () => Promise<void>
|
||||
unreadAllNotifications: () => Promise<void>
|
||||
}
|
||||
@ -401,7 +403,8 @@ const notification = plugin(notificationId, {
|
||||
UnpinDocNotifyContext: '' as Ref<Action>,
|
||||
UnReadNotifyContext: '' as Ref<Action>,
|
||||
ReadNotifyContext: '' as Ref<Action>,
|
||||
DeleteContextNotifications: '' as Ref<Action>
|
||||
ArchiveContextNotifications: '' as Ref<Action>,
|
||||
UnarchiveContextNotifications: '' as Ref<Action>
|
||||
},
|
||||
icon: {
|
||||
Notifications: '' as Asset,
|
||||
|
@ -144,4 +144,7 @@
|
||||
<path d="M1 9C1 8.72386 1.22386 8.5 1.5 8.5C1.77614 8.5 2 8.72386 2 9V10C2 10.5523 2.44772 11 3 11H11C11.5523 11 12 10.5523 12 10V9C12 8.72386 12.2239 8.5 12.5 8.5C12.7761 8.5 13 8.72386 13 9V10C13 11.1046 12.1046 12 11 12H3C1.89543 12 1 11.1046 1 10V9Z" />
|
||||
<path d="M14 6C14 5.72386 13.7761 5.5 13.5 5.5H0.5C0.223858 5.5 0 5.72386 0 6C0 6.27614 0.223857 6.5 0.5 6.5H13.5C13.7761 6.5 14 6.27614 14 6Z" />
|
||||
</symbol>
|
||||
<symbol id="circle" viewBox="0 0 32 32">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16 28.0002C22.6274 28.0002 28 22.6277 28 16.0002C28 9.37283 22.6274 4.00024 16 4.00024C9.37258 4.00024 4 9.37283 4 16.0002C4 22.6277 9.37258 28.0002 16 28.0002ZM16 30.0002C23.732 30.0002 30 23.7322 30 16.0002C30 8.26826 23.732 2.00024 16 2.00024C8.26801 2.00024 2 8.26826 2 16.0002C2 23.7322 8.26801 30.0002 16 30.0002Z" />
|
||||
</symbol>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
@ -120,6 +120,7 @@
|
||||
"UnArchive": "Unarchive",
|
||||
"Pin": "Pin",
|
||||
"Unpin": "Unpin",
|
||||
"Archived": "Archived",
|
||||
"MoreActions": "More actions"
|
||||
}
|
||||
}
|
||||
|
@ -117,6 +117,7 @@
|
||||
"UnArchive": "Разархивировать",
|
||||
"Pin": "Закрепить",
|
||||
"Unpin": "Открепить",
|
||||
"Archived": "Архивированные",
|
||||
"MoreActions": "Больше действий"
|
||||
}
|
||||
}
|
||||
|
@ -50,5 +50,6 @@ loadMetadata(view.icon, {
|
||||
Image: `${icons}#image`,
|
||||
Table2: `${icons}#table2`,
|
||||
CodeBlock: `${icons}#code-block`,
|
||||
SeparatorLine: `${icons}#separator-line`
|
||||
SeparatorLine: `${icons}#separator-line`,
|
||||
Circle: `${icons}#circle`
|
||||
})
|
||||
|
@ -43,7 +43,6 @@ export default mergeIds(viewId, view, {
|
||||
DeletePopupNoPermissionLabel: '' as IntlString,
|
||||
DeletePopupCreatorLabel: '' as IntlString,
|
||||
DeletePopupOwnerLabel: '' as IntlString,
|
||||
Archive: '' as IntlString,
|
||||
ArchiveConfirm: '' as IntlString,
|
||||
Assignees: '' as IntlString,
|
||||
Labels: '' as IntlString,
|
||||
|
@ -193,8 +193,10 @@ const view = plugin(viewId, {
|
||||
NoGrouping: '' as IntlString,
|
||||
Type: '' as IntlString,
|
||||
UnArchive: '' as IntlString,
|
||||
Archive: '' as IntlString,
|
||||
Save: '' as IntlString,
|
||||
PublicView: '' as IntlString,
|
||||
Archived: '' as IntlString,
|
||||
MoreActions: '' as IntlString
|
||||
},
|
||||
icon: {
|
||||
@ -230,7 +232,8 @@ const view = plugin(viewId, {
|
||||
Image: '' as Asset,
|
||||
Table2: '' as Asset,
|
||||
CodeBlock: '' as Asset,
|
||||
SeparatorLine: '' as Asset
|
||||
SeparatorLine: '' as Asset,
|
||||
Circle: '' as Asset
|
||||
},
|
||||
category: {
|
||||
General: '' as Ref<ActionCategory>,
|
||||
|
Loading…
Reference in New Issue
Block a user