Ashhar
commited on
Commit
·
21999ba
1
Parent(s):
7822987
auth final
Browse files- app.py +46 -15
- constants.py +3 -0
- data/userActivities.py +61 -0
- helpers/activities.py +111 -0
- auth.py → helpers/auth.py +8 -4
- helpers/sidebar.py +102 -0
- icons/avatar.png +0 -0
- sidebar.py +0 -57
- utils.py +9 -0
app.py
CHANGED
@@ -13,8 +13,9 @@ from groq import Groq
|
|
13 |
|
14 |
import constants as C
|
15 |
import utils as U
|
16 |
-
from auth import
|
17 |
-
from sidebar import showSidebar
|
|
|
18 |
|
19 |
from dotenv import load_dotenv
|
20 |
load_dotenv()
|
@@ -273,7 +274,10 @@ def __predict():
|
|
273 |
|
274 |
|
275 |
def __generateImage(prompt: str):
|
276 |
-
fluxClient = Client(
|
|
|
|
|
|
|
277 |
result = fluxClient.predict(
|
278 |
prompt=prompt,
|
279 |
seed=0,
|
@@ -315,10 +319,28 @@ def __paintImageIfApplicable(
|
|
315 |
return imagePath
|
316 |
|
317 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
318 |
def __resetButtonState():
|
319 |
st.session_state.buttonValue = ""
|
320 |
|
321 |
|
|
|
|
|
|
|
|
|
322 |
def __resetSelectedStory():
|
323 |
st.session_state.selectedStory = {}
|
324 |
|
@@ -348,6 +370,16 @@ if "selectedStoryTitle" not in st.session_state:
|
|
348 |
if "isStoryChosen" not in st.session_state:
|
349 |
st.session_state.isStoryChosen = False
|
350 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
351 |
U.pprint("\n")
|
352 |
U.pprint("\n")
|
353 |
|
@@ -360,15 +392,19 @@ def mainApp():
|
|
360 |
__setStartMsg("")
|
361 |
st.button(C.START_MSG, on_click=lambda: __setStartMsg(C.START_MSG))
|
362 |
|
363 |
-
for chat in st.session_state.chatHistory:
|
364 |
role = chat["role"]
|
365 |
content = chat["content"]
|
366 |
imagePath = chat.get("image")
|
|
|
367 |
avatar = C.AI_ICON if role == "assistant" else C.USER_ICON
|
368 |
with st.chat_message(role, avatar=avatar):
|
369 |
st.markdown(content)
|
370 |
if imagePath:
|
371 |
st.image(imagePath)
|
|
|
|
|
|
|
372 |
|
373 |
# U.pprint(f"{st.session_state.buttonValue=}")
|
374 |
# U.pprint(f"{st.session_state.selectedStoryTitle=}")
|
@@ -381,6 +417,7 @@ def mainApp():
|
|
381 |
or st.session_state["startMsg"]
|
382 |
):
|
383 |
__resetButtonState()
|
|
|
384 |
__setStartMsg("")
|
385 |
if st.session_state["selectedStoryTitle"] != prompt:
|
386 |
__resetSelectedStory()
|
@@ -419,10 +456,6 @@ def mainApp():
|
|
419 |
|
420 |
U.pprint(f"{response=}")
|
421 |
|
422 |
-
def selectButton(optionLabel):
|
423 |
-
st.session_state["buttonValue"] = optionLabel
|
424 |
-
U.pprint(f"Selected: {optionLabel}")
|
425 |
-
|
426 |
rawResponse = response
|
427 |
responseParts = response.split(C.JSON_SEPARATOR)
|
428 |
|
@@ -451,12 +484,8 @@ def mainApp():
|
|
451 |
action = jsonObj.get("action")
|
452 |
|
453 |
if options:
|
454 |
-
|
455 |
-
|
456 |
-
option["label"],
|
457 |
-
key=option["id"],
|
458 |
-
on_click=lambda label=option["label"]: selectButton(label)
|
459 |
-
)
|
460 |
elif action:
|
461 |
U.pprint(f"{action=}")
|
462 |
if action == "SHOW_STORY_DATABASE":
|
@@ -466,6 +495,8 @@ def mainApp():
|
|
466 |
except Exception as e:
|
467 |
U.pprint(e)
|
468 |
|
|
|
|
|
469 |
|
470 |
-
|
471 |
showSidebar()
|
|
|
13 |
|
14 |
import constants as C
|
15 |
import utils as U
|
16 |
+
from helpers.auth import runWithAuth
|
17 |
+
from helpers.sidebar import showSidebar
|
18 |
+
from helpers.activities import saveLatestActivity
|
19 |
|
20 |
from dotenv import load_dotenv
|
21 |
load_dotenv()
|
|
|
274 |
|
275 |
|
276 |
def __generateImage(prompt: str):
|
277 |
+
fluxClient = Client(
|
278 |
+
"black-forest-labs/FLUX.1-schnell",
|
279 |
+
os.environ.get("HF_FLUX_CLIENT_TOKEN")
|
280 |
+
)
|
281 |
result = fluxClient.predict(
|
282 |
prompt=prompt,
|
283 |
seed=0,
|
|
|
319 |
return imagePath
|
320 |
|
321 |
|
322 |
+
def __selectButton(optionLabel: str):
|
323 |
+
st.session_state["buttonValue"] = optionLabel
|
324 |
+
U.pprint(f"Selected: {optionLabel}")
|
325 |
+
|
326 |
+
|
327 |
+
def __showButtons(options: list):
|
328 |
+
for option in options:
|
329 |
+
st.button(
|
330 |
+
option["label"],
|
331 |
+
key=option["id"],
|
332 |
+
on_click=lambda label=option["label"]: __selectButton(label)
|
333 |
+
)
|
334 |
+
|
335 |
+
|
336 |
def __resetButtonState():
|
337 |
st.session_state.buttonValue = ""
|
338 |
|
339 |
|
340 |
+
def __resetButtons():
|
341 |
+
st.session_state.buttons = []
|
342 |
+
|
343 |
+
|
344 |
def __resetSelectedStory():
|
345 |
st.session_state.selectedStory = {}
|
346 |
|
|
|
370 |
if "isStoryChosen" not in st.session_state:
|
371 |
st.session_state.isStoryChosen = False
|
372 |
|
373 |
+
if "buttons" not in st.session_state:
|
374 |
+
st.session_state.buttons = []
|
375 |
+
|
376 |
+
if "activityId" not in st.session_state:
|
377 |
+
st.session_state.activityId = None
|
378 |
+
|
379 |
+
if "userActivitiesLog" not in st.session_state:
|
380 |
+
st.session_state.userActivitiesLog = []
|
381 |
+
|
382 |
+
|
383 |
U.pprint("\n")
|
384 |
U.pprint("\n")
|
385 |
|
|
|
392 |
__setStartMsg("")
|
393 |
st.button(C.START_MSG, on_click=lambda: __setStartMsg(C.START_MSG))
|
394 |
|
395 |
+
for (i, chat) in enumerate(st.session_state.chatHistory):
|
396 |
role = chat["role"]
|
397 |
content = chat["content"]
|
398 |
imagePath = chat.get("image")
|
399 |
+
buttons = chat.get("buttons")
|
400 |
avatar = C.AI_ICON if role == "assistant" else C.USER_ICON
|
401 |
with st.chat_message(role, avatar=avatar):
|
402 |
st.markdown(content)
|
403 |
if imagePath:
|
404 |
st.image(imagePath)
|
405 |
+
if buttons:
|
406 |
+
__showButtons(buttons)
|
407 |
+
chat["buttons"] = []
|
408 |
|
409 |
# U.pprint(f"{st.session_state.buttonValue=}")
|
410 |
# U.pprint(f"{st.session_state.selectedStoryTitle=}")
|
|
|
417 |
or st.session_state["startMsg"]
|
418 |
):
|
419 |
__resetButtonState()
|
420 |
+
__resetButtons()
|
421 |
__setStartMsg("")
|
422 |
if st.session_state["selectedStoryTitle"] != prompt:
|
423 |
__resetSelectedStory()
|
|
|
456 |
|
457 |
U.pprint(f"{response=}")
|
458 |
|
|
|
|
|
|
|
|
|
459 |
rawResponse = response
|
460 |
responseParts = response.split(C.JSON_SEPARATOR)
|
461 |
|
|
|
484 |
action = jsonObj.get("action")
|
485 |
|
486 |
if options:
|
487 |
+
__showButtons(options)
|
488 |
+
st.session_state.buttons = options
|
|
|
|
|
|
|
|
|
489 |
elif action:
|
490 |
U.pprint(f"{action=}")
|
491 |
if action == "SHOW_STORY_DATABASE":
|
|
|
495 |
except Exception as e:
|
496 |
U.pprint(e)
|
497 |
|
498 |
+
saveLatestActivity()
|
499 |
+
|
500 |
|
501 |
+
runWithAuth(mainApp)
|
502 |
showSidebar()
|
constants.py
CHANGED
@@ -156,7 +156,10 @@ Note that the final story should include twist, turns and events that make it re
|
|
156 |
USER_ICON = "icons/man.png"
|
157 |
AI_ICON = "icons/Kommuneity.png"
|
158 |
LOGIN_ICON = "icons/authenticity.png"
|
|
|
|
|
159 |
IMAGE_LOADER = "icons/Wedges.svg"
|
160 |
TEXT_LOADER = "icons/balls.svg"
|
161 |
DB_LOADER = "icons/db_loader.svg"
|
|
|
162 |
START_MSG = "I want to create a story 😊"
|
|
|
156 |
USER_ICON = "icons/man.png"
|
157 |
AI_ICON = "icons/Kommuneity.png"
|
158 |
LOGIN_ICON = "icons/authenticity.png"
|
159 |
+
AVATAR_ICON = "icons/avatar.png"
|
160 |
+
|
161 |
IMAGE_LOADER = "icons/Wedges.svg"
|
162 |
TEXT_LOADER = "icons/balls.svg"
|
163 |
DB_LOADER = "icons/db_loader.svg"
|
164 |
+
|
165 |
START_MSG = "I want to create a story 😊"
|
data/userActivities.py
ADDED
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import datetime as DT
|
3 |
+
from typing import TypedDict, List
|
4 |
+
from supabase import create_client, Client
|
5 |
+
from zoneinfo import ZoneInfo
|
6 |
+
|
7 |
+
from dotenv import load_dotenv
|
8 |
+
load_dotenv()
|
9 |
+
|
10 |
+
url: str = os.environ.get("SUPABASE_URL")
|
11 |
+
key: str = os.environ.get("SUPABASE_KEY")
|
12 |
+
supabase: Client = create_client(url, key)
|
13 |
+
|
14 |
+
Activity = TypedDict("Activity", {
|
15 |
+
"id": str,
|
16 |
+
"email": str,
|
17 |
+
"chat_history": list,
|
18 |
+
"messages": list,
|
19 |
+
"buttons": list,
|
20 |
+
"created_at": str,
|
21 |
+
"updated_at": str,
|
22 |
+
})
|
23 |
+
|
24 |
+
|
25 |
+
def getUserActivities(emailId: str, id=None) -> List[Activity]:
|
26 |
+
filterQuery = supabase.table("user_activities") \
|
27 |
+
.select("*") \
|
28 |
+
.eq("email", emailId)
|
29 |
+
|
30 |
+
if id:
|
31 |
+
filterQuery = filterQuery.eq("id", id)
|
32 |
+
|
33 |
+
response = filterQuery \
|
34 |
+
.order("updated_at", desc=True) \
|
35 |
+
.execute()
|
36 |
+
|
37 |
+
activities = response.data
|
38 |
+
return activities or []
|
39 |
+
|
40 |
+
|
41 |
+
def createUserActivity(emailId: str, chatHistory: list, messages: list, buttons: list) -> str:
|
42 |
+
data, count = supabase.table('user_activities').insert({
|
43 |
+
"email": emailId,
|
44 |
+
"chat_history": chatHistory,
|
45 |
+
"messages": messages,
|
46 |
+
"buttons": buttons,
|
47 |
+
}).execute()
|
48 |
+
|
49 |
+
if data:
|
50 |
+
print(f"{data=}")
|
51 |
+
activityId = data[1][0]["id"]
|
52 |
+
return activityId
|
53 |
+
|
54 |
+
|
55 |
+
def updateUserActivityById(id: str, chatHistory: list, messages: list, buttons: list):
|
56 |
+
data, count = supabase.table('user_activities').update({
|
57 |
+
"chat_history": chatHistory,
|
58 |
+
"messages": messages,
|
59 |
+
"buttons": buttons,
|
60 |
+
"updated_at": DT.datetime.now(ZoneInfo("UTC")).isoformat()
|
61 |
+
}).eq("id", id).execute()
|
helpers/activities.py
ADDED
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
from typing import List, TypedDict
|
3 |
+
import utils as U
|
4 |
+
from data import userActivities
|
5 |
+
|
6 |
+
|
7 |
+
class ActivityLog(TypedDict):
|
8 |
+
id: str
|
9 |
+
created_at: str
|
10 |
+
updated_at: str
|
11 |
+
|
12 |
+
|
13 |
+
ActivityLogs = List[ActivityLog]
|
14 |
+
|
15 |
+
|
16 |
+
def saveLatestActivity():
|
17 |
+
emailId = st.session_state.get("user", {}).get("email")
|
18 |
+
if not emailId:
|
19 |
+
U.pprint("No user email found")
|
20 |
+
return
|
21 |
+
|
22 |
+
activityId = st.session_state.activityId
|
23 |
+
|
24 |
+
if activityId:
|
25 |
+
userActivities.updateUserActivityById(
|
26 |
+
id=activityId,
|
27 |
+
chatHistory=st.session_state.get("chatHistory", []),
|
28 |
+
messages=st.session_state.get("messages", []),
|
29 |
+
buttons=st.session_state.get("buttons", [])
|
30 |
+
)
|
31 |
+
U.pprint("Current activity updated")
|
32 |
+
else:
|
33 |
+
activityId = userActivities.createUserActivity(
|
34 |
+
emailId=emailId,
|
35 |
+
chatHistory=st.session_state.get("chatHistory", []),
|
36 |
+
messages=st.session_state.get("messages", []),
|
37 |
+
buttons=st.session_state.get("buttons", [])
|
38 |
+
)
|
39 |
+
U.pprint("New activity created")
|
40 |
+
st.session_state.activityId = activityId
|
41 |
+
|
42 |
+
|
43 |
+
def __convertActivitiesToLog(activities) -> ActivityLogs:
|
44 |
+
if not activities:
|
45 |
+
return []
|
46 |
+
|
47 |
+
logs = []
|
48 |
+
for activity in activities:
|
49 |
+
logs.append({
|
50 |
+
"id": activity.get("id"),
|
51 |
+
"created_at": activity.get("created_at"),
|
52 |
+
"updated_at": activity.get("updated_at"),
|
53 |
+
})
|
54 |
+
return logs
|
55 |
+
|
56 |
+
|
57 |
+
def __retrieveUserActivities():
|
58 |
+
emailId = st.session_state.get("user", {}).get("email")
|
59 |
+
if not emailId:
|
60 |
+
U.pprint("No user email found")
|
61 |
+
return
|
62 |
+
|
63 |
+
activities = userActivities.getUserActivities(emailId)
|
64 |
+
st.session_state.userActivitiesLog = __convertActivitiesToLog(activities)
|
65 |
+
U.pprint(f"{len(activities)=}")
|
66 |
+
return activities
|
67 |
+
|
68 |
+
|
69 |
+
def restoreUserActivity(activityId: str = None):
|
70 |
+
activities = __retrieveUserActivities()
|
71 |
+
if activityId:
|
72 |
+
activities = [activity for activity in activities if activity.get("id") == activityId]
|
73 |
+
|
74 |
+
if activities:
|
75 |
+
latestActivity = activities[0]
|
76 |
+
U.pprint(f"{latestActivity=}")
|
77 |
+
activityId = latestActivity.get("id")
|
78 |
+
messages = latestActivity.get("messages", [])
|
79 |
+
chatHistory = latestActivity.get("chat_history", [])
|
80 |
+
buttons = latestActivity.get("buttons", [])
|
81 |
+
|
82 |
+
st.session_state.activityId = activityId
|
83 |
+
st.session_state.messages = messages
|
84 |
+
st.session_state.chatHistory = chatHistory
|
85 |
+
|
86 |
+
if chatHistory and buttons:
|
87 |
+
st.session_state.chatHistory[-1]["buttons"] = buttons
|
88 |
+
|
89 |
+
st.rerun()
|
90 |
+
|
91 |
+
|
92 |
+
def resetActivity():
|
93 |
+
keysToDelete = [
|
94 |
+
"activityId",
|
95 |
+
"messages",
|
96 |
+
"chatHistory",
|
97 |
+
"userActivitiesLog",
|
98 |
+
"buttons",
|
99 |
+
"buttonValue",
|
100 |
+
"isStoryChosen",
|
101 |
+
"selectedStory",
|
102 |
+
"selectedStoryTitle",
|
103 |
+
"startMsg",
|
104 |
+
"isStartMsgChosen"
|
105 |
+
]
|
106 |
+
for key in keysToDelete:
|
107 |
+
if key in st.session_state:
|
108 |
+
del st.session_state[key]
|
109 |
+
|
110 |
+
__retrieveUserActivities()
|
111 |
+
st.rerun()
|
auth.py → helpers/auth.py
RENAMED
@@ -3,7 +3,8 @@ from typing import Callable, Any
|
|
3 |
import streamlit as st
|
4 |
from descope.descope_client import DescopeClient
|
5 |
from descope.exceptions import AuthException
|
6 |
-
|
|
|
7 |
|
8 |
from dotenv import load_dotenv
|
9 |
load_dotenv()
|
@@ -12,7 +13,7 @@ DESCOPE_PROJECT_ID = os.environ.get("DESCOPE_PROJECT_ID")
|
|
12 |
descopeClient = DescopeClient(project_id=DESCOPE_PROJECT_ID)
|
13 |
|
14 |
|
15 |
-
def
|
16 |
if "token" not in st.session_state:
|
17 |
if "code" in st.query_params:
|
18 |
code = st.query_params["code"]
|
@@ -25,11 +26,13 @@ def authenticateFunc(func: Callable[[], Any]):
|
|
25 |
"jwt"
|
26 |
)
|
27 |
st.session_state["user"] = jwtResponse["user"]
|
|
|
|
|
28 |
st.rerun()
|
29 |
except AuthException:
|
30 |
st.error("Login failed!")
|
31 |
|
32 |
-
st.warning("
|
33 |
with st.container(border=False):
|
34 |
if st.button(
|
35 |
"Sign in with Google",
|
@@ -47,11 +50,12 @@ def authenticateFunc(func: Callable[[], Any]):
|
|
47 |
)
|
48 |
else:
|
49 |
try:
|
50 |
-
with st.spinner("
|
51 |
jwtResponse = descopeClient.validate_and_refresh_session(
|
52 |
st.session_state.token, st.session_state.refreshToken
|
53 |
)
|
54 |
st.session_state["token"] = jwtResponse["sessionToken"].get("jwt")
|
|
|
55 |
|
56 |
func()
|
57 |
except AuthException:
|
|
|
3 |
import streamlit as st
|
4 |
from descope.descope_client import DescopeClient
|
5 |
from descope.exceptions import AuthException
|
6 |
+
from helpers.activities import restoreUserActivity
|
7 |
+
import utils as U
|
8 |
|
9 |
from dotenv import load_dotenv
|
10 |
load_dotenv()
|
|
|
13 |
descopeClient = DescopeClient(project_id=DESCOPE_PROJECT_ID)
|
14 |
|
15 |
|
16 |
+
def runWithAuth(func: Callable[[], Any]):
|
17 |
if "token" not in st.session_state:
|
18 |
if "code" in st.query_params:
|
19 |
code = st.query_params["code"]
|
|
|
26 |
"jwt"
|
27 |
)
|
28 |
st.session_state["user"] = jwtResponse["user"]
|
29 |
+
with st.spinner("Restoring your chats ..."):
|
30 |
+
restoreUserActivity()
|
31 |
st.rerun()
|
32 |
except AuthException:
|
33 |
st.error("Login failed!")
|
34 |
|
35 |
+
st.warning("Login to Unlock the Magic ✨", icon=":material/login:")
|
36 |
with st.container(border=False):
|
37 |
if st.button(
|
38 |
"Sign in with Google",
|
|
|
50 |
)
|
51 |
else:
|
52 |
try:
|
53 |
+
with st.spinner("Verifying your identity ..."):
|
54 |
jwtResponse = descopeClient.validate_and_refresh_session(
|
55 |
st.session_state.token, st.session_state.refreshToken
|
56 |
)
|
57 |
st.session_state["token"] = jwtResponse["sessionToken"].get("jwt")
|
58 |
+
U.pprint("User successfully authenticated!")
|
59 |
|
60 |
func()
|
61 |
except AuthException:
|
helpers/sidebar.py
ADDED
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import requests
|
3 |
+
import datetime as DT
|
4 |
+
from helpers.activities import resetActivity, restoreUserActivity, ActivityLogs
|
5 |
+
import constants as C
|
6 |
+
import utils as U
|
7 |
+
|
8 |
+
|
9 |
+
def isValidImageUrl(url):
|
10 |
+
if not url:
|
11 |
+
return False
|
12 |
+
try:
|
13 |
+
imageResponse = requests.head(url, timeout=5)
|
14 |
+
contentType = imageResponse.headers.get('Content-Type', '')
|
15 |
+
return imageResponse.status_code == 200 and contentType.startswith('image/')
|
16 |
+
except Exception:
|
17 |
+
return False
|
18 |
+
|
19 |
+
|
20 |
+
def showSidebar():
|
21 |
+
with st.sidebar:
|
22 |
+
if not ("user" in st.session_state and "token" in st.session_state):
|
23 |
+
return
|
24 |
+
|
25 |
+
st.markdown("""
|
26 |
+
<style>
|
27 |
+
.user-info {
|
28 |
+
display: flex;
|
29 |
+
align-items: center;
|
30 |
+
padding: 10px;
|
31 |
+
background-color: rgba(255, 255, 255, 0.1);
|
32 |
+
border-radius: 5px;
|
33 |
+
margin-bottom: 10px;
|
34 |
+
margin-top: -2rem;
|
35 |
+
}
|
36 |
+
.user-avatar {
|
37 |
+
width: 40px;
|
38 |
+
height: 40px;
|
39 |
+
border-radius: 50%;
|
40 |
+
margin-right: 10px;
|
41 |
+
}
|
42 |
+
.user-details {
|
43 |
+
flex-grow: 1;
|
44 |
+
}
|
45 |
+
.user-name {
|
46 |
+
font-weight: bold;
|
47 |
+
margin: 0;
|
48 |
+
}
|
49 |
+
.user-email {
|
50 |
+
font-size: 0.8em;
|
51 |
+
color: #888;
|
52 |
+
margin: 0;
|
53 |
+
}
|
54 |
+
</style>
|
55 |
+
""", unsafe_allow_html=True)
|
56 |
+
|
57 |
+
userAvatar = st.session_state["user"].get("picture", "https://example.com/default-avatar.png")
|
58 |
+
if not isValidImageUrl(userAvatar):
|
59 |
+
userAvatar = C.AVATAR_ICON
|
60 |
+
userName = st.session_state["user"].get("name", "User")
|
61 |
+
userEmail = st.session_state["user"].get("email", "")
|
62 |
+
|
63 |
+
st.markdown(f"""
|
64 |
+
<div class="user-info">
|
65 |
+
<img src="{userAvatar}" class="user-avatar">
|
66 |
+
<div class="user-details">
|
67 |
+
<p class="user-name">{userName}</p>
|
68 |
+
<p class="user-email">{userEmail}</p>
|
69 |
+
</div>
|
70 |
+
</div>
|
71 |
+
""", unsafe_allow_html=True)
|
72 |
+
|
73 |
+
if st.button("Logout", key="logout_button", type="secondary", use_container_width=True):
|
74 |
+
for key in ["token", "user"]:
|
75 |
+
if key in st.session_state:
|
76 |
+
del st.session_state[key]
|
77 |
+
st.rerun()
|
78 |
+
|
79 |
+
st.markdown("---")
|
80 |
+
|
81 |
+
if st.button("\\+ New Story", help="Your current story will be auto-saved 😊", key="save_activities_button", type="primary", use_container_width=True):
|
82 |
+
resetActivity()
|
83 |
+
|
84 |
+
userActivityLogs: ActivityLogs = st.session_state.get("userActivitiesLog", [])
|
85 |
+
U.pprint(f"{userActivityLogs=}")
|
86 |
+
|
87 |
+
if not userActivityLogs:
|
88 |
+
return
|
89 |
+
|
90 |
+
st.markdown("""
|
91 |
+
---
|
92 |
+
### Your Past Stories
|
93 |
+
""")
|
94 |
+
with st.container(height=300, border=False):
|
95 |
+
for log in userActivityLogs:
|
96 |
+
updatedAt = DT.datetime.fromisoformat(log["updated_at"].replace("Z", "+00:00"))
|
97 |
+
localTime = updatedAt.astimezone().strftime("%b %d, %I:%M %p")
|
98 |
+
activityId = log["id"]
|
99 |
+
if activityId == st.session_state.get("activityId"):
|
100 |
+
continue
|
101 |
+
if st.button(f"Saved ⌛ {localTime}", key=f"activity_{log['id']}", use_container_width=True):
|
102 |
+
restoreUserActivity(log["id"])
|
icons/avatar.png
ADDED
sidebar.py
DELETED
@@ -1,57 +0,0 @@
|
|
1 |
-
import streamlit as st
|
2 |
-
|
3 |
-
|
4 |
-
def showSidebar():
|
5 |
-
with st.sidebar:
|
6 |
-
if "user" in st.session_state and "token" in st.session_state:
|
7 |
-
st.markdown("""
|
8 |
-
<style>
|
9 |
-
.user-info {
|
10 |
-
display: flex;
|
11 |
-
align-items: center;
|
12 |
-
padding: 10px;
|
13 |
-
background-color: rgba(255, 255, 255, 0.1);
|
14 |
-
border-radius: 5px;
|
15 |
-
margin-bottom: 10px;
|
16 |
-
margin-top: -2rem;
|
17 |
-
}
|
18 |
-
.user-avatar {
|
19 |
-
width: 40px;
|
20 |
-
height: 40px;
|
21 |
-
border-radius: 50%;
|
22 |
-
margin-right: 10px;
|
23 |
-
}
|
24 |
-
.user-details {
|
25 |
-
flex-grow: 1;
|
26 |
-
}
|
27 |
-
.user-name {
|
28 |
-
font-weight: bold;
|
29 |
-
margin: 0;
|
30 |
-
}
|
31 |
-
.user-email {
|
32 |
-
font-size: 0.8em;
|
33 |
-
color: #888;
|
34 |
-
margin: 0;
|
35 |
-
}
|
36 |
-
</style>
|
37 |
-
""", unsafe_allow_html=True)
|
38 |
-
|
39 |
-
user_avatar = st.session_state["user"].get("picture", "https://example.com/default-avatar.png")
|
40 |
-
user_name = st.session_state["user"].get("name", "User")
|
41 |
-
user_email = st.session_state["user"].get("email", "")
|
42 |
-
|
43 |
-
st.markdown(f"""
|
44 |
-
<div class="user-info">
|
45 |
-
<img src="{user_avatar}" class="user-avatar">
|
46 |
-
<div class="user-details">
|
47 |
-
<p class="user-name">{user_name}</p>
|
48 |
-
<p class="user-email">{user_email}</p>
|
49 |
-
</div>
|
50 |
-
</div>
|
51 |
-
""", unsafe_allow_html=True)
|
52 |
-
|
53 |
-
if st.button("Logout", key="logout_button", type="secondary", use_container_width=True):
|
54 |
-
for key in ["token", "user"]:
|
55 |
-
if key in st.session_state:
|
56 |
-
del st.session_state[key]
|
57 |
-
st.rerun()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils.py
CHANGED
@@ -115,6 +115,15 @@ def applyCommonStyles():
|
|
115 |
background-color: #1a1c23 !important;
|
116 |
}
|
117 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
118 |
</style>
|
119 |
""",
|
120 |
unsafe_allow_html=True
|
|
|
115 |
background-color: #1a1c23 !important;
|
116 |
}
|
117 |
|
118 |
+
div[data-testid="stSidebarUserContent"] {
|
119 |
+
padding-bottom: 1rem !important;
|
120 |
+
}
|
121 |
+
|
122 |
+
div[data-testid="stMarkdownContainer"] > hr {
|
123 |
+
margin-top: 0.5rem;
|
124 |
+
margin-bottom: 1rem;
|
125 |
+
}
|
126 |
+
|
127 |
</style>
|
128 |
""",
|
129 |
unsafe_allow_html=True
|