Skip to content

Commit f689648

Browse files
committed
feat: inapp acode account login
1 parent 163e533 commit f689648

File tree

5 files changed

+494
-4
lines changed

5 files changed

+494
-4
lines changed

src/components/sidebar/index.js

Lines changed: 145 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import toast from "components/toast";
12
import "./style.scss";
23
import Ref from "html-tag-js/ref";
34
import actionStack from "lib/actionStack";
5+
import auth, { loginEvents } from "lib/auth";
46
import constants from "lib/constants";
57

68
let $sidebar;
@@ -33,6 +35,8 @@ function create($container, $toggler) {
3335
const MIN_WIDTH = 200; //Min width of the side bar
3436
const MAX_WIDTH = () => innerWidth * 0.7; //Max width of the side bar
3537
const resizeBar = new Ref();
38+
const userAvatar = new Ref();
39+
const userContextMenu = new Ref();
3640

3741
$container = $container || app;
3842
let mode = innerWidth > 600 ? "tab" : "phone";
@@ -41,13 +45,34 @@ function create($container, $toggler) {
4145
const eventOptions = { passive: false };
4246
const $el = (
4347
<div id="sidebar" className={mode}>
44-
<div className="apps"></div>
48+
<div className="apps">
49+
<div className="app-icons-container"></div>
50+
<div
51+
ref={userAvatar}
52+
className="user-icon-container"
53+
onclick={handleUserIconClick}
54+
>
55+
<span className="icon account_circle"></span>
56+
</div>
57+
</div>
4558
<div className="container"></div>
4659
<div
4760
className="resize-bar w-resize"
4861
onmousedown={onresize}
4962
ontouchstart={onresize}
5063
></div>
64+
65+
<div ref={userContextMenu} className="user-menu">
66+
<div className="user-menu-header">
67+
<div className="user-menu-name"></div>
68+
<div className="user-menu-email"></div>
69+
</div>
70+
{/* <div className="user-menu-separator"></div> */}
71+
<div className="user-menu-item" onclick={handleLogout}>
72+
<span className="icon logout"></span>
73+
{strings.logout}
74+
</div>
75+
</div>
5176
</div>
5277
);
5378
const mask = <span className="mask" onclick={hide}></span>;
@@ -72,6 +97,113 @@ function create($container, $toggler) {
7297
show();
7398
}
7499

100+
loginEvents.on(() => {
101+
updateSidebarAvatar();
102+
});
103+
104+
async function handleUserIconClick(e) {
105+
try {
106+
const isLoggedIn = await auth.isLoggedIn();
107+
108+
if (!isLoggedIn) {
109+
auth.openLoginUrl();
110+
} else {
111+
toggleUserMenu();
112+
}
113+
} catch (error) {
114+
console.error("Error checking login status:", error);
115+
toast("Error checking login status", 3000);
116+
}
117+
}
118+
119+
function toggleUserMenu() {
120+
const menu = userContextMenu.el;
121+
const isActive = menu.classList.toggle("active");
122+
123+
if (isActive) {
124+
// Populate user info
125+
updateUserMenuInfo();
126+
127+
// Add click outside listener
128+
setTimeout(() => {
129+
document.addEventListener("click", handleClickOutside);
130+
}, 10);
131+
} else {
132+
document.removeEventListener("click", handleClickOutside);
133+
}
134+
}
135+
136+
function handleClickOutside(e) {
137+
if (
138+
!userContextMenu.el.contains(e.target) &&
139+
e.target !== userAvatar.el &&
140+
!userAvatar.el.contains(e.target)
141+
) {
142+
userContextMenu.el.classList.remove("active");
143+
document.removeEventListener("click", handleClickOutside);
144+
}
145+
}
146+
147+
async function updateUserMenuInfo() {
148+
try {
149+
const userInfo = await auth.getUserInfo();
150+
if (userInfo) {
151+
const menuName = userContextMenu.el.querySelector(".user-menu-name");
152+
const menuEmail = userContextMenu.el.querySelector(".user-menu-email");
153+
menuName.textContent = userInfo.name || "Anonymous";
154+
if (userInfo.isAdmin) {
155+
menuName.innerHTML += ' <span class="badge">Admin</span>';
156+
}
157+
menuEmail.textContent = userInfo.email || "";
158+
}
159+
} catch (error) {
160+
console.error("Error fetching user info:", error);
161+
}
162+
}
163+
164+
async function handleLogout() {
165+
try {
166+
const success = await auth.logout();
167+
if (success) {
168+
userContextMenu.el.classList.remove("active");
169+
document.removeEventListener("click", handleClickOutside);
170+
toast("Logged out successfully");
171+
updateSidebarAvatar();
172+
} else {
173+
toast("Failed to logout");
174+
}
175+
} catch (error) {
176+
console.error("Error during logout:", error);
177+
}
178+
}
179+
180+
async function updateSidebarAvatar() {
181+
const avatarUrl = await auth.getAvatar();
182+
// Remove existing icon or avatar
183+
const existingIcon = userAvatar.el.querySelector(".icon");
184+
const existingAvatar = userAvatar.el.querySelector(".avatar");
185+
186+
if (existingIcon) {
187+
existingIcon.remove();
188+
}
189+
if (existingAvatar) {
190+
existingAvatar.remove();
191+
}
192+
193+
if (avatarUrl?.startsWith("data:") || avatarUrl?.startsWith("http")) {
194+
// Create and add avatar image
195+
const avatarImg = document.createElement("img");
196+
avatarImg.className = "avatar";
197+
avatarImg.src = avatarUrl;
198+
userAvatar.append(avatarImg);
199+
} else {
200+
// Fallback to default icon
201+
const defaultIcon = document.createElement("span");
202+
defaultIcon.className = "icon account_circle";
203+
userAvatar.append(defaultIcon);
204+
}
205+
}
206+
75207
function onWindowResize() {
76208
clearTimeout(resizeTimeout);
77209
resizeTimeout = setTimeout(() => {
@@ -148,9 +280,20 @@ function create($container, $toggler) {
148280
openedFolders = [];
149281
}
150282

151-
function onshow() {
283+
async function onshow() {
152284
if ($el.onshow) $el.onshow.call($el);
153285
events.show.forEach((fn) => fn());
286+
287+
// try {
288+
// if (await auth.isLoggedIn()) {
289+
// const avatar = await auth.getAvatar();
290+
// if (avatar) {
291+
// auth.updateSidebarAvatar(avatar);
292+
// }
293+
// }
294+
// } catch (error) {
295+
// console.error("Error updating avatar:", error);
296+
// }
154297
}
155298

156299
function onhide() {

src/components/sidebar/style.scss

Lines changed: 102 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,47 @@ body.no-animation {
5959
min-width: 45px;
6060
height: 100%;
6161
background-color: rgba(0, 0, 0, 0.2);
62-
overflow: auto;
6362
padding: 10px 0;
63+
display: flex;
64+
flex-direction: column;
65+
position: relative;
66+
.app-icons-container {
67+
flex: 1;
68+
overflow-y: auto;
69+
padding: 10px 0;
70+
}
71+
.user-icon-container {
72+
position: sticky;
73+
bottom: 0;
74+
width: 100%;
75+
padding: 10px 0;
76+
display: flex;
77+
justify-content: center;
78+
border-top: 1px solid var(--border-color);
79+
.avatar {
80+
display: block;
81+
height: 35px;
82+
width: 35px;
83+
border-radius: 50%;
84+
object-fit: cover;
85+
object-position: center;
86+
}
87+
.icon {
88+
height: 40px;
89+
width: 40px;
90+
color: currentColor;
91+
font-size: 1.6em;
92+
border-radius: 12px;
93+
opacity: 0.5;
94+
transition: all 0.2s ease;
95+
margin: 0 auto;
96+
cursor: pointer;
6497

98+
&.active {
99+
opacity: 1;
100+
}
101+
}
102+
}
65103
.icon {
66104
height: 35px;
67105
width: 35px;
@@ -210,3 +248,66 @@ body.no-animation {
210248
}
211249
}
212250
}
251+
252+
.user-menu {
253+
position: absolute;
254+
bottom: 55px;
255+
left: 30px;
256+
width: 200px;
257+
background-color: var(--popup-background-color);
258+
border: 1px solid var(--border-color);
259+
border-radius: 6px;
260+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
261+
display: none;
262+
z-index: 1000;
263+
overflow: hidden;
264+
&.active {
265+
display: block;
266+
}
267+
.user-menu-header {
268+
padding: 12px;
269+
border-bottom: 1px solid var(--border-color);
270+
271+
.user-menu-name {
272+
font-weight: 500;
273+
margin-bottom: 4px;
274+
275+
.badge {
276+
display: inline-flex;
277+
align-items: center;
278+
background-color: color-mix(
279+
in srgb,
280+
var(--error-text-color) 20%,
281+
transparent
282+
);
283+
color: var(--error-text-color);
284+
padding: 2px 6px;
285+
border-radius: 4px;
286+
font-size: 12px;
287+
font-weight: 500;
288+
margin-left: 8px;
289+
}
290+
}
291+
292+
.user-menu-email {
293+
font-size: 12px;
294+
color: color-mix(in srgb, var(--popup-text-color) 70%, transparent);
295+
}
296+
}
297+
298+
.user-menu-item {
299+
padding: 10px 12px;
300+
cursor: pointer;
301+
display: flex;
302+
align-items: center;
303+
gap: 8px;
304+
&:hover {
305+
background-color: var(--active-icon-color);
306+
}
307+
}
308+
.user-menu-separator {
309+
height: 1px;
310+
background-color: var(--border-color);
311+
margin: 4px 0;
312+
}
313+
}

0 commit comments

Comments
 (0)