Skip to content

Commit

Permalink
Enabling notifications (#2490)
Browse files Browse the repository at this point in the history
* chore(): Update image

* feat(): update settings page for notifications - disabled state (wip)

* feat(): update notifications settings images, texts, card component

* feat: update notifications sdk init, make sdk call

* feat: call sdk for enabling browser notifications

* feat: update handling of permission state

* feat: hide option from settings

* feat: check state of notifications

* chore: remove unused variable

* feat: update notifications welcome screen to match new design

* feat: handle browser notifications rejection

* chore: revert yarn lock

* feat: replace hook, add optional property to card componend
  • Loading branch information
kunstefix authored Nov 19, 2024
1 parent 9471df8 commit 7797fae
Show file tree
Hide file tree
Showing 11 changed files with 239 additions and 148 deletions.
107 changes: 29 additions & 78 deletions extensions/apps/notifications/src/components/pages/welcome-page.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,30 @@
import React, { useRef } from 'react';
import React, { useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useRootComponentProps, useSaveSettings, useAkashaStore } from '@akashaorg/ui-awf-hooks';
import { NotificationEvents, NotificationTypes } from '@akashaorg/typings/lib/ui';
import { useRootComponentProps, useAkashaStore } from '@akashaorg/ui-awf-hooks';
import Button from '@akashaorg/design-system-core/lib/components/Button';
import Card from '@akashaorg/design-system-core/lib/components/Card';
import ErrorLoader from '@akashaorg/design-system-core/lib/components/ErrorLoader';
import Image from '@akashaorg/design-system-core/lib/components/Image';
import NotificationSettingsCard from '@akashaorg/design-system-components/lib/components/NotificationSettingsCard';
import getSDK from '@akashaorg/core-sdk';
import Stack from '@akashaorg/design-system-core/lib/components/Stack';
import Text from '@akashaorg/design-system-core/lib/components/Text';
import routes, { CUSTOMISE_NOTIFICATION_OPTIONS_PAGE, SHOW_NOTIFICATIONS_PAGE } from '../../routes';

import { useNavigate } from '@tanstack/react-router';

export type WelcomePageProps = {
finalStep?: boolean;
};

const WelcomePage: React.FC<WelcomePageProps> = props => {
const { finalStep = false } = props;

const navigate = useNavigate();
const { baseRouteName, name: appName, uiEvents, getCorePlugins } = useRootComponentProps();
const WelcomePage: React.FC<WelcomePageProps> = () => {
const sdk = getSDK();
const notificationsEnabled = useMemo(
() => sdk.services.common.notification.checkIfNotificationsEnabled(),
[sdk.services.common.notification],
);
const { baseRouteName, getCorePlugins } = useRootComponentProps();
const {
data: { authenticatedProfile },
} = useAkashaStore();
const _uiEvents = useRef(uiEvents);
const { t } = useTranslation('app-notifications');
const navigateTo = getCorePlugins().routing.navigateTo;

const { saveNotificationSettings } = useSaveSettings();

const handleConnectButtonClick = () => {
navigateTo?.({
appName: '@akashaorg/app-auth-ewa',
Expand All @@ -41,43 +36,13 @@ const WelcomePage: React.FC<WelcomePageProps> = props => {
});
};

const confirmCustomization = () => {
if (finalStep) {
_uiEvents.current.next({
event: NotificationEvents.ShowNotification,
data: {
type: NotificationTypes.Success,
title: t('Notification settings updated successfully'),
},
});

navigate({ to: routes[SHOW_NOTIFICATIONS_PAGE] });
} else {
// navigate to next step
navigate({ to: routes[CUSTOMISE_NOTIFICATION_OPTIONS_PAGE] });
}
};

const skipCustomization = () => {
saveNotificationSettings(
{ app: appName, options: { default: true } },
{ onComplete: () => navigate({ to: routes[SHOW_NOTIFICATIONS_PAGE] }) },
);
const handleGoToNotificationsSettings = () => {
navigateTo?.({
appName: '@akashaorg/app-settings-ewa',
getNavigationUrl: navRoutes => navRoutes['Notifications'],
});
};

const header = finalStep ? t('All Done') : t('Welcome to the Notifications App');
const description = finalStep
? t(
'You will receive notifications based on your choices now! You can always change that or even pause it from the notifications settings!',
)
: t(
`Get the latest updates about what's going on with your world. You can personalize your notifications and get only what you want to see!`,
);

const rightButtonLabel = finalStep
? t('Go to my notifications')
: t('Customize my notifications');

if (!authenticatedProfile?.did.id)
return (
<ErrorLoader
Expand All @@ -95,33 +60,19 @@ const WelcomePage: React.FC<WelcomePageProps> = props => {
</ErrorLoader>
);

return (
<Card radius={16} padding={'p-2'} dataTestId="notifications">
<Stack justify="center" align="center" customStyle="mb-32">
<Image
src={`/images/${finalStep ? 'notificationapp-success-min' : 'not-authenticated'}.webp`}
customStyle="w-[180px] h-[180px] m-auto my-4"
/>

<Text variant={finalStep ? 'h5' : 'h6'} align="center">
{header}
</Text>
<Text variant="footnotes2" align="center" color={{ light: 'black', dark: 'grey6' }}>
{description}
</Text>
</Stack>
<Stack direction="row" fullWidth justify="end" spacing="gap-x-4" customStyle="pr-2 pb-2">
{!finalStep && (
<Button
variant="text"
label={t('Skip this step')}
color="secondaryLight dark:secondaryDark"
onClick={skipCustomization}
/>
)}
<Button variant="primary" label={rightButtonLabel} onClick={confirmCustomization} />
</Stack>
</Card>
return notificationsEnabled ? (
<Stack padding="p-4">
<Text>TODO - Notifications show up here</Text>
</Stack>
) : (
<NotificationSettingsCard
image={'notificationsDefault'}
isLoading={false}
handleButtonClick={handleGoToNotificationsSettings}
text={t('Receive personalised updates and community news.')}
title={t('Turn on in-app notifications')}
buttonLabel={t('Go to Settings')}
/>
);
};
export default WelcomePage;
Original file line number Diff line number Diff line change
@@ -1,14 +1,31 @@
import React from 'react';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import Text from '@akashaorg/design-system-core/lib/components/Text';
import PageLayout from './base-layout';
import { useAkashaStore, useRootComponentProps } from '@akashaorg/ui-awf-hooks';
import Stack from '@akashaorg/design-system-core/lib/components/Stack';
import ErrorLoader from '@akashaorg/design-system-core/lib/components/ErrorLoader';
import Button from '@akashaorg/design-system-core/lib/components/Button';
import appRoutes, { BROWSER_NOTIFICATIONS } from '../../routes';
import NotificationSettingsCard, {
NotificationsImageSrc,
} from '@akashaorg/design-system-components/lib/components/NotificationSettingsCard';
import getSDK from '@akashaorg/core-sdk';

type StatesContents = {
[key in NotificationPermission]: {
title: string;
description: string;
image: NotificationsImageSrc;
};
};

const BrowserNotificationsOption: React.FC = () => {
const sdk = getSDK();
const [loading, setLoading] = useState(false);
const [permission, setPermission] = useState<NotificationPermission>(
'Notification' in window ? Notification.permission : 'default',
);

const { baseRouteName, getCorePlugins } = useRootComponentProps();
const navigateTo = getCorePlugins().routing.navigateTo;
const { t } = useTranslation('app-settings-ewa');
Expand All @@ -17,6 +34,24 @@ const BrowserNotificationsOption: React.FC = () => {
} = useAkashaStore();
const isLoggedIn = !!authenticatedDID;

const STATES_CONTENT: StatesContents = {
default: {
title: t('Turn on browser notifications'),
description: t('You will see a browser prompt to allow notifications'),
image: 'browserDefault',
},
granted: {
title: t('Browser notifications Enabled'),
description: t('You can disable them from your browser’s settings at any time.'),
image: 'browserEnabled',
},
denied: {
title: t('Browser notifications disabled'),
description: t('You can enable them from your browser’s settings at any time.'),
image: 'browserDisabled',
},
};

const handleConnectButtonClick = () => {
navigateTo?.({
appName: '@akashaorg/app-auth-ewa',
Expand Down Expand Up @@ -47,9 +82,32 @@ const BrowserNotificationsOption: React.FC = () => {
);
}

const handleEnableBrowserNotifications = async () => {
setLoading(true);
try {
await sdk.services.common.notification.listenToNotificationEvents();
} catch (error) {
console.warn('User rejected browser notifications');
} finally {
setPermission(Notification.permission);
setLoading(false);
}
};

return (
<PageLayout title={t('Browser notifications')}>
<Text>TODO - add content</Text>
<Stack padding="p-4">
<NotificationSettingsCard
noWrapperCard={true}
isLoading={loading}
handleButtonClick={handleEnableBrowserNotifications}
text={STATES_CONTENT[permission].description}
title={STATES_CONTENT[permission].title}
image={STATES_CONTENT[permission].image}
showButton={permission === 'default'}
buttonLabel={t('Turn on')}
/>
</Stack>
</PageLayout>
);
};
Expand Down
124 changes: 92 additions & 32 deletions extensions/apps/settings/src/components/pages/notifications-option.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useRef } from 'react';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import Stack from '@akashaorg/design-system-core/lib/components/Stack';
import Text from '@akashaorg/design-system-core/lib/components/Text';
Expand All @@ -9,7 +9,10 @@ import Icon from '@akashaorg/design-system-core/lib/components/Icon';
import { ChevronRightIcon } from '@heroicons/react/24/outline';
import { ISettingsItem, SettingsOption } from '../../utils/settings-items';
import ErrorLoader from '@akashaorg/design-system-core/lib/components/ErrorLoader';
import NotificationSettingsCard from '@akashaorg/design-system-components/lib/components/NotificationSettingsCard';
import appRoutes, { NOTIFICATIONS } from '../../routes';
import { NotificationEvents, NotificationTypes } from '@akashaorg/typings/lib/ui';
import getSDK from '@akashaorg/core-sdk';

const notificationsSettingsItems: ISettingsItem[] = [
{
Expand All @@ -23,14 +26,32 @@ const notificationsSettingsItems: ISettingsItem[] = [
];

const NotificationsOption: React.FC = () => {
const { baseRouteName, getCorePlugins } = useRootComponentProps();
const navigateTo = getCorePlugins().routing.navigateTo;
const sdk = getSDK();
const [notificationsEnabled, setNotificationsEnabled] = useState(() =>
sdk.services.common.notification.checkIfNotificationsEnabled(),
);
const [loading, setLoading] = useState(false);
const { baseRouteName, uiEvents, getCorePlugins } = useRootComponentProps();
const { t } = useTranslation('app-settings-ewa');
const _uiEvents = React.useRef(uiEvents);
const navigateTo = getCorePlugins().routing.navigateTo;

const {
data: { authenticatedDID, isAuthenticating },
} = useAkashaStore();
const isLoggedIn = !!authenticatedDID;

const TOAST_TEXTS = {
success: {
title: t('In-app notifications enabled'),
description: t('Notifications for all default apps are enabled. Manage them in preferences.'),
},
error: {
title: t('Couldn’t enable notifications'),
description: t('Signature verification failed. Please try again.'),
},
};

const handleSettingsOptionClick = (option: SettingsOption) => () => {
return getCorePlugins().routing.navigateTo?.({
appName: '@akashaorg/app-settings-ewa',
Expand Down Expand Up @@ -68,39 +89,78 @@ const NotificationsOption: React.FC = () => {
);
}

const handleEnableNotifications = async () => {
setLoading(true);
let result: keyof typeof TOAST_TEXTS;
try {
await sdk.services.common.notification.initialize({ readonly: false });
result = 'success';
setNotificationsEnabled(true);
} catch (error) {
result = 'error';
setNotificationsEnabled(false);
} finally {
setLoading(false);
}

_uiEvents.current.next({
event: NotificationEvents.ShowNotification,
data: {
type: result == 'success' ? NotificationTypes.Success : NotificationTypes.Error,
title: TOAST_TEXTS[result].title,
description: TOAST_TEXTS[result].description,
},
});
};

return (
<PageLayout title={t('Notifications Settings')}>
<Stack padding="p-4">
{notificationsSettingsItems.map((item: ISettingsItem, idx: number) => {
const baseStyle = `flex py-4 justify-between items-center ${
idx !== notificationsSettingsItems.length - 1
? 'border(b-1 solid grey8 dark:grey5)'
: 'border-none'
}`;
{!notificationsEnabled && (
<Stack padding="p-4">
<NotificationSettingsCard
image={'notificationsDefault'}
isLoading={loading}
noWrapperCard={true}
handleButtonClick={handleEnableNotifications}
text={t('You’ll be prompted with 1 signature')}
title={t('Turn on in-app notifications')}
buttonLabel={t('Turn on')}
/>
</Stack>
)}
{notificationsEnabled && (
<Stack padding="p-4">
{notificationsSettingsItems.map((item: ISettingsItem, idx: number) => {
const baseStyle = `flex py-4 justify-between items-center ${
idx !== notificationsSettingsItems.length - 1
? 'border(b-1 solid grey8 dark:grey5)'
: 'border-none'
}`;

const children = (
<>
<Text>{`${t('{{itemLabel}}', { itemLabel: item.label as string })}`}</Text>
{!item.isSubheading && <Icon icon={<ChevronRightIcon />} accentColor={true} />}
</>
);
const children = (
<>
<Text>{`${t('{{itemLabel}}', { itemLabel: item.label as string })}`}</Text>
{!item.isSubheading && <Icon icon={<ChevronRightIcon />} accentColor={true} />}
</>
);

return (
<React.Fragment key={`${item.label}`}>
{item.clickable && (
<Button
plain={true}
customStyle={`w-full ${baseStyle}`}
onClick={handleSettingsOptionClick(item.label)}
>
{children}
</Button>
)}
{!item.clickable && <Stack customStyle={baseStyle}>{children}</Stack>}
</React.Fragment>
);
})}
</Stack>
return (
<React.Fragment key={`${item.label}`}>
{item.clickable && (
<Button
plain={true}
customStyle={`w-full ${baseStyle}`}
onClick={handleSettingsOptionClick(item.label)}
>
{children}
</Button>
)}
{!item.clickable && <Stack customStyle={baseStyle}>{children}</Stack>}
</React.Fragment>
);
})}
</Stack>
)}
</PageLayout>
);
};
Expand Down
Loading

0 comments on commit 7797fae

Please sign in to comment.