Skip to content

Commit

Permalink
feat: product analytic UI (#4262)
Browse files Browse the repository at this point in the history
* chore: initial ui prompt product analytic

* chore: integrate posthog

* chore: update event app_version

* chore: update build env

* feat: posthog config in ci

* chore: resolve linter test CI

* chore: resolve e2e

* chore: disable capture unless property

* chore: update e2e

* chore: update privacy data analytic

---------

Co-authored-by: Hien To <[email protected]>
  • Loading branch information
urmauur and hiento09 authored Dec 16, 2024
1 parent 5041651 commit 5fc04e0
Show file tree
Hide file tree
Showing 18 changed files with 456 additions and 88 deletions.
14 changes: 10 additions & 4 deletions .github/workflows/template-build-linux-x64.yml
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,10 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
AWS_ACCESS_KEY_ID: ${{ secrets.DELTA_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.DELTA_AWS_SECRET_ACCESS_KEY }}
AWS_EC2_METADATA_DISABLED: "true"
AWS_MAX_ATTEMPTS: "5"
AWS_EC2_METADATA_DISABLED: 'true'
AWS_MAX_ATTEMPTS: '5'
POSTHOG_KEY: ${{ secrets.POSTHOG_KEY }}
POSTHOG_HOST: ${{ secrets.POSTHOG_HOST }}

- name: Build and publish app to github
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') && inputs.public_provider == 'github' && inputs.beta == false
Expand All @@ -122,6 +124,8 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ANALYTICS_ID: ${{ secrets.JAN_APP_UMAMI_PROJECT_API_KEY }}
ANALYTICS_HOST: ${{ secrets.JAN_APP_UMAMI_URL }}
POSTHOG_KEY: ${{ secrets.POSTHOG_KEY }}
POSTHOG_HOST: ${{ secrets.POSTHOG_HOST }}

- name: Build and publish app to github
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') && inputs.public_provider == 'github' && inputs.beta == true
Expand All @@ -131,8 +135,10 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
AWS_ACCESS_KEY_ID: ${{ secrets.DELTA_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.DELTA_AWS_SECRET_ACCESS_KEY }}
AWS_EC2_METADATA_DISABLED: "true"
AWS_MAX_ATTEMPTS: "5"
AWS_EC2_METADATA_DISABLED: 'true'
AWS_MAX_ATTEMPTS: '5'
POSTHOG_KEY: ${{ secrets.POSTHOG_KEY }}
POSTHOG_HOST: ${{ secrets.POSTHOG_HOST }}

- name: Upload Artifact .deb file
if: inputs.public_provider != 'github'
Expand Down
32 changes: 19 additions & 13 deletions .github/workflows/template-build-macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -140,53 +140,59 @@ jobs:
fi
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CSC_LINK: "/tmp/codesign.p12"
CSC_LINK: '/tmp/codesign.p12'
CSC_KEY_PASSWORD: ${{ secrets.CODE_SIGN_P12_PASSWORD }}
CSC_IDENTITY_AUTO_DISCOVERY: "true"
CSC_IDENTITY_AUTO_DISCOVERY: 'true'
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
APP_PATH: "."
APP_PATH: '.'
DEVELOPER_ID: ${{ secrets.DEVELOPER_ID }}
AWS_ACCESS_KEY_ID: ${{ secrets.DELTA_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.DELTA_AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: auto
AWS_EC2_METADATA_DISABLED: "true"
AWS_MAX_ATTEMPTS: "5"
AWS_EC2_METADATA_DISABLED: 'true'
AWS_MAX_ATTEMPTS: '5'
POSTHOG_KEY: ${{ secrets.POSTHOG_KEY }}
POSTHOG_HOST: ${{ secrets.POSTHOG_HOST }}

- name: Build and publish app to github
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') && inputs.public_provider == 'github' && inputs.beta == false
run: |
make build-and-publish
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CSC_LINK: "/tmp/codesign.p12"
CSC_LINK: '/tmp/codesign.p12'
CSC_KEY_PASSWORD: ${{ secrets.CODE_SIGN_P12_PASSWORD }}
CSC_IDENTITY_AUTO_DISCOVERY: "true"
CSC_IDENTITY_AUTO_DISCOVERY: 'true'
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
APP_PATH: "."
APP_PATH: '.'
DEVELOPER_ID: ${{ secrets.DEVELOPER_ID }}
ANALYTICS_ID: ${{ secrets.JAN_APP_UMAMI_PROJECT_API_KEY }}
ANALYTICS_HOST: ${{ secrets.JAN_APP_UMAMI_URL }}
POSTHOG_KEY: ${{ secrets.POSTHOG_KEY }}
POSTHOG_HOST: ${{ secrets.POSTHOG_HOST }}

- name: Build and publish app to github
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') && inputs.public_provider == 'github' && inputs.beta == true
run: |
make build-and-publish
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CSC_LINK: "/tmp/codesign.p12"
CSC_LINK: '/tmp/codesign.p12'
CSC_KEY_PASSWORD: ${{ secrets.CODE_SIGN_P12_PASSWORD }}
CSC_IDENTITY_AUTO_DISCOVERY: "true"
CSC_IDENTITY_AUTO_DISCOVERY: 'true'
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
APP_PATH: "."
APP_PATH: '.'
DEVELOPER_ID: ${{ secrets.DEVELOPER_ID }}
AWS_ACCESS_KEY_ID: ${{ secrets.DELTA_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.DELTA_AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: auto
AWS_EC2_METADATA_DISABLED: "true"
AWS_MAX_ATTEMPTS: "5"
AWS_EC2_METADATA_DISABLED: 'true'
AWS_MAX_ATTEMPTS: '5'
POSTHOG_KEY: ${{ secrets.POSTHOG_KEY }}
POSTHOG_HOST: ${{ secrets.POSTHOG_HOST }}

- name: Upload Artifact
if: inputs.public_provider != 'github'
Expand Down
15 changes: 10 additions & 5 deletions .github/workflows/template-build-windows-x64.yml
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,10 @@ jobs:
AWS_ACCESS_KEY_ID: ${{ secrets.DELTA_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.DELTA_AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: auto
AWS_EC2_METADATA_DISABLED: "true"
AWS_MAX_ATTEMPTS: "5"
AWS_EC2_METADATA_DISABLED: 'true'
AWS_MAX_ATTEMPTS: '5'
POSTHOG_KEY: ${{ secrets.POSTHOG_KEY }}
POSTHOG_HOST: ${{ secrets.POSTHOG_HOST }}

- name: Build app and publish app to github
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') && inputs.public_provider == 'github' && inputs.beta == false
Expand All @@ -165,6 +167,8 @@ jobs:
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
AZURE_CERT_NAME: homebrewltd
POSTHOG_KEY: ${{ secrets.POSTHOG_KEY }}
POSTHOG_HOST: ${{ secrets.POSTHOG_HOST }}

- name: Build app and publish app to github
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') && inputs.public_provider == 'github' && inputs.beta == true
Expand All @@ -175,19 +179,20 @@ jobs:
AWS_ACCESS_KEY_ID: ${{ secrets.DELTA_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.DELTA_AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: auto
AWS_EC2_METADATA_DISABLED: "true"
AWS_MAX_ATTEMPTS: "5"
AWS_EC2_METADATA_DISABLED: 'true'
AWS_MAX_ATTEMPTS: '5'
AZURE_KEY_VAULT_URI: ${{ secrets.AZURE_KEY_VAULT_URI }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
# AZURE_CERT_NAME: ${{ secrets.AZURE_CERT_NAME }}
AZURE_CERT_NAME: homebrewltd
POSTHOG_KEY: ${{ secrets.POSTHOG_KEY }}
POSTHOG_HOST: ${{ secrets.POSTHOG_HOST }}

- name: Upload Artifact
if: inputs.public_provider != 'github'
uses: actions/upload-artifact@v4
with:
name: jan-win-x64-${{ inputs.new_version }}
path: ./electron/dist/*.exe

13 changes: 7 additions & 6 deletions electron/tests/e2e/thread.e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@ test('Select GPT model from Hub and Chat with Invalid API Key', async ({

await page.getByTestId('txt-input-chat').fill('dummy value')

await page.getByTestId('btn-send-chat').click()
const denyButton = page.locator('[data-testid="btn-deny-product-analytics"]')

if ((await denyButton.count()) > 0) {
await denyButton.click({ force: true })
} else {
await page.getByTestId('btn-send-chat').click({ force: true })
}

await page.waitForFunction(
() => {
Expand All @@ -24,9 +30,4 @@ test('Select GPT model from Hub and Chat with Invalid API Key', async ({
},
{ timeout: TIMEOUT }
)

const APIKeyError = page.getByTestId('passthrough-error-message')
await expect(APIKeyError).toBeVisible({
timeout: TIMEOUT,
})
})
2 changes: 1 addition & 1 deletion web/containers/Layout/BottomPanel/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const BottomPanel = () => {
return (
<div
className={twMerge(
'fixed bottom-0 left-0 z-50 flex h-9 w-full items-center justify-between px-3 text-xs',
'fixed bottom-0 left-0 z-40 flex h-9 w-full items-center justify-between px-3 text-xs',
reduceTransparent &&
'border-t border-[hsla(var(--app-border))] bg-[hsla(var(--bottom-panel-bg))]'
)}
Expand Down
152 changes: 149 additions & 3 deletions web/containers/Layout/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
'use client'

import { useEffect } from 'react'
import { useEffect, useState } from 'react'

import { useAtomValue, useSetAtom } from 'jotai'
import { Button } from '@janhq/joi'
import { useAtom, useAtomValue, useSetAtom } from 'jotai'

import posthog from 'posthog-js'
import { twMerge } from 'tailwind-merge'

import BottomPanel from '@/containers/Layout/BottomPanel'
Expand Down Expand Up @@ -31,12 +33,72 @@ import MainViewContainer from '../MainViewContainer'
import InstallingExtensionModal from './BottomPanel/InstallingExtension/InstallingExtensionModal'

import { mainViewStateAtom } from '@/helpers/atoms/App.atom'
import { reduceTransparentAtom } from '@/helpers/atoms/Setting.atom'
import {
productAnalyticAtom,
productAnalyticPromptAtom,
reduceTransparentAtom,
} from '@/helpers/atoms/Setting.atom'

const BaseLayout = () => {
const setMainViewState = useSetAtom(mainViewStateAtom)
const importModelStage = useAtomValue(getImportModelStageAtom)
const reduceTransparent = useAtomValue(reduceTransparentAtom)
const [productAnalytic, setProductAnalytic] = useAtom(productAnalyticAtom)
const [productAnalyticPrompt, setProductAnalyticPrompt] = useAtom(
productAnalyticPromptAtom
)
const [showProductAnalyticPrompt, setShowProductAnalyticPrompt] =
useState(false)

useEffect(() => {
const timer = setTimeout(() => {
if (productAnalyticPrompt) {
setShowProductAnalyticPrompt(true)
}
return () => clearTimeout(timer)
}, 3000) // 3 seconds delay

return () => clearTimeout(timer) // Cleanup timer on unmount
}, [productAnalyticPrompt])

useEffect(() => {
if (productAnalytic) {
posthog.init(POSTHOG_KEY, {
api_host: POSTHOG_HOST,
autocapture: false,
capture_pageview: false,
capture_pageleave: false,
disable_session_recording: true,
person_profiles: 'always',
persistence: 'localStorage',
opt_out_capturing_by_default: true,
// eslint-disable-next-line @typescript-eslint/naming-convention
sanitize_properties: function (properties) {
const denylist = [
'$pathname',
'$initial_pathname',
'$current_url',
'$initial_current_url',
'$host',
'$initial_host',
'$initial_person_info',
]

denylist.forEach((key) => {
if (properties[key]) {
properties[key] = null // Set each denied property to null
}
})

return properties
},
})
posthog.opt_in_capturing()
posthog.register({ app_version: VERSION })
} else {
posthog.opt_out_capturing()
}
}, [productAnalytic])

useEffect(() => {
if (localStorage.getItem(SUCCESS_SET_NEW_DESTINATION) === 'true') {
Expand All @@ -54,6 +116,17 @@ const BaseLayout = () => {
)
}, [setMainViewState])

const handleProductAnalytics = (isAllowed: boolean) => {
setProductAnalytic(isAllowed)
setProductAnalyticPrompt(false)
setShowProductAnalyticPrompt(false)
if (isAllowed) {
posthog.opt_in_capturing()
} else {
posthog.opt_out_capturing()
}
}

return (
<div
className={twMerge(
Expand All @@ -76,6 +149,79 @@ const BaseLayout = () => {
<ChooseWhatToImportModal />
<InstallingExtensionModal />
<HuggingFaceRepoDetailModal />
{showProductAnalyticPrompt && (
<div className="fixed bottom-4 z-50 m-4 max-w-full rounded-xl border border-[hsla(var(--app-border))] bg-[hsla(var(--app-bg))] p-6 shadow-2xl sm:bottom-8 sm:right-4 sm:m-0 sm:max-w-[400px]">
<div className="mb-4 flex items-center gap-x-2">
<svg
width="32"
height="32"
viewBox="0 0 32 32"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5.5 12.5C5.5 11.1193 6.61929 10 8 10H24C25.3807 10 26.5 11.1193 26.5 12.5V18.5C26.5 24.299 21.799 29 16 29C10.201 29 5.5 24.299 5.5 18.5V12.5Z"
fill="#2563EB"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M8.20959 25.54L12.0093 10H14.0093L9.84984 27.0113C9.25274 26.579 8.70292 26.0855 8.20959 25.54ZM11.5993 28.0361C11.2955 27.8957 10.9996 27.7412 10.7124 27.5734L15.0093 10H16.0093L11.5993 28.0361Z"
fill="white"
/>
<path
d="M21 8C21 6.67392 20.4732 5.40215 19.5355 4.46447C18.5979 3.52678 17.3261 3 16 3C14.6739 3 13.4021 3.52678 12.4645 4.46447C11.5268 5.40215 11 6.67392 11 8"
stroke="#2563EB"
strokeWidth="2"
strokeLinecap="round"
/>
<path
d="M27.0478 18.054C27.609 18.5733 27.609 19.4267 27.0478 19.946C25.221 21.6363 20.9622 25 16 25C11.0378 25 6.77899 21.6363 4.95219 19.946C4.39099 19.4267 4.39099 18.5733 4.95219 18.054C6.77899 16.3637 11.0378 13 16 13C20.9622 13 25.221 16.3637 27.0478 18.054Z"
fill="#C8D1EA"
/>
<circle cx="16" cy="19" r="4" fill="#2563EB" />
<path
d="M19.25 17.5C19.9404 17.5 20.5 16.9404 20.5 16.25C20.5 15.5596 19.9404 15 19.25 15C18.5596 15 18 15.5596 18 16.25C18 16.9404 18.5596 17.5 19.25 17.5Z"
fill="white"
/>
<path
d="M17.75 18.5C18.1642 18.5 18.5 18.1642 18.5 17.75C18.5 17.3358 18.1642 17 17.75 17C17.3358 17 17 17.3358 17 17.75C17 18.1642 17.3358 18.5 17.75 18.5Z"
fill="white"
/>
</svg>

<h6 className="text-base font-semibold">Help Us Improve Jan</h6>
</div>
<p className="text-[hsla(var(--text-secondary))]">
To improve Jan, we collect anonymous data to understand feature
usage. Your chats and personal information are never tracked. You
can change this anytime in&nbsp;
<span className="font-semibold">{`Settings > Privacy.`}</span>
</p>
<p className="mt-6 text-[hsla(var(--text-secondary))]">
Would you like to help us to improve Jan?
</p>
<div className="mt-6 flex items-center gap-x-2">
<Button
onClick={() => {
handleProductAnalytics(true)
}}
>
Allow
</Button>
<Button
data-testid="btn-deny-product-analytics"
theme="ghost"
variant="outline"
onClick={() => {
handleProductAnalytics(false)
}}
>
Deny
</Button>
</div>
</div>
)}
</div>
<BottomPanel />
</div>
Expand Down
2 changes: 1 addition & 1 deletion web/containers/LeftPanelContainer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ const LeftPanelContainer = ({ children }: Props) => {
<Fragment>
<div
className={twMerge(
'group/resize absolute right-0 top-0 z-[9999] h-full w-1 flex-shrink-0 flex-grow-0 resize-x blur-sm hover:cursor-col-resize hover:bg-[hsla(var(--resize-bg))]',
'group/resize absolute right-0 top-0 z-40 h-full w-1 flex-shrink-0 flex-grow-0 resize-x blur-sm hover:cursor-col-resize hover:bg-[hsla(var(--resize-bg))]',
isResizing && 'cursor-col-resize bg-[hsla(var(--resize-bg))]',
!reduceTransparent && 'shadow-sm'
)}
Expand Down
2 changes: 1 addition & 1 deletion web/containers/RightPanelContainer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ const RightPanelContainer = ({ children }: Props) => {
<Fragment>
<div
className={twMerge(
'group/resize absolute left-0 top-0 z-[9999] h-full w-1 flex-shrink-0 flex-grow-0 resize-x blur-sm hover:cursor-col-resize hover:bg-[hsla(var(--resize-bg))]',
'group/resize absolute left-0 top-0 z-40 h-full w-1 flex-shrink-0 flex-grow-0 resize-x blur-sm hover:cursor-col-resize hover:bg-[hsla(var(--resize-bg))]',
isResizing && 'cursor-col-resize bg-[hsla(var(--resize-bg))]',
!reduceTransparent && 'shadow-sm'
)}
Expand Down
Loading

0 comments on commit 5fc04e0

Please sign in to comment.