Skip to content

Commit

Permalink
Merge pull request #324 from fperera123/feature/add-mmol-unit-2
Browse files Browse the repository at this point in the history
Allow user to switch between mg/dL and mmol/L
  • Loading branch information
CrazyMarvin authored Aug 9, 2024
2 parents 88b2334 + 2b2d04b commit 0855068
Show file tree
Hide file tree
Showing 10 changed files with 622 additions and 66 deletions.
548 changes: 516 additions & 32 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
"axios": "^1.6.7",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"electron-debug": "^4.0.0",
"electron-debug": "^3.2.0",
"electron-log": "^5.1.1",
"electron-updater": "^6.1.8",
"framer-motion": "^11.1.7",
Expand Down Expand Up @@ -146,7 +146,8 @@
"jest": "^29.5.0",
"jest-environment-jsdom": "^29.7.0",
"mini-css-extract-plugin": "^2.7.6",
"npm-check-updates": "^16.14.18",
"ncu": "^0.2.1",
"npm-check-updates": "^16.14.20",
"postcss-loader": "^7.3.3",
"prettier": "^2.8.8",
"react-refresh": "^0.14.0",
Expand Down
3 changes: 3 additions & 0 deletions src/main/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { resolveHtmlPath } from "./util"
import { WindowStateManager, WindowState } from './windowState';
import { registerWindowHandlers, destroyWindowHandlers } from "./windowHandler";
import { registerLogoutHandler, destroyLogoutHandler } from "./logoutHandler";
import { registerRefreshHandler, destroyRefreshHandler } from "./refreshHandler";
// class AppUpdater {
// constructor() {
// log.transports.file.level = 'info'
Expand Down Expand Up @@ -117,6 +118,7 @@ const createWindow = async () => {
app.on('window-all-closed', () => {
destroyWindowHandlers();
destroyLogoutHandler();
destroyRefreshHandler();
// Respect the OSX convention of having the application in memory even
// after all windows have been closed
if (process.platform !== 'darwin') {
Expand Down Expand Up @@ -149,3 +151,4 @@ ipcMain.handle('ipc-open-file', async (event, ...args) => {

registerWindowHandlers();
registerLogoutHandler();
registerRefreshHandler();
23 changes: 23 additions & 0 deletions src/main/refreshHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { BrowserWindow, ipcMain } from "electron"

export const registerRefreshHandler = () => {
ipcMain.on('refresh-all', () => {
BrowserWindow.getAllWindows().forEach(window => {
if (window.webContents) {
window.webContents.reload();
}
});
});

ipcMain.on('refresh-primary', () => {
BrowserWindow.getAllWindows().forEach(window => {
if (window.isPrimary && window.webContents) {
window.webContents.reload();
}
});
});
}

export const destroyRefreshHandler = () => {
ipcMain.removeAllListeners('refresh-all');
}
12 changes: 12 additions & 0 deletions src/renderer/config/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,20 @@ const themes: DropdownConfigType[] = [
},
];

const resultUnits: DropdownConfigType[] = [
{
label: 'mg/dL',
value: 'mg/dL',
},
{
label: 'mmol/L',
value: 'mmol/L',
},
];

export {
countries,
languages,
themes,
resultUnits,
}
4 changes: 3 additions & 1 deletion src/renderer/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,7 @@
"Country": "Country",
"Language": "Language",
"SelectCountry": "Select Country",
"SelectLanguage": "Select Language"
"SelectLanguage": "Select Language",
"Theme": "Theme",
"Unit": "Unit"
}
27 changes: 27 additions & 0 deletions src/renderer/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
import { useAuthStore } from '../stores/auth'

export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
Expand Down Expand Up @@ -29,4 +30,30 @@ export function sendLogout() {
window.electron.ipcRenderer.sendMessage('logout')
}

export function sendRefreshAllWindows() {
window.electron.ipcRenderer.sendMessage('refresh-all')
}

export function sendRefreshPrimaryWindow() {
window.electron.ipcRenderer.sendMessage('refresh-primary')
}

export function getUserValue(value: number): number {
const { resultUnit } = useAuthStore.getState()

if (resultUnit === 'mg/dL') {
return Math.round(value)
}

if (resultUnit === 'mmol/L') {
return Math.round(value / 18.0182)
}

throw new Error(`Unsupported result unit: ${resultUnit}`)
}

export function getUserUnit(): string {
const { resultUnit } = useAuthStore.getState()

return resultUnit
}
4 changes: 2 additions & 2 deletions src/renderer/pages/dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { GearIcon } from "@radix-ui/react-icons"
import { useNavigate } from "react-router-dom"
import { LoadingScreen } from "@/components/ui/loading"
import { useClearSession } from "@/hooks/session"
import { openNewWindow, setRedirectTo } from "@/lib/utils"
import { openNewWindow, setRedirectTo, getUserValue, getUserUnit } from "@/lib/utils"

const LOW = 70;
const HIGH = 240;
Expand Down Expand Up @@ -85,7 +85,7 @@ export default function DashboardPage() {
<GearIcon className="text-white h-6 w-6" />
</button>
<div className="flex items-center gap-3">
<p className="text-white text-3xl font-semibold">{graphData?.glucoseMeasurement?.ValueInMgPerDl} mg/dL</p>
<p className="text-white text-3xl font-semibold">{getUserValue(graphData?.glucoseMeasurement?.ValueInMgPerDl) + ' ' + getUserUnit() }</p>
<div className="flex justify-center items-center h-12 w-12 rounded-full bg-white/25">
<TrendArrow className="h-9 w-9 text-white" trend={graphData?.glucoseMeasurement?.TrendArrow ?? 1} />
</div>
Expand Down
58 changes: 29 additions & 29 deletions src/renderer/pages/settings/general.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import {
SelectValue,
} from "@/components/ui/select"
import { useAuthStore } from "@/stores/auth"
import { countries, languages, themes } from "@/config/app"
import { countries, languages, themes, resultUnits } from "@/config/app"
import { useTranslation } from "react-i18next"
import { setRedirectTo } from "@/lib/utils"
import { setRedirectTo, sendRefreshPrimaryWindow } from "@/lib/utils"

export default function SettingsGeneralPage() {
const navigate = useNavigate()
Expand All @@ -24,6 +24,9 @@ export default function SettingsGeneralPage() {
const country = useAuthStore((state) => state.country)
const setCountry = useAuthStore((state) => state.setCountry)

const resultUnit = useAuthStore((state) => state.resultUnit)
const setResultUnit = useAuthStore((state) => state.setResultUnit)

const { setTheme } = useTheme()
const setAndRefreshTheme = (t: ThemeType) => {
setTheme(t)
Expand All @@ -35,11 +38,16 @@ export default function SettingsGeneralPage() {
setLanguage(l)
}

const handleSetResultUnit = (value) => {
setResultUnit(value);
sendRefreshPrimaryWindow();
}

return (
<SettingsLayout>
<div className="grid grid-cols-3 gap-3">
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4">
<div>
<p className="text-foreground/30 text-xs mb-2">Theme</p>
<p className="text-foreground/30 text-xs mb-2">{t('Theme')}</p>
<Select onValueChange={setAndRefreshTheme} defaultValue={theme ?? ''}>
<SelectTrigger>
<SelectValue placeholder="Select Theme" />
Expand All @@ -52,32 +60,24 @@ export default function SettingsGeneralPage() {
))}
</SelectContent>
</Select>
{/* <div className="border rounded-md">
<Button
className={cn(theme === 'dark' ? 'bg-primary/10 border border-primary' : '')}
variant="ghost"
onClick={() => setAndRefreshTheme('dark')}
>
{t('Dark')}
</Button>
<Button
className={cn(theme === 'light' ? 'bg-primary/10 border border-primary' : '')}
variant="ghost"
onClick={() => setAndRefreshTheme('light')}
>
{t('Light')}
</Button>
<Button
className={cn(theme === 'system' ? 'bg-primary/10 border border-primary' : '')}
variant="ghost"
onClick={() => setAndRefreshTheme('system')}
>
{t('System')}
</Button>
</div> */}
</div>
<div>
<p className="text-foreground/30 text-xs mb-2">Country</p>
<p className="text-foreground/30 text-xs mb-2">{t('Unit')}</p>
<Select onValueChange={handleSetResultUnit} defaultValue={resultUnit ?? 'mg/dL'}>
<SelectTrigger>
<SelectValue placeholder="Select Unit" />
</SelectTrigger>
<SelectContent>
{resultUnits.map(item => (
<SelectItem value={item.value} key={item.value}>
{t(item.label)}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div>
<p className="text-foreground/30 text-xs mb-2">{t('Country')}</p>
<Select onValueChange={setCountry} defaultValue={country ?? ''}>
<SelectTrigger>
<SelectValue placeholder="Select Country" />
Expand All @@ -92,7 +92,7 @@ export default function SettingsGeneralPage() {
</Select>
</div>
<div>
<p className="text-foreground/30 text-xs mb-2">Language</p>
<p className="text-foreground/30 text-xs mb-2">{t('Language')}</p>
<Select onValueChange={setAndRefreshLanguage} defaultValue={language ?? ''}>
<SelectTrigger>
<SelectValue placeholder="Select Language" />
Expand Down
4 changes: 4 additions & 0 deletions src/renderer/stores/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ type AuthStore = {
token: string|null
country: string|null
language: string|null
resultUnit: string
setCountry: (value: string) => void
setLanguage: (value: string) => void
setResultUnit: (value: string) => void
login: (token: string, country: string, language: string) => void
logout: () => void
}
Expand All @@ -17,10 +19,12 @@ const useAuthStore = create<AuthStore>()(
token: null,
country: null,
language: null,
resultUnit: 'mg/dL',
login: (token: string, country: string, language: string) => set(() => ({ token, country, language })),
logout: () => set(() => ({ token: null })),
setCountry: (value) => set(() => ({ country: value })),
setLanguage: (value) => set(() => ({ language: value })),
setResultUnit: (value) => set(() => ({ resultUnit: value })),
}), {
name: 'auth-storage',
storage: createJSONStorage(() => localStorage),
Expand Down

0 comments on commit 0855068

Please sign in to comment.