added integration for calendar to have twake top bar (#576)

This commit is contained in:
Camille Moussu
2026-02-25 11:01:09 +01:00
committed by GitHub
parent 9cc6625e84
commit 8c3fa4bc1b
6 changed files with 84 additions and 43 deletions
+16
View File
@@ -23,6 +23,7 @@
"@mui/material": "^7.1.2",
"@mui/x-date-pickers": "^8.14.0",
"@reduxjs/toolkit": "^2.8.2",
"cozy-external-bridge": "^1.2.1",
"dayjs": "^1.11.19",
"i18next": "^23.7.9",
"ical.js": "^2.2.0",
@@ -6387,6 +6388,12 @@
"dev": true,
"license": "MIT"
},
"node_modules/comlink": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/comlink/-/comlink-4.4.1.tgz",
"integrity": "sha512-+1dlx0aY5Jo1vHy/tSsIGpSkN4tS9rZSW8FIhG0JH/crs9wwweswIo/POr451r7bZww3hFbPAKnTpimzL/mm4Q==",
"license": "Apache-2.0"
},
"node_modules/commander": {
"version": "9.5.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
@@ -6547,6 +6554,15 @@
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/cozy-external-bridge": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/cozy-external-bridge/-/cozy-external-bridge-1.2.1.tgz",
"integrity": "sha512-Vbf3ugIuYWGHHD4fiWdUIvljadpHiV/GF1YaETMmlmOlxagiwbK3NESlAiXoGgg5aHdLjXliGJy+j9ccMdVm/g==",
"license": "MIT",
"dependencies": {
"comlink": "4.4.1"
}
},
"node_modules/create-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
+1
View File
@@ -18,6 +18,7 @@
"@mui/material": "^7.1.2",
"@mui/x-date-pickers": "^8.14.0",
"@reduxjs/toolkit": "^2.8.2",
"cozy-external-bridge": "^1.2.1",
"dayjs": "^1.11.19",
"i18next": "^23.7.9",
"ical.js": "^2.2.0",
+4
View File
@@ -33,6 +33,7 @@ import { User } from "../Attendees/PeopleSearch";
import { EventErrorSnackbar } from "../Error/ErrorSnackbar";
import { EventErrorHandler } from "../Error/EventErrorHandler";
import { EditModeDialog } from "../Event/EditModeDialog";
import { Menubar, MenubarProps } from "../Menubar/Menubar";
import "./Calendar.styl";
import CalendarSelection from "./CalendarSelection";
import "./CustomCalendar.styl";
@@ -59,12 +60,14 @@ interface CalendarAppProps {
calendarRef: MutableRefObject<CalendarApi | null>;
onDateChange?: (date: Date) => void;
onViewChange?: (view: string) => void;
menubarProps?: MenubarProps;
}
export default function CalendarApp({
calendarRef,
onDateChange,
onViewChange,
menubarProps,
}: CalendarAppProps) {
const [selectedDate, setSelectedDate] = useState(new Date());
const [selectedMiniDate, setSelectedMiniDate] = useState(new Date());
@@ -661,6 +664,7 @@ export default function CalendarApp({
</Box>
<div className="calendar">
<ImportAlert />
{menubarProps?.isIframe && <Menubar {...menubarProps} />}
{view === "calendar" && (
<FullCalendar
ref={(ref) => {
+6 -2
View File
@@ -2,7 +2,8 @@ import { useAppDispatch, useAppSelector } from "@/app/hooks";
import SettingsPage from "@/features/Settings/SettingsPage";
import { getCalendarRange } from "@/utils/dateUtils";
import type { CalendarApi } from "@fullcalendar/core";
import { useEffect, useRef, useState } from "react";
import CozyBridge from "cozy-external-bridge";
import { useEffect, useMemo, useRef, useState } from "react";
import { ErrorSnackbar } from "../Error/ErrorSnackbar";
import { refreshCalendars } from "../Event/utils/eventUtils";
import { Menubar, MenubarProps } from "../Menubar/Menubar";
@@ -17,6 +18,7 @@ export default function CalendarLayout() {
const view = useAppSelector((state) => state.settings.view);
const [currentDate, setCurrentDate] = useState<Date>(new Date());
const [currentView, setCurrentView] = useState<string>("timeGridWeek");
const isInIframe = useMemo(() => new CozyBridge().isInIframe(), []);
const handleRefresh = async () => {
// Get current calendar range
@@ -70,16 +72,18 @@ export default function CalendarLayout() {
onDateChange: handleDateChange,
currentView,
onViewChange: handleViewChange,
isIframe: isInIframe,
};
return (
<div className="App">
<Menubar {...menubarProps} />
{!isInIframe && <Menubar {...menubarProps} />}
{(view === "calendar" || view === "search") && (
<CalendarApp
calendarRef={calendarRef}
onDateChange={handleDateChange}
onViewChange={handleViewChange}
menubarProps={menubarProps}
/>
)}
{view === "settings" && <SettingsPage />}
+57 -41
View File
@@ -46,6 +46,7 @@ export type MenubarProps = {
onDateChange?: (date: Date) => void;
currentView: string;
onViewChange?: (view: string) => void;
isIframe?: boolean;
};
export function Menubar({
@@ -55,6 +56,7 @@ export function Menubar({
onDateChange,
currentView,
onViewChange,
isIframe,
}: MenubarProps) {
const { t } = useI18n(); // deliberately NOT using f()
const user = useAppSelector((state) => state.user.userData);
@@ -155,14 +157,16 @@ export function Menubar({
<>
<header className="menubar">
<div className="left-menu">
<div className="menu-items">
<MainTitle
calendarRef={calendarRef}
currentView={currentView}
onViewChange={onViewChange}
onDateChange={onDateChange}
/>
</div>
{!isIframe && (
<div className="menu-items">
<MainTitle
calendarRef={calendarRef}
currentView={currentView}
onViewChange={onViewChange}
onDateChange={onDateChange}
/>
</div>
)}
<div className="menu-items">
<div className="navigation-controls">
<ButtonGroup
@@ -245,41 +249,53 @@ export function Menubar({
</Select>
</FormControl>
</div>
<div className="menu-items">
<IconButton
component="a"
href="https://twake.app/support/"
target="_blank"
rel="noopener noreferrer"
style={{ marginRight: 8 }}
aria-label={t("menubar.help")}
title={t("menubar.help")}
>
<HelpOutlineIcon />
</IconButton>
</div>
<div className="menu-items">
{applist.length > 0 && (
<IconButton
onClick={handleOpen}
style={{ marginRight: 8 }}
aria-label={t("menubar.apps")}
title={t("menubar.apps")}
>
<WidgetsOutlinedIcon />
</IconButton>
)}
</div>
{!isIframe && (
<>
<div className="menu-items">
<IconButton
component="a"
href="https://twake.app/support/"
target="_blank"
rel="noopener noreferrer"
style={{ marginRight: 8 }}
aria-label={t("menubar.help")}
title={t("menubar.help")}
>
<HelpOutlineIcon />
</IconButton>
</div>
<div className="menu-items">
{applist.length > 0 && (
<IconButton
onClick={handleOpen}
style={{ marginRight: 8 }}
aria-label={t("menubar.apps")}
title={t("menubar.apps")}
>
<WidgetsOutlinedIcon />
</IconButton>
)}
</div>
</>
)}
<div className="menu-items">
<IconButton onClick={handleUserMenuClick}>
<Avatar
color={stringToGradient(getUserDisplayName(user))}
size="m"
aria-label={t("menubar.userProfile")}
>
{getInitials(getUserDisplayName(user))}
</Avatar>
<IconButton
onClick={!isIframe ? handleUserMenuClick : handleSettingsClick}
aria-label={isIframe ? t("menubar.settings") : undefined}
>
{!isIframe ? (
<Avatar
color={stringToGradient(getUserDisplayName(user))}
size="m"
aria-label={t("menubar.userProfile")}
>
{getInitials(getUserDisplayName(user))}
</Avatar>
) : (
<SettingsOutlinedIcon />
)}
</IconButton>
</div>
</div>