diff --git a/apps/api/src/security/rbac/rbac.py b/apps/api/src/security/rbac/rbac.py index 8a16afd3..1e56238c 100644 --- a/apps/api/src/security/rbac/rbac.py +++ b/apps/api/src/security/rbac/rbac.py @@ -60,7 +60,11 @@ async def authorization_verify_if_user_is_author( element_uuid: str, db_session: Session, ): - if action == "update" or "delete" or "read": + # For create action, we don't need to check existing resource + if action == "create": + return True # Allow creation if user is authenticated + + if action in ["update", "delete", "read"]: statement = select(ResourceAuthor).where( ResourceAuthor.resource_uuid == element_uuid ) @@ -79,6 +83,7 @@ async def authorization_verify_if_user_is_author( return False else: return False + return False # Tested and working @@ -101,17 +106,17 @@ async def authorization_verify_based_on_roles( user_roles_in_organization_and_standard_roles = db_session.exec(statement).all() - # Find in roles list if there is a role that matches users action for this type of element + # Check all roles until we find one that grants the permission for role in user_roles_in_organization_and_standard_roles: role = Role.model_validate(role) if role.rights: rights = role.rights - if rights[element_type][f"action_{action}"] is True: + element_rights = getattr(rights, element_type, None) + if element_rights and getattr(element_rights, f"action_{action}", False): return True - else: - return False - else: - return False + + # If we get here, no role granted the permission + return False async def authorization_verify_based_on_org_admin_status( @@ -133,13 +138,13 @@ async def authorization_verify_based_on_org_admin_status( user_roles_in_organization_and_standard_roles = db_session.exec(statement).all() - # Find in roles list if there is a role that matches users action for this type of element + # Check if user has admin role (role_id 1 or 2) in any organization for role in user_roles_in_organization_and_standard_roles: role = Role.model_validate(role) - if role.id == 1 or role.id == 2: + if role.id in [1, 2]: # Assuming 1 and 2 are admin role IDs return True - else: - return False + + return False # Tested and working diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/activity/[activityid]/activity.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/activity/[activityid]/activity.tsx index 885d4ab4..c629dbf3 100644 --- a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/activity/[activityid]/activity.tsx +++ b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/activity/[activityid]/activity.tsx @@ -3,7 +3,7 @@ import Link from 'next/link' import { getAPIUrl, getUriWithOrg } from '@services/config/config' import Canva from '@components/Objects/Activities/DynamicCanva/DynamicCanva' import VideoActivity from '@components/Objects/Activities/Video/Video' -import { BookOpenCheck, Check, CheckCircle, ChevronDown, ChevronLeft, ChevronRight, FileText, Folder, List, Menu, MoreVertical, UserRoundPen, Video, Layers, ListFilter, ListTree, X, Edit2 } from 'lucide-react' +import { BookOpenCheck, Check, CheckCircle, ChevronDown, ChevronLeft, ChevronRight, FileText, Folder, List, Menu, MoreVertical, UserRoundPen, Video, Layers, ListFilter, ListTree, X, Edit2, EllipsisVertical, Maximize2, Minimize2 } from 'lucide-react' import { markActivityAsComplete, unmarkActivityAsComplete } from '@services/courses/activity' import DocumentPdfActivity from '@components/Objects/Activities/DocumentPdf/DocumentPdf' import ActivityIndicators from '@components/Pages/Courses/ActivityIndicators' @@ -16,7 +16,7 @@ import { CourseProvider } from '@components/Contexts/CourseContext' import AIActivityAsk from '@components/Objects/Activities/AI/AIActivityAsk' import AIChatBotProvider from '@components/Contexts/AI/AIChatBotContext' import { useLHSession } from '@components/Contexts/LHSessionContext' -import React, { useEffect } from 'react' +import React, { useEffect, useRef } from 'react' import { getAssignmentFromActivityUUID, getFinalGrade, submitAssignmentForGrading } from '@services/courses/assignments' import AssignmentStudentActivity from '@components/Objects/Activities/Assignment/AssignmentStudentActivity' import { AssignmentProvider } from '@components/Contexts/Assignments/AssignmentContext' @@ -33,6 +33,7 @@ import ActivityNavigation from '@components/Pages/Activity/ActivityNavigation' import ActivityChapterDropdown from '@components/Pages/Activity/ActivityChapterDropdown' import FixedActivitySecondaryBar from '@components/Pages/Activity/FixedActivitySecondaryBar' import CourseEndView from '@components/Pages/Activity/CourseEndView' +import { motion, AnimatePresence } from 'framer-motion' interface ActivityClientProps { activityid: string @@ -42,6 +43,55 @@ interface ActivityClientProps { course: any } +interface ActivityActionsProps { + activity: any + activityid: string + course: any + orgslug: string + assignment: any + showNavigation?: boolean +} + +function ActivityActions({ activity, activityid, course, orgslug, assignment, showNavigation = true }: ActivityActionsProps) { + const session = useLHSession() as any; + const { contributorStatus } = useContributorStatus(course.course_uuid); + + return ( +
+ {activity && activity.published == true && activity.content.paid_access != false && ( + + {activity.activity_type != 'TYPE_ASSIGNMENT' && ( + <> + + + )} + {activity.activity_type == 'TYPE_ASSIGNMENT' && ( + <> + + + + + )} + {showNavigation && ( + + )} + + )} +
+ ); +} + function ActivityClient(props: ActivityClientProps) { const activityid = props.activityid const courseuuid = props.courseuuid @@ -55,8 +105,69 @@ function ActivityClient(props: ActivityClientProps) { const [bgColor, setBgColor] = React.useState('bg-white') const [assignment, setAssignment] = React.useState(null) as any; const [markStatusButtonActive, setMarkStatusButtonActive] = React.useState(false); + const [isFocusMode, setIsFocusMode] = React.useState(false); + const isInitialRender = useRef(true); const { contributorStatus } = useContributorStatus(courseuuid); - + const router = useRouter(); + + // Function to find the current activity's position in the course + const findActivityPosition = () => { + let allActivities: any[] = []; + let currentIndex = -1; + + // Flatten all activities from all chapters + course.chapters.forEach((chapter: any) => { + chapter.activities.forEach((activity: any) => { + const cleanActivityUuid = activity.activity_uuid?.replace('activity_', ''); + allActivities.push({ + ...activity, + cleanUuid: cleanActivityUuid, + chapterName: chapter.name + }); + + // Check if this is the current activity + if (cleanActivityUuid === activityid.replace('activity_', '')) { + currentIndex = allActivities.length - 1; + } + }); + }); + + return { allActivities, currentIndex }; + }; + + const { allActivities, currentIndex } = findActivityPosition(); + + // Get previous and next activities + const prevActivity = currentIndex > 0 ? allActivities[currentIndex - 1] : null; + const nextActivity = currentIndex < allActivities.length - 1 ? allActivities[currentIndex + 1] : null; + + // Navigate to an activity + const navigateToActivity = (activity: any) => { + if (!activity) return; + + const cleanCourseUuid = course.course_uuid?.replace('course_', ''); + router.push(getUriWithOrg(orgslug, '') + `/course/${cleanCourseUuid}/activity/${activity.cleanUuid}`); + }; + + // Initialize focus mode from localStorage + React.useEffect(() => { + if (typeof window !== 'undefined') { + const saved = localStorage.getItem('globalFocusMode'); + setIsFocusMode(saved === 'true'); + } + }, []); + + // Save focus mode to localStorage + React.useEffect(() => { + if (typeof window !== 'undefined') { + localStorage.setItem('globalFocusMode', isFocusMode.toString()); + // Dispatch custom event for focus mode change + window.dispatchEvent(new CustomEvent('focusModeChange', { + detail: { isFocusMode } + })); + isInitialRender.current = false; + } + }, [isFocusMode]); function getChapterNameByActivityId(course: any, activity_id: any) { for (let i = 0; i < course.chapters.length; i++) { @@ -78,43 +189,72 @@ function ActivityClient(props: ActivityClientProps) { useEffect(() => { if (activity.activity_type == 'TYPE_DYNAMIC') { - setBgColor('bg-white nice-shadow'); + setBgColor(isFocusMode ? 'bg-white' : 'bg-white nice-shadow'); } else if (activity.activity_type == 'TYPE_ASSIGNMENT') { setMarkStatusButtonActive(false); - setBgColor('bg-white nice-shadow'); + setBgColor(isFocusMode ? 'bg-white' : 'bg-white nice-shadow'); getAssignmentUI(); } else { - setBgColor('bg-zinc-950'); + setBgColor(isFocusMode ? 'bg-zinc-950' : 'bg-zinc-950 nice-shadow'); } } - , [activity, pathname]) + , [activity, pathname, isFocusMode]) return ( <> - - {activityid === 'end' ? ( - - ) : ( -
-
-
-
-
+ {isFocusMode ? ( + + + {/* Focus Mode Top Bar */} + +
+
+
+ setIsFocusMode(false)} + className="bg-white nice-shadow p-2 rounded-full cursor-pointer hover:bg-gray-50" + title="Exit focus mode" + > + + + +
+ + {/* Center Course Info */} +
-

Course

-

+

Course

+

{course.name}

-
+ + + {/* Progress Indicator */} + +
+ + + run.course_id === course.id)?.steps?.filter((step: any) => step.complete)?.length || 0) / (course.chapters?.reduce((acc: number, chapter: any) => acc + chapter.activities.length, 0) || 1))} + /> + +
+ + {Math.round(((course.trail?.runs?.find((run: any) => run.course_id === course.id)?.steps?.filter((step: any) => step.complete)?.length || 0) / (course.chapters?.reduce((acc: number, chapter: any) => acc + chapter.activities.length, 0) || 1)) * 100)}% + +
+
+
+ {course.trail?.runs?.find((run: any) => run.course_id === course.id)?.steps?.filter((step: any) => step.complete)?.length || 0} of {course.chapters?.reduce((acc: number, chapter: any) => acc + chapter.activities.length, 0) || 0} +
+
+
+ - + {/* Focus Mode Content */} +
+
+ {activity && activity.published == true && ( + <> + {activity.content.paid_access == false ? ( + + ) : ( + + {/* Activity Types */} +
+ {activity.activity_type == 'TYPE_DYNAMIC' && ( + + )} + {activity.activity_type == 'TYPE_VIDEO' && ( + + )} + {activity.activity_type == 'TYPE_DOCUMENT' && ( + + )} + {activity.activity_type == 'TYPE_ASSIGNMENT' && ( +
+ {assignment ? ( + + + + + + + + ) : ( +
+ )} +
+ )} +
+
+ )} + + )} +
+
-
-
- -
-

