Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
R
rescheduled 🗓️
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Analyze
Contributor analytics
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Terms and privacy
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
c3lingo
rescheduled 🗓️
Commits
59ea2827
Commit
59ea2827
authored
2 weeks ago
by
Teal Bauer
Browse files
Options
Downloads
Patches
Plain Diff
Add dark mode toggle button
parent
d0bfe026
No related branches found
No related tags found
1 merge request
!38
Add dark mode toggle button
Pipeline
#38429
passed
2 weeks ago
Stage: test
Stage: deploy
Changes
3
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
app/controllers/users_controller.rb
+20
-0
20 additions, 0 deletions
app/controllers/users_controller.rb
app/views/layouts/application.html.erb
+86
-5
86 additions, 5 deletions
app/views/layouts/application.html.erb
config/routes.rb
+2
-0
2 additions, 0 deletions
config/routes.rb
with
108 additions
and
5 deletions
app/controllers/users_controller.rb
+
20
−
0
View file @
59ea2827
class
UsersController
<
ApplicationController
before_action
:authenticate_user!
,
only:
[
:update_theme
]
def
leaderboard
@workload_data
=
User
.
leaderboard
end
def
update_theme
if
current_user
.
present?
# Ensure darkmode parameter is valid (light, dark)
theme
=
params
[
:darkmode
].
to_s
if
[
"light"
,
"dark"
].
include?
(
theme
)
if
current_user
.
update
(
darkmode:
theme
)
render
json:
{
success:
true
,
theme:
theme
},
status: :ok
else
render
json:
{
success:
false
,
errors:
current_user
.
errors
.
full_messages
},
status: :unprocessable_entity
end
else
render
json:
{
success:
false
,
error:
"Invalid theme value"
},
status: :bad_request
end
else
render
json:
{
success:
false
,
error:
"User not authenticated"
},
status: :unauthorized
end
end
end
This diff is collapsed.
Click to expand it.
app/views/layouts/application.html.erb
+
86
−
5
View file @
59ea2827
...
...
@@ -45,7 +45,7 @@
</div>
</div>
<nav
class=
"flex items-center"
>
<nav
class=
"flex items-center
space-x-3
"
>
<%
if
user_signed_in?
%>
<!-- Desktop user navigation -->
<div
class=
"hidden md:flex md:items-center md:space-x-4"
>
...
...
@@ -66,6 +66,16 @@
<div
class=
"flex items-center ml-4"
>
<%=
render
partial:
"application/user_avatar"
,
locals:
{
user:
current_user
}
%>
</div>
<!-- Dark mode toggle button -->
<button
id=
"theme-toggle"
class=
"text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white"
aria-label=
"Toggle dark mode"
>
<svg
xmlns=
"http://www.w3.org/2000/svg"
class=
"h-5 w-5 hidden dark:block"
fill=
"none"
viewBox=
"0 0 24 24"
stroke=
"currentColor"
>
<path
stroke-linecap=
"round"
stroke-linejoin=
"round"
stroke-width=
"2"
d=
"M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"
/>
</svg>
<svg
xmlns=
"http://www.w3.org/2000/svg"
class=
"h-5 w-5 block dark:hidden"
fill=
"none"
viewBox=
"0 0 24 24"
stroke=
"currentColor"
>
<path
stroke-linecap=
"round"
stroke-linejoin=
"round"
stroke-width=
"2"
d=
"M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"
/>
</svg>
</button>
<!-- Mobile hamburger menu -->
<div
class=
"md:hidden relative ml-4"
>
...
...
@@ -100,10 +110,19 @@
<%
else
%>
<!-- Not logged in state -->
<div
class=
"flex items-center space-x-4"
>
<span
class=
"px-2 text-gray-600 dark:text-gray-300 hidden md:inline"
>
not logged in
</span>
<%=
link_to
"Assignments"
,
assignments_path
,
class:
"text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white"
%>
<%=
link_to
"Log in"
,
new_user_session_path
,
class:
"text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white"
%>
</div>
<!-- Dark mode toggle button -->
<button
id=
"theme-toggle"
class=
"text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white block"
aria-label=
"Toggle dark mode"
>
<svg
xmlns=
"http://www.w3.org/2000/svg"
class=
"h-5 w-5 hidden dark:block"
fill=
"none"
viewBox=
"0 0 24 24"
stroke=
"currentColor"
>
<path
stroke-linecap=
"round"
stroke-linejoin=
"round"
stroke-width=
"2"
d=
"M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"
/>
</svg>
<svg
xmlns=
"http://www.w3.org/2000/svg"
class=
"h-5 w-5 block dark:hidden"
fill=
"none"
viewBox=
"0 0 24 24"
stroke=
"currentColor"
>
<path
stroke-linecap=
"round"
stroke-linejoin=
"round"
stroke-width=
"2"
d=
"M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"
/>
</svg>
</button>
<!-- Mobile hamburger menu for non-logged in users -->
<div
class=
"md:hidden relative ml-4"
>
...
...
@@ -131,6 +150,56 @@
</header>
<script>
function
initializeThemeToggle
()
{
const
themeToggle
=
document
.
getElementById
(
'
theme-toggle
'
);
if
(
!
themeToggle
)
return
;
// Remove any existing event listeners to prevent duplicates
const
newThemeToggle
=
themeToggle
.
cloneNode
(
true
);
themeToggle
.
parentNode
.
replaceChild
(
newThemeToggle
,
themeToggle
);
newThemeToggle
.
addEventListener
(
'
click
'
,
function
()
{
const
htmlElement
=
document
.
documentElement
;
const
isDark
=
htmlElement
.
classList
.
contains
(
'
dark
'
);
const
newMode
=
isDark
?
'
light
'
:
'
dark
'
;
// Remove all theme classes
htmlElement
.
classList
.
remove
(
'
dark
'
,
'
light
'
,
'
auto
'
);
// Add the new theme class
htmlElement
.
classList
.
add
(
newMode
);
// For logged-in users, save preference via AJAX
if
(
window
.
userSignedIn
)
{
fetch
(
'
/users/update_theme
'
,
{
method
:
'
PATCH
'
,
headers
:
{
'
Content-Type
'
:
'
application/json
'
,
'
X-CSRF-Token
'
:
document
.
querySelector
(
'
meta[name="csrf-token"]
'
).
content
},
body
:
JSON
.
stringify
({
darkmode
:
newMode
})
}).
catch
(
error
=>
console
.
error
(
'
Error updating theme preference:
'
,
error
));
}
// For logged-out users, save preference in localStorage
else
{
localStorage
.
setItem
(
'
theme
'
,
newMode
);
}
});
// If logged out and we have a theme in localStorage, apply it
if
(
!
window
.
userSignedIn
&&
localStorage
.
getItem
(
'
theme
'
))
{
const
savedTheme
=
localStorage
.
getItem
(
'
theme
'
);
if
(
savedTheme
===
'
dark
'
||
savedTheme
===
'
light
'
)
{
const
htmlElement
=
document
.
documentElement
;
// Only apply if we're in 'auto' mode (default for logged-out users)
if
(
htmlElement
.
classList
.
contains
(
'
auto
'
))
{
htmlElement
.
classList
.
remove
(
'
auto
'
,
'
dark
'
,
'
light
'
);
htmlElement
.
classList
.
add
(
savedTheme
);
}
}
}
}
function
initializeNavMenus
()
{
// Initialize main menu for logged in users
const
menuToggle
=
document
.
getElementById
(
'
main-menu-toggle
'
);
...
...
@@ -174,12 +243,24 @@
});
}
// Set user signed in state for JS
window
.
userSignedIn
=
<%=
user_signed_in?
%>
;
// Initialize on DOMContentLoaded
document
.
addEventListener
(
'
DOMContentLoaded
'
,
initializeNavMenus
);
document
.
addEventListener
(
'
DOMContentLoaded
'
,
()
=>
{
initializeThemeToggle
();
initializeNavMenus
();
});
// Re-initialize on Turbo navigation
document
.
addEventListener
(
'
turbo:load
'
,
initializeNavMenus
);
document
.
addEventListener
(
'
turbo:render
'
,
initializeNavMenus
);
document
.
addEventListener
(
'
turbo:load
'
,
()
=>
{
initializeThemeToggle
();
initializeNavMenus
();
});
document
.
addEventListener
(
'
turbo:render
'
,
()
=>
{
initializeThemeToggle
();
initializeNavMenus
();
});
</script>
<main>
...
...
This diff is collapsed.
Click to expand it.
config/routes.rb
+
2
−
0
View file @
59ea2827
...
...
@@ -24,7 +24,9 @@ Rails.application.routes.draw do
end
get
"speakers/show"
get
"users/leaderboard"
patch
"users/update_theme"
,
to:
"users#update_theme"
# Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
# Can be used by load balancers and uptime monitors to verify that the app is live.
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment