Analytics channels fixes (#6847)

Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
This commit is contained in:
Kristina 2024-10-09 11:46:48 +04:00 committed by GitHub
parent e9b3ef523e
commit d6bbe79c88
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 93 additions and 30 deletions

2
.vscode/launch.json vendored
View File

@ -432,7 +432,7 @@
"request": "launch", "request": "launch",
"args": ["src/index.ts"], "args": ["src/index.ts"],
"env": { "env": {
"PORT": "4007", "PORT": "4017",
"SECRET": "secret", "SECRET": "secret",
"MONGO_URL": "mongodb://localhost:27017", "MONGO_URL": "mongodb://localhost:27017",
"MINIO_ENDPOINT": "localhost", "MINIO_ENDPOINT": "localhost",

View File

@ -87,7 +87,7 @@
<ModernTab <ModernTab
label={tab.name} label={tab.name}
labelIntl={tab.nameIntl ?? widget.label} labelIntl={widget.label}
highlighted={selected} highlighted={selected}
orientation="vertical" orientation="vertical"
kind={tab.isPinned ? 'secondary' : 'primary'} kind={tab.isPinned ? 'secondary' : 'primary'}

View File

@ -43,7 +43,7 @@
<ModernTab <ModernTab
label={tab.name} label={tab.name}
labelIntl={tab.nameIntl ?? widget.label} labelIntl={widget.label}
highlighted={selected} highlighted={selected}
orientation="vertical" orientation="vertical"
kind={tab.isPinned ? 'secondary' : 'primary'} kind={tab.isPinned ? 'secondary' : 'primary'}

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// //
import { type Widget, type WidgetTab } from '@hcengineering/workbench' import { WorkbenchEvents, type Widget, type WidgetTab } from '@hcengineering/workbench'
import { getCurrentAccount, type Ref } from '@hcengineering/core' import { getCurrentAccount, type Ref } from '@hcengineering/core'
import { get, writable } from 'svelte/store' import { get, writable } from 'svelte/store'
import { getCurrentLocation } from '@hcengineering/ui' import { getCurrentLocation } from '@hcengineering/ui'
@ -117,7 +117,7 @@ export function openWidget (
openedByUser openedByUser
}) })
Analytics.handleEvent('workbench.OpenSidebarWidget', { widget: widget._id }) Analytics.handleEvent(WorkbenchEvents.SidebarOpenWidget, { widget: widget._id })
sidebarStore.set({ sidebarStore.set({
...state, ...state,
widgetsState, widgetsState,
@ -135,7 +135,7 @@ export function closeWidget (widget: Ref<Widget>): void {
} }
widgetsState.delete(widget) widgetsState.delete(widget)
Analytics.handleEvent('workbench.CloseSidebarWidget', { widget }) Analytics.handleEvent(WorkbenchEvents.SidebarCloseWidget, { widget })
if (state.widget === widget) { if (state.widget === widget) {
sidebarStore.set({ sidebarStore.set({
...state, ...state,
@ -162,7 +162,7 @@ export async function closeWidgetTab (widget: Widget, tab: string): Promise<void
const newTabs = tabs.filter((it) => it.id !== tab) const newTabs = tabs.filter((it) => it.id !== tab)
const closedTab = tabs.find((it) => it.id === tab) const closedTab = tabs.find((it) => it.id === tab)
Analytics.handleEvent('workbench.CloseSidebarWidget', { widget: widget._id, tab }) Analytics.handleEvent(WorkbenchEvents.SidebarCloseWidget, { widget: widget._id, tab: closedTab?.name })
if (widget.onTabClose !== undefined && closedTab !== undefined) { if (widget.onTabClose !== undefined && closedTab !== undefined) {
const fn = await getResource(widget.onTabClose) const fn = await getResource(widget.onTabClose)
@ -208,7 +208,7 @@ export function openWidgetTab (widget: Ref<Widget>, tab: string): void {
if (newTab === undefined) return if (newTab === undefined) return
widgetsState.set(widget, { ...widgetState, tab }) widgetsState.set(widget, { ...widgetState, tab })
Analytics.handleEvent('workbench.OpenSidebarWidget', { widget, tab }) Analytics.handleEvent(WorkbenchEvents.SidebarOpenWidget, { widget, tab: newTab?.name })
sidebarStore.set({ sidebarStore.set({
...state, ...state,
widgetsState widgetsState
@ -243,7 +243,7 @@ export function createWidgetTab (widget: Widget, tab: WidgetTab, newTab = false)
tab: tab.id tab: tab.id
}) })
Analytics.handleEvent('workbench.OpenSidebarWidget', { widget: widget._id, tab: tab.id }) Analytics.handleEvent(WorkbenchEvents.SidebarOpenWidget, { widget: widget._id, tab: tab.name })
sidebarStore.set({ sidebarStore.set({
...state, ...state,
widget: widget._id, widget: widget._id,

View File

@ -1,4 +1,6 @@
export enum WorkbenchEvents { export enum WorkbenchEvents {
DocumentationOpened = 'workbench.help.DocumentationOpened', DocumentationOpened = 'workbench.help.DocumentationOpened',
KeyboardShortcutsOpened = 'workbench.help.KeyboardShortcutsOpened' KeyboardShortcutsOpened = 'workbench.help.KeyboardShortcutsOpened',
SidebarCloseWidget = 'workbench.sidebar.CloseWidget',
SidebarOpenWidget = 'workbench.sidebar.OpenWidget'
} }

View File

@ -87,7 +87,6 @@ export interface WidgetPreference extends Preference {
export interface WidgetTab { export interface WidgetTab {
id: string id: string
name?: string name?: string
nameIntl?: IntlString
icon?: Asset | AnySvelteComponent icon?: Asset | AnySvelteComponent
iconComponent?: AnyComponent iconComponent?: AnyComponent
iconProps?: Record<string, any> iconProps?: Record<string, any>

View File

@ -130,27 +130,27 @@ export class WorkspaceClient {
this.ctx.info('Upload avatar file', { workspace: this.workspace }) this.ctx.info('Upload avatar file', { workspace: this.workspace })
try { try {
await this.checkPersonData(client)
const stat = fs.statSync(config.AvatarPath) const stat = fs.statSync(config.AvatarPath)
const lastModified = stat.mtime.getTime() const lastModified = stat.mtime.getTime()
if ( const isAlreadyUploaded =
this.info !== undefined && this.info !== undefined &&
this.info.avatarPath === config.AvatarPath && this.info.avatarPath === config.AvatarPath &&
this.info.avatarLastModified === lastModified this.info.avatarLastModified === lastModified
) { if (!isAlreadyUploaded) {
this.ctx.info('Avatar file already uploaded', { workspace: this.workspace, path: config.AvatarPath }) const data = fs.readFileSync(config.AvatarPath)
return
}
const data = fs.readFileSync(config.AvatarPath)
await this.blobClient.upload(this.ctx, config.AvatarName, data.length, config.AvatarContentType, data) await this.blobClient.upload(this.ctx, config.AvatarName, data.length, config.AvatarContentType, data)
await this.controller.updateAvatarInfo(this.workspace, config.AvatarPath, lastModified) await this.controller.updateAvatarInfo(this.workspace, config.AvatarPath, lastModified)
this.ctx.info('Uploaded avatar file', { workspace: this.workspace, path: config.AvatarPath }) this.ctx.info('Avatar file uploaded successfully', { workspace: this.workspace, path: config.AvatarPath })
} else {
this.ctx.info('Avatar file already uploaded', { workspace: this.workspace, path: config.AvatarPath })
}
} catch (e) { } catch (e) {
this.ctx.error('Failed to upload avatar file', { e }) this.ctx.error('Failed to upload avatar file', { e })
} }
await this.checkPersonData(client)
} }
private async tryLogin (): Promise<void> { private async tryLogin (): Promise<void> {

View File

@ -57,6 +57,8 @@
"@hcengineering/analytics-collector": "^0.6.0", "@hcengineering/analytics-collector": "^0.6.0",
"@hcengineering/analytics-collector-assets": "^0.6.0", "@hcengineering/analytics-collector-assets": "^0.6.0",
"@hcengineering/analytics-service": "^0.6.0", "@hcengineering/analytics-service": "^0.6.0",
"@hcengineering/calendar": "^0.6.24",
"@hcengineering/calendar-assets": "^0.6.22",
"@hcengineering/chunter": "^0.6.20", "@hcengineering/chunter": "^0.6.20",
"@hcengineering/chunter-assets": "^0.6.18", "@hcengineering/chunter-assets": "^0.6.18",
"@hcengineering/client": "^0.6.18", "@hcengineering/client": "^0.6.18",

View File

@ -23,14 +23,19 @@ import notification, { notificationId } from '@hcengineering/notification'
import recruit, { recruitId } from '@hcengineering/recruit' import recruit, { recruitId } from '@hcengineering/recruit'
import time, { timeId } from '@hcengineering/time' import time, { timeId } from '@hcengineering/time'
import tracker, { trackerId } from '@hcengineering/tracker' import tracker, { trackerId } from '@hcengineering/tracker'
import { Class, Doc, Hierarchy, Markup, Ref } from '@hcengineering/core' import workbench, { WorkbenchEvents } from '@hcengineering/workbench'
import { Class, Doc, Hierarchy, Markup, Ref, TxOperations } from '@hcengineering/core'
import { MarkupNode, MarkupNodeType, MarkupMark, MarkupMarkType } from '@hcengineering/text' import { MarkupNode, MarkupNodeType, MarkupMark, MarkupMarkType } from '@hcengineering/text'
import { translate } from '@hcengineering/platform' import { translate } from '@hcengineering/platform'
export async function eventToMarkup (event: AnalyticEvent, hierarchy: Hierarchy): Promise<Markup | undefined> { export async function eventToMarkup (
event: AnalyticEvent,
hierarchy: Hierarchy,
client: TxOperations
): Promise<Markup | undefined> {
switch (event.event) { switch (event.event) {
case AnalyticEventType.CustomEvent: case AnalyticEventType.CustomEvent:
return formatCustomEvent(event) return await formatCustomEvent(event, client)
case AnalyticEventType.Error: case AnalyticEventType.Error:
return await formatErrorEvent(event) return await formatErrorEvent(event)
case AnalyticEventType.Navigation: case AnalyticEventType.Navigation:
@ -67,10 +72,15 @@ function toText (text: string, display: 'normal' | 'bold' | 'code' = 'normal'):
return { type: MarkupNodeType.text, text, marks } return { type: MarkupNodeType.text, text, marks }
} }
function formatCustomEvent (event: AnalyticEvent): string | undefined { async function formatCustomEvent (event: AnalyticEvent, client: TxOperations): Promise<string | undefined> {
const text = event.params.event as string | undefined const text = event.params.event as string | undefined
if (text === undefined || text === '') return if (text === undefined || text === '') return
if (eventsToSkip.includes(event.params.event)) return
if (sidebarEvents.includes(text)) {
return await formatSidebarEvent(text, event.params, client)
}
const paramsTexts = [] const paramsTexts = []
for (const key in event.params) { for (const key in event.params) {
@ -85,6 +95,41 @@ function formatCustomEvent (event: AnalyticEvent): string | undefined {
return toMarkup([toText(text + ' '), toText(paramsTexts.join(', '), 'code')]) return toMarkup([toText(text + ' '), toText(paramsTexts.join(', '), 'code')])
} }
async function formatSidebarEvent (
event: string,
params: Record<string, any>,
client: TxOperations
): Promise<string | undefined> {
let text = event
switch (event) {
case WorkbenchEvents.SidebarOpenWidget:
text = 'open widget'
break
case WorkbenchEvents.SidebarCloseWidget:
text = 'close widget'
break
default:
break
}
const paramsTexts = []
if (params.widget !== undefined) {
const widget = client.getModel().findAllSync(workbench.class.Widget, { _id: params.widget })[0]
if (widget !== undefined) {
const widgetName = await translate(widget.label, {})
paramsTexts.push(`widget: ${widgetName}`)
} else {
paramsTexts.push(`widget: ${params.widget}`)
}
}
if (params.tab !== undefined) {
paramsTexts.push(`tab: ${params.tab}`)
}
return toMarkup([toText('Sidebar: ', 'bold'), toText(text + ' '), toText(paramsTexts.join(', '), 'code')])
}
async function formatErrorEvent (event: AnalyticEvent): Promise<string | undefined> { async function formatErrorEvent (event: AnalyticEvent): Promise<string | undefined> {
const error = event.params.error const error = event.params.error
@ -427,3 +472,17 @@ export function getOnboardingMessage (email: string, workspace: string, name: st
return toMarkup(nodes) return toMarkup(nodes)
} }
const eventsToSkip = [
'Fetch workspace',
'Create Tab',
'Update Tab',
'document.Opened',
'Create Message',
'chunter.MessageCreated',
'Create Time',
'Create Tag',
'SetCollectionItems'
]
const sidebarEvents = [WorkbenchEvents.SidebarOpenWidget, WorkbenchEvents.SidebarCloseWidget] as string[]

View File

@ -14,6 +14,7 @@
// //
import { analyticsCollectorId } from '@hcengineering/analytics-collector' import { analyticsCollectorId } from '@hcengineering/analytics-collector'
import { calendarId } from '@hcengineering/calendar'
import { chunterId } from '@hcengineering/chunter' import { chunterId } from '@hcengineering/chunter'
import { contactId } from '@hcengineering/contact' import { contactId } from '@hcengineering/contact'
import { coreId } from '@hcengineering/core' import { coreId } from '@hcengineering/core'
@ -32,6 +33,7 @@ import { viewId } from '@hcengineering/view'
import { workbenchId } from '@hcengineering/workbench' import { workbenchId } from '@hcengineering/workbench'
import analyticsCollectorEn from '@hcengineering/analytics-collector-assets/lang/en.json' import analyticsCollectorEn from '@hcengineering/analytics-collector-assets/lang/en.json'
import calendarEn from '@hcengineering/calendar-assets/lang/en.json'
import chunterEn from '@hcengineering/chunter-assets/lang/en.json' import chunterEn from '@hcengineering/chunter-assets/lang/en.json'
import contactEn from '@hcengineering/contact-assets/lang/en.json' import contactEn from '@hcengineering/contact-assets/lang/en.json'
import coreEng from '@hcengineering/core/lang/en.json' import coreEng from '@hcengineering/core/lang/en.json'
@ -57,12 +59,14 @@ export function registerLoaders (): void {
addStringsLoader(platformId, async (lang: string) => platformEng) addStringsLoader(platformId, async (lang: string) => platformEng)
addStringsLoader(analyticsCollectorId, async (lang: string) => analyticsCollectorEn) addStringsLoader(analyticsCollectorId, async (lang: string) => analyticsCollectorEn)
addStringsLoader(calendarId, async (lang: string) => calendarEn)
addStringsLoader(chunterId, async (lang: string) => chunterEn) addStringsLoader(chunterId, async (lang: string) => chunterEn)
addStringsLoader(contactId, async (lang: string) => contactEn) addStringsLoader(contactId, async (lang: string) => contactEn)
addStringsLoader(documentId, async (lang: string) => documentEn) addStringsLoader(documentId, async (lang: string) => documentEn)
addStringsLoader(driveId, async (lang: string) => driveEn) addStringsLoader(driveId, async (lang: string) => driveEn)
addStringsLoader(hrId, async (lang: string) => hrEn) addStringsLoader(hrId, async (lang: string) => hrEn)
addStringsLoader(leadId, async (lang: string) => leadEn) addStringsLoader(leadId, async (lang: string) => leadEn)
addStringsLoader(loveId, async (lang: string) => loveEn)
addStringsLoader(notificationId, async (lang: string) => notificationEn) addStringsLoader(notificationId, async (lang: string) => notificationEn)
addStringsLoader(preferenceId, async (lang: string) => preferenceEn) addStringsLoader(preferenceId, async (lang: string) => preferenceEn)
addStringsLoader(recruitId, async (lang: string) => recruitEn) addStringsLoader(recruitId, async (lang: string) => recruitEn)
@ -71,5 +75,4 @@ export function registerLoaders (): void {
addStringsLoader(trackerId, async (lang: string) => trackerEn) addStringsLoader(trackerId, async (lang: string) => trackerEn)
addStringsLoader(viewId, async (lang: string) => viewEn) addStringsLoader(viewId, async (lang: string) => viewEn)
addStringsLoader(workbenchId, async (lang: string) => workbenchEn) addStringsLoader(workbenchId, async (lang: string) => workbenchEn)
addStringsLoader(loveId, async (lang: string) => loveEn)
} }

View File

@ -245,7 +245,7 @@ export class SupportWsClient extends WorkspaceClient {
const hierarchy = client.getHierarchy() const hierarchy = client.getHierarchy()
for (const event of events) { for (const event of events) {
const markup = await eventToMarkup(event, hierarchy) const markup = await eventToMarkup(event, hierarchy, client)
if (markup === undefined) { if (markup === undefined) {
continue continue

View File

@ -23,8 +23,6 @@ export class WorkspaceClient {
client: Client | undefined client: Client | undefined
opClient: Promise<TxOperations> | TxOperations opClient: Promise<TxOperations> | TxOperations
initializePromise: Promise<void> | undefined = undefined
constructor ( constructor (
readonly ctx: MeasureContext, readonly ctx: MeasureContext,
readonly workspace: WorkspaceId readonly workspace: WorkspaceId