- Chapter : {getChapterNameByActivityId(course, activity.id)} -

-

- {activity.name} -

+ {/* Focus Mode Bottom Bar */} + {activity && activity.published == true && activity.content.paid_access != false && ( + +
+
+
+ +
+
+ +
-
+
+ + )} + + + ) : ( + + {/* Original non-focus mode UI */} + {activityid === 'end' ? ( + + ) : ( +
+
+
+
+
+
+ + + +
+
+

Course

+

+ {course.name} +

+
+
{activity && activity.published == true && activity.content.paid_access != false && ( - {activity.activity_type != 'TYPE_ASSIGNMENT' && ( - <> - - {contributorStatus === 'ACTIVE' && activity.activity_type == 'TYPE_DYNAMIC' && ( - - - Contribute to Activity - - )} - - + - - )} - {activity.activity_type == 'TYPE_ASSIGNMENT' && ( - <> - - - - - + +
)} )}
-
-
- {activity && activity.published == false && ( -
-
-

- This activity is not published yet -

-
-
- )} - {activity && activity.published == true && ( - <> - {activity.content.paid_access == false ? ( - - ) : ( -
- {/* Activity Types */} -
- {activity.activity_type == 'TYPE_DYNAMIC' && ( - - )} - {activity.activity_type == 'TYPE_VIDEO' && ( - - )} - {activity.activity_type == 'TYPE_DOCUMENT' && ( - - )} - {activity.activity_type == 'TYPE_ASSIGNMENT' && ( -
- {assignment ? ( - - - - - - - - ) : ( -
- )} -
- )} + + +
+
+ + +
+

+ Chapter : {getChapterNameByActivityId(course, activity.id)} +

+

+ {activity.name} +

- )} - - )} - +
+ {activity && activity.published == true && activity.content.paid_access != false && ( + + {activity.activity_type != 'TYPE_ASSIGNMENT' && ( + <> + + {contributorStatus === 'ACTIVE' && activity.activity_type == 'TYPE_DYNAMIC' && ( + + + Contribute + + )} + + )} + + )} +
+
+
- {/* Fixed Activity Secondary Bar */} - {activity && activity.published == true && activity.content.paid_access != false && ( - - )} - -
+ {activity && activity.published == false && ( +
+
+

+ This activity is not published yet +

+
+
+ )} + + {activity && activity.published == true && ( + <> + {activity.content.paid_access == false ? ( + + ) : ( +
+ {/* Activity Types */} +
+ {activity.activity_type == 'TYPE_DYNAMIC' && ( + + )} + {activity.activity_type == 'TYPE_VIDEO' && ( + + )} + {activity.activity_type == 'TYPE_DOCUMENT' && ( + + )} + {activity.activity_type == 'TYPE_ASSIGNMENT' && ( +
+ {assignment ? ( + + + + + + + + ) : ( +
+ )} +
+ )} +
+
+ )} + + )} + + {/* Activity Actions below the content box */} + {activity && activity.published == true && activity.content.paid_access != false && ( +
+ +
+ )} + + {/* Fixed Activity Secondary Bar */} + {activity && activity.published == true && activity.content.paid_access != false && ( + + )} + +
+
-
- )} - + )} + + )} @@ -415,7 +771,6 @@ export function MarkStatus(props: { status="warning" /> -
) : (
@@ -437,7 +792,6 @@ export function MarkStatus(props: { )}{' '} {!isMobile && {isLoading ? 'Marking...' : 'Mark as complete'}}
-
)} @@ -483,15 +837,66 @@ function NextActivityButton({ course, currentActivityId, orgslug }: { course: an }; return ( - -
- {!isMobile && Next} - -
-
+
+ Next + + {nextActivity.name} + +
+ ); +} + +function PreviousActivityButton({ course, currentActivityId, orgslug }: { course: any, currentActivityId: string, orgslug: string }) { + const router = useRouter(); + const isMobile = useMediaQuery('(max-width: 768px)'); + + const findPreviousActivity = () => { + let allActivities: any[] = []; + let currentIndex = -1; + + // Flatten all activities from all chapters + course.chapters.forEach((chapter: any) => { + chapter.activities.forEach((activity: any) => { + const cleanActivityUuid = activity.activity_uuid?.replace('activity_', ''); + allActivities.push({ + ...activity, + cleanUuid: cleanActivityUuid, + chapterName: chapter.name + }); + + // Check if this is the current activity + if (activity.id === currentActivityId) { + currentIndex = allActivities.length - 1; + } + }); + }); + + // Get previous activity + return currentIndex > 0 ? allActivities[currentIndex - 1] : null; + }; + + const previousActivity = findPreviousActivity(); + + if (!previousActivity) return null; + + const navigateToActivity = () => { + const cleanCourseUuid = course.course_uuid?.replace('course_', ''); + router.push(getUriWithOrg(orgslug, '') + `/course/${cleanCourseUuid}/activity/${previousActivity.cleanUuid}`); + }; + + return ( +
+ + Previous + + {previousActivity.name} +
); } diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/course.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/course.tsx index 236415c3..41dfbbb1 100644 --- a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/course.tsx +++ b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/course.tsx @@ -60,6 +60,16 @@ const CourseClient = (props: any) => { useEffect(() => { getLearningTags() + + // Collapse chapters by default if more than 5 activities in total + if (course?.chapters) { + const totalActivities = course.chapters.reduce((sum: number, chapter: any) => sum + (chapter.activities?.length || 0), 0) + const defaultExpanded: {[key: string]: boolean} = {} + course.chapters.forEach((chapter: any) => { + defaultExpanded[chapter.chapter_uuid] = totalActivities <= 5 + }) + setExpandedChapters(defaultExpanded) + } }, [org, course]) const getActivityTypeLabel = (activityType: string) => { @@ -117,191 +127,52 @@ const CourseClient = (props: any) => { ) : ( <> -
+

Course

{course.name}

- {props.course?.thumbnail_image && org ? ( -
- ) : ( -
- )} - - - -
-
-

About

-
-

{course.about}

-
- - {learnings.length > 0 && learnings[0]?.text !== 'null' && ( -
-

- What you will learn -

-
- {learnings.map((learning: any) => { - // Handle both new format (object with text and emoji) and legacy format (string) - const learningText = typeof learning === 'string' ? learning : learning.text - const learningEmoji = typeof learning === 'string' ? null : learning.emoji - const learningId = typeof learning === 'string' ? learning : learning.id || learning.text - - if (!learningText) return null - - return ( -
-
- {learningEmoji ? ( - {learningEmoji} - ) : ( - - )} -
-

{learningText}

- {learning.link && ( - - Link to {learningText} - - - )} -
- ) - })} -
-
+
+
+ {props.course?.thumbnail_image && org ? ( +
+ ) : ( +
)} -

Course Lessons

-
- {course.chapters.map((chapter: any) => { - const isExpanded = expandedChapters[chapter.chapter_uuid] ?? true; // Default to expanded - return ( -
-
setExpandedChapters(prev => ({ - ...prev, - [chapter.chapter_uuid]: !isExpanded - }))} - > -

{chapter.name}

-
-

- {chapter.activities.length} Activities -

- - - -
-
-
-
- {chapter.activities.map((activity: any) => { - return ( -
-
-
-
- {isActivityDone(activity) ? ( -
- - -
- ) : ( -
- -
- )} -
- -
-

{activity.name}

- {isActivityCurrent(activity) && ( -
- Current -
- )} -
-
- {activity.activity_type === 'TYPE_DYNAMIC' && ( - - )} - {activity.activity_type === 'TYPE_VIDEO' && ( -
- -
- -
-
-
-
- ) - })} -
-
-
- ) - })} + {course?.trail?.runs?.find((run: any) => run.course_id == course.id) && ( + + )} + +
+
+

{course.about}

+
-
+ +
{/* Actions Box */} @@ -313,6 +184,145 @@ const CourseClient = (props: any) => {
+ + {learnings.length > 0 && learnings[0]?.text !== 'null' && ( +
+

What you will learn

+
+ {learnings.map((learning: any) => { + // Handle both new format (object with text and emoji) and legacy format (string) + const learningText = typeof learning === 'string' ? learning : learning.text + const learningEmoji = typeof learning === 'string' ? null : learning.emoji + const learningId = typeof learning === 'string' ? learning : learning.id || learning.text + + if (!learningText) return null + + return ( +
+
+ {learningEmoji ? ( + {learningEmoji} + ) : ( + + )} +
+

{learningText}

+ {learning.link && ( + + Link to {learningText} + + + )} +
+ ) + })} +
+
+ )} + +
+

Course Lessons

+
+ {course.chapters.map((chapter: any) => { + const isExpanded = expandedChapters[chapter.chapter_uuid] ?? true; // Default to expanded + return ( +
+
setExpandedChapters(prev => ({ + ...prev, + [chapter.chapter_uuid]: !isExpanded + }))} + > +

{chapter.name}

+
+

+ {chapter.activities.length} Activities +

+ + + +
+
+
+
+ {chapter.activities.map((activity: any) => { + return ( + +
+
+ {isActivityDone(activity) ? ( +
+ + +
+ ) : ( +
+ +
+ )} +
+
+
+

{activity.name}

+ {isActivityCurrent(activity) && ( +
+ Current +
+ )} +
+
+ {activity.activity_type === 'TYPE_DYNAMIC' && ( + + )} + {activity.activity_type === 'TYPE_VIDEO' && ( +
+
+
+ +
+
+ + ) + })} +
+
+
+ ) + })} +
+
{isMobile && ( diff --git a/apps/web/components/Objects/Editor/Extensions/Image/ImageBlock.ts b/apps/web/components/Objects/Editor/Extensions/Image/ImageBlock.ts index caff7013..abc7dc15 100644 --- a/apps/web/components/Objects/Editor/Extensions/Image/ImageBlock.ts +++ b/apps/web/components/Objects/Editor/Extensions/Image/ImageBlock.ts @@ -17,6 +17,9 @@ export default Node.create({ size: { width: 300, }, + alignment: { + default: 'center', + }, } }, diff --git a/apps/web/components/Objects/Editor/Extensions/Image/ImageBlockComponent.tsx b/apps/web/components/Objects/Editor/Extensions/Image/ImageBlockComponent.tsx index fb985d78..5227b95c 100644 --- a/apps/web/components/Objects/Editor/Extensions/Image/ImageBlockComponent.tsx +++ b/apps/web/components/Objects/Editor/Extensions/Image/ImageBlockComponent.tsx @@ -1,7 +1,7 @@ import { NodeViewWrapper } from '@tiptap/react' import React, { useEffect } from 'react' import { Resizable } from 're-resizable' -import { AlertTriangle, Image, Download } from 'lucide-react' +import { AlertTriangle, Image, Download, AlignLeft, AlignCenter, AlignRight } from 'lucide-react' import { uploadNewImageFile } from '../../../../../services/blocks/Image/images' import { getActivityBlockMediaDirectory } from '@services/media/media' import { useOrg } from '@components/Contexts/OrgContext' @@ -29,10 +29,12 @@ function ImageBlockComponent(props: any) { const [imageSize, setImageSize] = React.useState({ width: props.node.attrs.size ? props.node.attrs.size.width : 300, }) + const [alignment, setAlignment] = React.useState(props.node.attrs.alignment || 'center') const fileId = blockObject ? `${blockObject.content.file_id}.${blockObject.content.file_format}` : null + const handleImageChange = (event: React.ChangeEvent) => { setImage(event.target.files[0]) } @@ -49,6 +51,7 @@ function ImageBlockComponent(props: any) { props.updateAttributes({ blockObject: object, size: imageSize, + alignment: alignment, }) } @@ -75,8 +78,26 @@ function ImageBlockComponent(props: any) { document.body.removeChild(link); }; + const handleAlignmentChange = (newAlignment: string) => { + setAlignment(newAlignment); + props.updateAttributes({ + alignment: newAlignment, + }); + }; + useEffect(() => {}, [course, org]) + const getAlignmentClass = () => { + switch (alignment) { + case 'left': + return 'justify-start'; + case 'right': + return 'justify-end'; + default: + return 'justify-center'; + } + }; + return ( @@ -85,7 +106,7 @@ function ImageBlockComponent(props: any) { {blockObject && isEditable && ( -
+
- +
+ +
+ + + +
+
)} {blockObject && !isEditable && ( -
+
{ const orgslug = props.orgslug @@ -17,6 +18,41 @@ export const OrgMenu = (props: any) => { const [feedbackModal, setFeedbackModal] = React.useState(false) const org = useOrg() as any; const [isMenuOpen, setIsMenuOpen] = React.useState(false) + const [isFocusMode, setIsFocusMode] = useState(false) + const pathname = usePathname() + + useEffect(() => { + // Only check focus mode if we're in an activity page + if (typeof window !== 'undefined' && pathname?.includes('/activity/')) { + const saved = localStorage.getItem('globalFocusMode'); + setIsFocusMode(saved === 'true'); + } else { + setIsFocusMode(false); + } + + // Add storage event listener for cross-window changes + const handleStorageChange = (e: StorageEvent) => { + if (e.key === 'globalFocusMode' && pathname?.includes('/activity/')) { + setIsFocusMode(e.newValue === 'true'); + } + }; + + // Add custom event listener for same-window changes + const handleFocusModeChange = (e: CustomEvent) => { + if (pathname?.includes('/activity/')) { + setIsFocusMode(e.detail.isFocusMode); + } + }; + + window.addEventListener('storage', handleStorageChange); + window.addEventListener('focusModeChange', handleFocusModeChange as EventListener); + + // Cleanup + return () => { + window.removeEventListener('storage', handleStorageChange); + window.removeEventListener('focusModeChange', handleFocusModeChange as EventListener); + }; + }, [pathname]); function closeFeedbackModal() { setFeedbackModal(false) @@ -26,6 +62,11 @@ export const OrgMenu = (props: any) => { setIsMenuOpen(!isMenuOpen) } + // Only hide menu if we're in an activity page and focus mode is enabled + if (pathname?.includes('/activity/') && isFocusMode) { + return null; + } + return ( <>
diff --git a/apps/web/components/Pages/Activity/FixedActivitySecondaryBar.tsx b/apps/web/components/Pages/Activity/FixedActivitySecondaryBar.tsx index 41679d4e..ae86377a 100644 --- a/apps/web/components/Pages/Activity/FixedActivitySecondaryBar.tsx +++ b/apps/web/components/Pages/Activity/FixedActivitySecondaryBar.tsx @@ -160,11 +160,7 @@ export default function FixedActivitySecondaryBar(props: FixedActivitySecondaryB