From a30003d17104bd2a46048007dd2026a35d374725 Mon Sep 17 00:00:00 2001 From: Denis Kralj Date: Thu, 18 Jul 2024 10:07:23 +0200 Subject: [PATCH 01/10] fix(web): ensure we wait for loads to happen Fix dark mode detection --- apps/web/tests/utils/browser.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/web/tests/utils/browser.ts b/apps/web/tests/utils/browser.ts index f2fb63bc034..960c72862e9 100644 --- a/apps/web/tests/utils/browser.ts +++ b/apps/web/tests/utils/browser.ts @@ -57,13 +57,14 @@ export async function deleteIndexedDB(page: Page, dbName: string) { } export async function isDarkTheme(page: Page) { + // TODO: there should be a more idiomatic way to find out what theme is selected const backgroundColor = await page.evaluate(() => { const body = document.body; return window.getComputedStyle(body).backgroundColor; }); - return backgroundColor.toLowerCase() !== '#EDF0F2' && backgroundColor.toLowerCase() !== 'rgb(237, 240, 242)'; + return backgroundColor.toLowerCase() === 'rgb(30, 30, 38)'; } export async function getAttByTestId(page: Page, testId: string, att: string) { From ed70eca462d77c8a6b061b65fecfd20ec82ddce7 Mon Sep 17 00:00:00 2001 From: Adam Chmara Date: Thu, 18 Jul 2024 10:11:02 +0200 Subject: [PATCH 02/10] ci(root): add ee clerk e2e tests to dev deploy (#6056) --- .github/workflows/dev-deploy-api.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dev-deploy-api.yml b/.github/workflows/dev-deploy-api.yml index 1b78da2eb7f..dfaa343d841 100644 --- a/.github/workflows/dev-deploy-api.yml +++ b/.github/workflows/dev-deploy-api.yml @@ -22,10 +22,11 @@ jobs: test_api: strategy: matrix: - name: ['novu/api-ee', 'novu/api'] + name: ['novu/api-ee', 'novu/api', 'novu/api-ee-clerk'] uses: ./.github/workflows/reusable-api-e2e.yml with: ee: ${{ contains (matrix.name,'-ee') }} + ee-clerk: ${{ contains (matrix.name,'-ee-clerk') }} job-name: ${{ matrix.name }} secrets: inherit From 573857077f7bb5df24d69e7dcbfcb0d6b142aa52 Mon Sep 17 00:00:00 2001 From: Denis Kralj <168424106+denis-kralj-novu@users.noreply.github.com> Date: Thu, 18 Jul 2024 10:35:56 +0200 Subject: [PATCH 03/10] fix(web): ensure we wait for loads to happen (#6098) --- .../src/components/providers/EnvironmentProvider.tsx | 12 ++++++++---- ...eateContextandHook.ts => createContextAndHook.ts} | 0 2 files changed, 8 insertions(+), 4 deletions(-) rename apps/web/src/components/providers/{createContextandHook.ts => createContextAndHook.ts} (100%) diff --git a/apps/web/src/components/providers/EnvironmentProvider.tsx b/apps/web/src/components/providers/EnvironmentProvider.tsx index 0e2488bceb6..fd9f0494cde 100644 --- a/apps/web/src/components/providers/EnvironmentProvider.tsx +++ b/apps/web/src/components/providers/EnvironmentProvider.tsx @@ -4,7 +4,7 @@ import { useQuery, useQueryClient } from '@tanstack/react-query'; import { IEnvironment } from '@novu/shared'; import { QueryKeys } from '../../api/query.keys'; import { getEnvironments } from '../../api/environment'; -import { createContextAndHook } from './createContextandHook'; +import { createContextAndHook } from './createContextAndHook'; import { IS_DOCKER_HOSTED } from '../../config/index'; import { BaseEnvironmentEnum } from '../../constants/BaseEnvironmentEnum'; import { useAuth } from './AuthProvider'; @@ -69,15 +69,19 @@ function selectEnvironment(environments: IEnvironment[] | undefined | null, sele export function EnvironmentProvider({ children }: { children: React.ReactNode }) { const navigate = useNavigate(); const queryClient = useQueryClient(); - const { currentOrganization } = useAuth(); + const { currentOrganization, isLoading: isLoadingAuth } = useAuth(); + const [internalLoading, setInternalLoading] = useState(true); const { data: environments, - isLoading, + isLoading: isLoadingEnvironments, refetch: refetchEnvironments, } = useQuery([QueryKeys.myEnvironments, currentOrganization?._id], getEnvironments, { enabled: !!currentOrganization, retry: false, staleTime: Infinity, + onSettled: (data, error) => { + setInternalLoading(false); + }, }); const [currentEnvironment, setCurrentEnvironment] = useState( @@ -163,7 +167,7 @@ export function EnvironmentProvider({ children }: { children: React.ReactNode }) switchEnvironment, switchToDevelopmentEnvironment, switchToProductionEnvironment, - isLoading, + isLoading: isLoadingEnvironments || isLoadingAuth || internalLoading, readOnly: currentEnvironment?._parentId !== undefined, }; diff --git a/apps/web/src/components/providers/createContextandHook.ts b/apps/web/src/components/providers/createContextAndHook.ts similarity index 100% rename from apps/web/src/components/providers/createContextandHook.ts rename to apps/web/src/components/providers/createContextAndHook.ts From d83ff00b1b4b2836a4058f91c0bde5e75b05bb03 Mon Sep 17 00:00:00 2001 From: Adam Chmara Date: Thu, 18 Jul 2024 11:01:10 +0200 Subject: [PATCH 04/10] fix(web): add internal id condition to ee switch org callback (#6096) --- apps/web/src/ee/clerk/providers/EnterpriseAuthProvider.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/web/src/ee/clerk/providers/EnterpriseAuthProvider.tsx b/apps/web/src/ee/clerk/providers/EnterpriseAuthProvider.tsx index 1320b32d604..e3b8dd0afd4 100644 --- a/apps/web/src/ee/clerk/providers/EnterpriseAuthProvider.tsx +++ b/apps/web/src/ee/clerk/providers/EnterpriseAuthProvider.tsx @@ -157,7 +157,11 @@ export const EnterpriseAuthProvider = ({ children }: { children: React.ReactNode // refetch queries on organization switch useEffect(() => { - if (organization && organization._id !== clerkOrganization?.id) { + // if linked, externalOrgId = internal org ObjectID, which is required on backend + const isInternalOrgLinked = !!clerkOrganization?.publicMetadata.externalOrgId; + const isOrgChanged = organization && organization._id !== clerkOrganization?.id; + + if (isInternalOrgLinked && isOrgChanged) { switchOrgCallback(); } }, [organization, clerkOrganization, switchOrgCallback]); From a1b3af360c3bc635c13d5c77a220e89b11075edf Mon Sep 17 00:00:00 2001 From: George Desipris <73396808+desiprisg@users.noreply.github.com> Date: Thu, 18 Jul 2024 13:34:37 +0300 Subject: [PATCH 05/10] feat(js): Notification list (#6002) --- packages/js/.eslintrc.cjs | 2 + packages/js/eslint-local-rules.js | 15 ++++- packages/js/src/ui/api/hooks/index.ts | 2 +- packages/js/src/ui/api/hooks/useFeed.ts | 12 ++++ packages/js/src/ui/components/Inbox.tsx | 44 +++++++------ .../Notification/NotificationList.tsx | 61 +++++++++++++++++++ .../Notification/NotificationListSkeleton.tsx | 39 ++++++++++++ .../src/ui/components/Notification/index.ts | 1 + .../ui/components/Settings/LoadingScreen.tsx | 27 -------- .../elements/Header/SettingsHeader.tsx | 4 +- .../elements/Header/StatusDropdown.tsx | 6 +- .../js/src/ui/components/elements/Root.tsx | 2 +- .../{ => elements}/Settings/ChannelRow.tsx | 12 ++-- .../elements/Settings/LoadingScreen.tsx | 30 +++++++++ .../{ => elements}/Settings/Settings.tsx | 13 ++-- .../{ => elements}/Settings/Switch.tsx | 2 +- .../{ => elements}/Settings/index.ts | 0 .../js/src/ui/components/elements/index.ts | 1 + packages/js/src/ui/components/index.ts | 1 - .../src/ui/components/primitives/Skeleton.tsx | 22 +++++++ .../js/src/ui/config/default-localization.ts | 5 -- ...ult-appearance.ts => defaultAppearance.ts} | 0 .../js/src/ui/config/defaultLocalization.ts | 4 ++ packages/js/src/ui/config/index.ts | 2 +- .../js/src/ui/context/AppearanceContext.tsx | 10 ++- .../js/src/ui/context/LocalizationContext.tsx | 8 +-- .../js/src/ui/helpers/createInfiniteScroll.ts | 52 ++++++++++++++++ packages/js/src/ui/helpers/index.ts | 3 +- packages/js/src/ui/icons/Archive.tsx | 2 +- packages/js/src/ui/icons/ArchiveRead.tsx | 2 +- packages/js/src/ui/icons/ArrowLeft.tsx | 2 +- packages/js/src/ui/icons/BellIcon.tsx | 1 - packages/js/src/ui/icons/Chat.tsx | 2 +- packages/js/src/ui/icons/Check.tsx | 2 +- packages/js/src/ui/icons/Email.tsx | 2 +- packages/js/src/ui/icons/EmptyIcon.tsx | 21 +++++++ packages/js/src/ui/icons/InApp.tsx | 2 +- packages/js/src/ui/icons/Inbox.tsx | 2 +- packages/js/src/ui/icons/Push.tsx | 2 +- packages/js/src/ui/icons/ReadAll.tsx | 2 +- packages/js/src/ui/icons/Sms.tsx | 2 +- packages/js/src/ui/icons/Unread.tsx | 2 +- playground/nextjs/.env.example | 4 ++ playground/nextjs/src/pages/index.tsx | 2 + 44 files changed, 332 insertions(+), 100 deletions(-) create mode 100644 packages/js/src/ui/components/Notification/NotificationList.tsx create mode 100644 packages/js/src/ui/components/Notification/NotificationListSkeleton.tsx create mode 100644 packages/js/src/ui/components/Notification/index.ts delete mode 100644 packages/js/src/ui/components/Settings/LoadingScreen.tsx rename packages/js/src/ui/components/{ => elements}/Settings/ChannelRow.tsx (88%) create mode 100644 packages/js/src/ui/components/elements/Settings/LoadingScreen.tsx rename packages/js/src/ui/components/{ => elements}/Settings/Settings.tsx (92%) rename packages/js/src/ui/components/{ => elements}/Settings/Switch.tsx (96%) rename packages/js/src/ui/components/{ => elements}/Settings/index.ts (100%) create mode 100644 packages/js/src/ui/components/primitives/Skeleton.tsx delete mode 100644 packages/js/src/ui/config/default-localization.ts rename packages/js/src/ui/config/{default-appearance.ts => defaultAppearance.ts} (100%) create mode 100644 packages/js/src/ui/config/defaultLocalization.ts create mode 100644 packages/js/src/ui/helpers/createInfiniteScroll.ts create mode 100644 packages/js/src/ui/icons/EmptyIcon.tsx create mode 100644 playground/nextjs/.env.example diff --git a/packages/js/.eslintrc.cjs b/packages/js/.eslintrc.cjs index a38fff6da84..815a32547c0 100644 --- a/packages/js/.eslintrc.cjs +++ b/packages/js/.eslintrc.cjs @@ -13,6 +13,8 @@ module.exports = { }, ], 'local-rules/no-class-without-style': 'error', + 'id-length': 'off', + '@typescript-eslint/no-shadow': 'off', }, parserOptions: { project: './tsconfig.json', diff --git a/packages/js/eslint-local-rules.js b/packages/js/eslint-local-rules.js index a600c0c6de7..8d799755a05 100644 --- a/packages/js/eslint-local-rules.js +++ b/packages/js/eslint-local-rules.js @@ -1,5 +1,5 @@ -module.exports = { - 'no-class-without-style' : { +module.exports = { + 'no-class-without-style': { meta: { type: 'problem', docs: { @@ -14,6 +14,15 @@ module.exports = { return { JSXAttribute(node) { if (node.name && node.name.name === 'class') { + const parentElement = node.parent; + const hasAppearanceKey = parentElement.attributes.some( + (attr) => attr.name && attr.name.name === 'appearanceKey' + ); + + if (hasAppearanceKey) { + return; // Skip reporting if appearanceKey is present, as it is most likely forwarded. + } + const value = context.getSourceCode().getText(node.value); if (!value.includes('style(')) { context.report({ @@ -26,4 +35,4 @@ module.exports = { }; }, }, -} +}; diff --git a/packages/js/src/ui/api/hooks/index.ts b/packages/js/src/ui/api/hooks/index.ts index 2676db6db7a..0dfeb6c4e29 100644 --- a/packages/js/src/ui/api/hooks/index.ts +++ b/packages/js/src/ui/api/hooks/index.ts @@ -1,3 +1,3 @@ export * from './useFeed'; -export * from './useReadAll'; export * from './usePreferences'; +export * from './useReadAll'; diff --git a/packages/js/src/ui/api/hooks/useFeed.ts b/packages/js/src/ui/api/hooks/useFeed.ts index 1819e9f9638..1593232ff2f 100644 --- a/packages/js/src/ui/api/hooks/useFeed.ts +++ b/packages/js/src/ui/api/hooks/useFeed.ts @@ -1,6 +1,7 @@ import { createEffect, createSignal } from 'solid-js'; import { FetchFeedArgs, FetchFeedResponse, Notification } from '../../../feeds'; import { useNovu } from '../../context'; +import { createInfiniteScroll } from '../../helpers'; export const useFeed = (props: { options: FetchFeedArgs; @@ -33,3 +34,14 @@ export const useFeed = (props: { return { feed, fetchFeed, hasMore: hasMore }; }; + +type UseFeedInfiniteScrollProps = { + options?: Exclude; +}; +export const useFeedInfiniteScroll = (props?: UseFeedInfiniteScrollProps) => { + const novu = useNovu(); + + return createInfiniteScroll(async (offset) => { + return await novu.feeds.fetch({ ...(props?.options || {}), offset }); + }); +}; diff --git a/packages/js/src/ui/components/Inbox.tsx b/packages/js/src/ui/components/Inbox.tsx index e34735e48e1..7571d879320 100644 --- a/packages/js/src/ui/components/Inbox.tsx +++ b/packages/js/src/ui/components/Inbox.tsx @@ -1,18 +1,39 @@ import { createSignal, JSX, Match, Switch } from 'solid-js'; -import { useLocalization } from '../context'; import { useStyle } from '../helpers'; -import { Bell, Footer, Header, SettingsHeader } from './elements'; +import { Bell, Footer, Header, Settings, SettingsHeader } from './elements'; +import { NotificationList } from './Notification'; import { Button, Popover } from './primitives'; -import { Settings } from './Settings'; type InboxProps = { open?: boolean; renderBell?: ({ unreadCount }: { unreadCount: number }) => JSX.Element; }; +enum Screen { + Inbox = 'inbox', + Settings = 'settings', +} +const InboxContent = () => { + const [currentScreen, setCurrentScreen] = createSignal(Screen.Inbox); + + return ( + <> + + +
+ + + + setCurrentScreen(Screen.Inbox)} /> + + + +