Skip to content

Commit

Permalink
Merge pull request #9 from HORNET-Storage/feature/install-pwa
Browse files Browse the repository at this point in the history
feat: install pwa
  • Loading branch information
f7f376a1fcd0d0e11a10ed1b6577c9 authored Jul 15, 2024
2 parents e2c535d + c0aa326 commit 78fd733
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 46 deletions.
2 changes: 1 addition & 1 deletion public/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"sizes": "512x512"
}
],
"start_url": ".",
"start_url": "/",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,28 @@ import { NightModeSettings } from '../nightModeSettings/NightModeSettings';
import { ThemePicker } from '../ThemePicker/ThemePicker';
import { BaseButton } from '@app/components/common/BaseButton/BaseButton';
import { useAppSelector } from '@app/hooks/reduxHooks';
import { useAppDispatch } from '@app/hooks/reduxHooks';
import { setShowAlert } from '@app/store/slices/pwaSlice';
import * as S from './SettingsOverlay.styles';
import { Alert } from 'antd';

export const SettingsOverlay: React.FC = ({ ...props }) => {
const { t } = useTranslation();
const { isPWASupported } = useAppSelector((state) => state.pwa);
const [promptEvent, setPromptEvent] = useState<Event | null>(null);

useEffect(() => {
const handler = (e: Event) => {
e.preventDefault();
console.log('beforeinstallprompt event fired');
setPromptEvent(e as any);
};

window.addEventListener('beforeinstallprompt', handler as EventListener);

return () => {
window.removeEventListener('beforeinstallprompt', handler as EventListener);
};
}, []);

useEffect(() => {
if (promptEvent) {
console.log('Prompt event set:', promptEvent);

const dispatch = useAppDispatch();
const { isPWASupported, event } = useAppSelector((state) => state.pwa);

const handleInstallClick = () => {
console.log('event', event);
if (event == null) {
// Display an alert if event is not available
console.log('Event is not available');
dispatch(setShowAlert(true));
return;
}
}, [promptEvent]);

(event as BeforeInstallPromptEvent).prompt();
};
return (
<S.SettingsOverlayMenu {...props}>
<DropdownCollapse bordered={false} expandIconPosition="end" ghost defaultActiveKey="themePicker">
Expand All @@ -46,9 +41,9 @@ export const SettingsOverlay: React.FC = ({ ...props }) => {
<S.Text>
<Link to="/logout">{t('header.logout')}</Link>
</S.Text>
{isPWASupported && promptEvent && (
{isPWASupported && (
<S.PwaInstallWrapper>
<BaseButton block type="primary" onClick={() => (promptEvent as any).prompt()}>
<BaseButton block type="primary" onClick={handleInstallClick}>
{t('common.pwa')}
</BaseButton>
</S.PwaInstallWrapper>
Expand Down
35 changes: 35 additions & 0 deletions src/components/layouts/main/MainLayout/MainLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import MainSider from '../sider/MainSider/MainSider';
import MainContent from '../MainContent/MainContent';
import { MainHeader } from '../MainHeader/MainHeader';
import * as S from './MainLayout.styles';
import { setShowAlert } from '@app/store/slices/pwaSlice';

import { Outlet, useLocation, useNavigate } from 'react-router-dom';
import {
MEDICAL_DASHBOARD_PATH,
Expand All @@ -16,15 +18,21 @@ import { References } from '@app/components/common/References/References';
import { useAppDispatch } from '@app/hooks/reduxHooks';
import { doLogout } from '@app/store/slices/authSlice';
import useIdleTimer from '@app/hooks/useIdleTimer';
import { Alert, theme } from 'antd';
import { useAppSelector } from '@app/hooks/reduxHooks';
import { themeObject } from '@app/styles/themes/themeVariables';

const MainLayout: React.FC = () => {
const [isTwoColumnsLayout, setIsTwoColumnsLayout] = useState(true);
const [siderCollapsed, setSiderCollapsed] = useState(true);

const { isDesktop } = useResponsive();
const location = useLocation();
const dispatch = useAppDispatch();
const navigate = useNavigate();

const { showAlert } = useAppSelector((state) => state.pwa);
const theme = useAppSelector((state) => state.theme.theme);
const toggleSider = () => setSiderCollapsed(!siderCollapsed);

useEffect(() => {
Expand Down Expand Up @@ -61,6 +69,33 @@ const MainLayout: React.FC = () => {
</div>
{!isTwoColumnsLayout && <References />}
</MainContent>
{showAlert && (
<div
style={{
margin: '1rem',
display: 'flex',
flexDirection: 'row',
width: isDesktop ? '55%' : '90%',
justifyContent: 'center',
position: 'absolute',
bottom: '0',
}}
>
<Alert
message={'There was an error installing the app. Please verify that the app is not already installed.'}
type="warning"
showIcon
closable
onClose={() => dispatch(setShowAlert(false))}
style={{
color: 'black',
width: 'fit-content',

backgroundColor: themeObject[theme].notificationWarning,
}}
/>
</div>
)}
</S.LayoutMain>
</S.LayoutMaster>
);
Expand Down
24 changes: 5 additions & 19 deletions src/hooks/usePWA.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,18 @@
import { useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { setPWASupported } from '@app/store/slices/pwaSlice';
import { addDeferredPrompt } from '@app/store/slices/pwaSlice';

export const usePWA = () => {
export const usePWA = (): void => {
const dispatch = useDispatch();

useEffect(() => {
const handler = (e: Event) => {
const handler = (e: any) => {
e.preventDefault();
// Assuming e is the BeforeInstallPromptEvent
// Instead of storing the event, we just indicate support is available
dispatch(setPWASupported(true));
console.log(e);
dispatch(addDeferredPrompt(e));
};

window.addEventListener('beforeinstallprompt', handler);

console.log('Setting up global beforeinstallprompt listener');
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault();
console.log('Global beforeinstallprompt event fired and stored');
(window as any).deferredPrompt = e;
});

return () => {
window.removeEventListener('beforeinstallprompt', handler);
// Optionally, dispatch that PWA is no longer supported if the event is dismissed
dispatch(setPWASupported(false));
};
}, [dispatch]);
};

Expand Down
11 changes: 10 additions & 1 deletion src/store/slices/pwaSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import { PayloadAction, createSlice } from '@reduxjs/toolkit';

// Define the initial state using only serializable values
const initialState = {
event: null as BeforeInstallPromptEvent | null,
isPWASupported: false,
showAlert: false,
isStandalone: window.matchMedia('(display-mode: standalone)').matches,
};

Expand All @@ -18,11 +20,18 @@ export const pwaSlice = createSlice({
clearPWASupport: (state) => {
state.isPWASupported = false;
},
addDeferredPrompt: (state, action: PayloadAction<BeforeInstallPromptEvent>) => {
state.event = action.payload;
state.isPWASupported = true;
},
setShowAlert: (state, action: PayloadAction<boolean>) => {
state.showAlert = action.payload;
},
},
});

// Exporting actions
export const { setPWASupported, clearPWASupport } = pwaSlice.actions;
export const { setPWASupported, clearPWASupport, addDeferredPrompt, setShowAlert } = pwaSlice.actions;

export default pwaSlice.reducer;

Expand Down
5 changes: 4 additions & 1 deletion src/store/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ const initialState = {

export const store = configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(errorLoggingMiddleware),
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: false,
}).concat(errorLoggingMiddleware),
preloadedState: initialState,
});

Expand Down
4 changes: 3 additions & 1 deletion src/styles/GlobalStyle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ export default createGlobalStyle`
.ant-picker-cell {
color: var(--text-main-color);
}
.ant-picker-cell-in-view .ant-picker-calendar-date-value {
color: var(--text-main-color);
font-weight: ${FONT_WEIGHT.bold};
Expand Down Expand Up @@ -205,6 +204,9 @@ export default createGlobalStyle`
background-color: white;
border-color: themeObject[theme].textMain;
}
.ant-alert-message{
color: black
}
.custom-checkbox-group .ant-checkbox-checked .ant-checkbox-inner {
background-color: #1890ff;
Expand Down

0 comments on commit 78fd733

Please sign in to comment.