fix: adjust contact and calendar page for mobile layout #103
This commit is contained in:
@@ -90,6 +90,9 @@ export default function CalendarPage() {
|
||||
const [pendingPreview, setPendingPreview] = useState<PendingEventPreview | null>(null);
|
||||
const [showTaskModal, setShowTaskModal] = useState(false);
|
||||
const [editTask, setEditTask] = useState<import("@/lib/jmap/types").CalendarTask | null>(null);
|
||||
const [mobileReturnToMonth, setMobileReturnToMonth] = useState(false);
|
||||
const [swipeDirection, setSwipeDirection] = useState<'left' | 'right' | null>(null);
|
||||
const [swipeKey, setSwipeKey] = useState(0);
|
||||
const hasFetched = useRef(false);
|
||||
|
||||
// Sidebar resize state
|
||||
@@ -230,6 +233,8 @@ export default function CalendarPage() {
|
||||
|
||||
const handleTouchEnd = useCallback((e: ReactTouchEvent) => {
|
||||
if (!touchStartRef.current || !isMobile) return;
|
||||
// Week view has its own horizontal scroll, skip swipe navigation
|
||||
if (normalizedViewMode === 'week') { touchStartRef.current = null; return; }
|
||||
const touch = e.changedTouches[0];
|
||||
const dx = touch.clientX - touchStartRef.current.x;
|
||||
const dy = touch.clientY - touchStartRef.current.y;
|
||||
@@ -238,20 +243,34 @@ export default function CalendarPage() {
|
||||
|
||||
// Only trigger swipe if horizontal movement is dominant and fast enough
|
||||
if (Math.abs(dx) > 60 && Math.abs(dx) > Math.abs(dy) * 1.5 && elapsed < 400) {
|
||||
if (dx > 0) navigatePrev();
|
||||
else navigateNext();
|
||||
if (dx > 0) {
|
||||
setSwipeDirection('right');
|
||||
navigatePrev();
|
||||
} else {
|
||||
setSwipeDirection('left');
|
||||
navigateNext();
|
||||
}
|
||||
setSwipeKey(k => k + 1);
|
||||
// Clear direction after animation completes
|
||||
setTimeout(() => setSwipeDirection(null), 250);
|
||||
}
|
||||
}, [isMobile, navigatePrev, navigateNext]);
|
||||
}, [isMobile, normalizedViewMode, navigatePrev, navigateNext]);
|
||||
|
||||
const handleSelectDate = useCallback((date: Date) => {
|
||||
setSelectedDate(date);
|
||||
setMiniMonth(date);
|
||||
// On mobile month view, tapping a date switches to day view
|
||||
if (isMobile && normalizedViewMode === "month") {
|
||||
setMobileReturnToMonth(true);
|
||||
setViewMode("day");
|
||||
}
|
||||
}, [setSelectedDate, isMobile, normalizedViewMode, setViewMode]);
|
||||
|
||||
const navigateBackToMonth = useCallback(() => {
|
||||
setMobileReturnToMonth(false);
|
||||
setViewMode("month");
|
||||
}, [setViewMode]);
|
||||
|
||||
const handleMiniMonthChange = useCallback((date: Date) => {
|
||||
setMiniMonth(date);
|
||||
setSelectedDate(date);
|
||||
@@ -887,11 +906,12 @@ export default function CalendarPage() {
|
||||
onPrev={navigatePrev}
|
||||
onNext={navigateNext}
|
||||
onToday={goToToday}
|
||||
onViewModeChange={setViewMode}
|
||||
onViewModeChange={(mode) => { setMobileReturnToMonth(false); setViewMode(mode); }}
|
||||
onCreateEvent={() => openCreateModal()}
|
||||
onImport={() => setShowImportModal(true)}
|
||||
onSubscribe={() => setShowSubscriptionModal(true)}
|
||||
isMobile={isMobile}
|
||||
onNavigateBack={isMobile && mobileReturnToMonth && normalizedViewMode === "day" ? navigateBackToMonth : undefined}
|
||||
calendars={calendars}
|
||||
selectedCalendarIds={selectedCalendarIds}
|
||||
onToggleVisibility={toggleCalendarVisibility}
|
||||
@@ -904,7 +924,16 @@ export default function CalendarPage() {
|
||||
onTouchStart={handleTouchStart}
|
||||
onTouchEnd={handleTouchEnd}
|
||||
>
|
||||
{renderView()}
|
||||
<div
|
||||
key={swipeKey}
|
||||
className={cn(
|
||||
"flex flex-1 min-w-0",
|
||||
isMobile && swipeDirection === 'left' && "animate-slide-in-right",
|
||||
isMobile && swipeDirection === 'right' && "animate-slide-in-left",
|
||||
)}
|
||||
>
|
||||
{renderView()}
|
||||
</div>
|
||||
|
||||
{/* Desktop event panel */}
|
||||
{!isMobile && showEventModal && (
|
||||
|
||||
@@ -601,7 +601,7 @@ export default function ContactsPage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-col flex-1 min-w-0">
|
||||
<div className="flex flex-col flex-1 min-w-0 min-h-0">
|
||||
{inlineApp && (
|
||||
<InlineAppView apps={loadedApps} activeAppId={inlineApp!.id} onClose={closeInlineApp} />
|
||||
)}
|
||||
@@ -701,7 +701,7 @@ export default function ContactsPage() {
|
||||
className="touch-manipulation"
|
||||
>
|
||||
<ArrowLeft className="w-4 h-4 mr-2" />
|
||||
{t("back_to_mail")}
|
||||
{t("back_to_contacts")}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -16,7 +16,7 @@ import { discoverOAuth, type OAuthMetadata } from "@/lib/oauth/discovery";
|
||||
import { generateCodeVerifier, generateCodeChallenge, generateState } from "@/lib/oauth/pkce";
|
||||
import { OAUTH_SCOPES } from "@/lib/oauth/tokens";
|
||||
|
||||
const APP_VERSION = "1.4.7";
|
||||
const APP_VERSION = "1.4.9";
|
||||
|
||||
const THEME_OPTIONS = [
|
||||
{ value: "light" as const, icon: Sun, label: "Light" },
|
||||
|
||||
@@ -529,6 +529,37 @@ body {
|
||||
animation: slide-in-from-left 0.3s ease-out;
|
||||
}
|
||||
|
||||
/* Calendar swipe animations (subtler slide for view transitions) */
|
||||
@keyframes cal-slide-in-right {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(30%);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes cal-slide-in-left {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(-30%);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-slide-in-right {
|
||||
animation: cal-slide-in-right 0.25s ease-out;
|
||||
}
|
||||
|
||||
.animate-slide-in-left {
|
||||
animation: cal-slide-in-left 0.25s ease-out;
|
||||
}
|
||||
|
||||
/* Reduced motion: respect user OS preference */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*,
|
||||
|
||||
Reference in New Issue
Block a user