Skip to content

Commit a8ffd60

Browse files
authored
Merge pull request #216 from medyo/feat-streaks
Feat streaks
2 parents ee24252 + d10274c commit a8ffd60

15 files changed

Lines changed: 221 additions & 28 deletions

File tree

src/assets/App.css

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1156,6 +1156,41 @@ Producthunt item
11561156
font-size: 24px;
11571157
}
11581158

1159+
/* User */
1160+
.profileImageContainer {
1161+
position: relative;
1162+
1163+
img {
1164+
width: 40px;
1165+
height: 40px;
1166+
border-radius: 50%;
1167+
border: 1px solid var(--tooltip-accent-color);
1168+
}
1169+
1170+
.streak {
1171+
position: absolute;
1172+
display: inline-block;
1173+
bottom: -12px;
1174+
left: 0;
1175+
right: 0;
1176+
color: white;
1177+
1178+
.content {
1179+
background-color: var(--tooltip-accent-color);
1180+
font-size: 10px;
1181+
border-radius: 12px;
1182+
font-weight: bold;
1183+
padding: 2px 8px;
1184+
display: inline-flex;
1185+
align-items: center;
1186+
justify-content: center;
1187+
}
1188+
.icon {
1189+
font-size: 12px;
1190+
}
1191+
}
1192+
}
1193+
11591194
/* Small devices (portrait tablets and large phones, 600px and up) */
11601195
@media only screen and (min-width: 600px) {
11611196
.floatingFilter {

src/components/Layout/AppLayout.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,25 @@
1-
import React from 'react'
1+
import React, { useEffect } from 'react'
22
import 'react-contexify/dist/ReactContexify.css'
33
import { Outlet } from 'react-router-dom'
44
import { BeatLoader } from 'react-spinners'
55
import 'src/assets/App.css'
66
import { AuthModal, useAuth } from 'src/features/auth'
7+
import { usePostStreak } from 'src/features/hits'
78
import { MarketingBanner } from 'src/features/MarketingBanner'
89
import { AuthProvider } from 'src/providers/AuthProvider'
910
import { Header } from './Header'
1011

1112
export const AppLayout = () => {
12-
const { isAuthModalOpen } = useAuth()
13+
const { isAuthModalOpen, setStreak, isConnected } = useAuth()
14+
const postStreakMutation = usePostStreak()
15+
16+
useEffect(() => {
17+
if (isConnected) {
18+
postStreakMutation.mutateAsync(undefined).then((data) => {
19+
setStreak(data.streak)
20+
})
21+
}
22+
}, [])
1323

1424
return (
1525
<AuthProvider>

src/components/Layout/Header.tsx

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,14 @@ import { BsFillBookmarksFill, BsFillGearFill, BsMoonFill } from 'react-icons/bs'
33
import { CgTab } from 'react-icons/cg'
44
import { FaUser } from 'react-icons/fa'
55
import { IoMdSunny } from 'react-icons/io'
6-
import { MdDoDisturbOff } from 'react-icons/md'
6+
import { MdDoDisturbOff, MdFlashOn } from 'react-icons/md'
77
import { Link, useLocation, useNavigate } from 'react-router-dom'
88
import { ReactComponent as HackertabLogo } from 'src/assets/logo.svg'
99
import { SearchBar } from 'src/components/Elements/SearchBar'
1010
import { UserTags } from 'src/components/Elements/UserTags'
1111
import { useAuth } from 'src/features/auth'
1212
import { Changelog } from 'src/features/changelog'
1313
import { identifyUserTheme, trackDNDDisable, trackThemeSelect } from 'src/lib/analytics'
14-
import { useBookmarks } from 'src/stores/bookmarks'
1514
import { useUserPreferences } from 'src/stores/preferences'
1615
import { Button, CircleButton } from '../Elements'
1716

@@ -20,7 +19,6 @@ export const Header = () => {
2019

2120
const [themeIcon, setThemeIcon] = useState(<BsMoonFill />)
2221
const { theme, setTheme, setDNDDuration, isDNDModeActive } = useUserPreferences()
23-
const { userBookmarks } = useBookmarks()
2422
const navigate = useNavigate()
2523
const location = useLocation()
2624

@@ -50,16 +48,6 @@ export const Header = () => {
5048
navigate('/settings/general')
5149
}
5250

53-
const BookmarksBadgeCount = () => {
54-
return userBookmarks.length > 0 ? (
55-
userBookmarks.length < 10 ? (
56-
<span className="badgeCount">{userBookmarks.length}</span>
57-
) : (
58-
<span className="badgeCount">+9</span>
59-
)
60-
) : null
61-
}
62-
6351
const onUnpauseClicked = () => {
6452
trackDNDDisable()
6553
setDNDDuration('never')
@@ -96,6 +84,7 @@ export const Header = () => {
9684
<BsFillBookmarksFill />
9785
</CircleButton>
9886
<CircleButton
87+
className="profileImageContainer"
9988
onClick={() => {
10089
if (isConnected) {
10190
navigate('/settings/general')
@@ -104,7 +93,14 @@ export const Header = () => {
10493
}
10594
}}>
10695
{isConnected ? (
107-
<img className="profileImage" src={user?.imageURL} />
96+
<>
97+
<img className="profileImage s" src={user?.imageURL} />
98+
<div className="streak">
99+
<span className="content">
100+
<MdFlashOn className="icon" /> {user?.streak || 1}
101+
</span>
102+
</div>
103+
</>
108104
) : (
109105
<FaUser style={{ fontSize: '1.2em' }} />
110106
)}

src/features/auth/hooks/useAuth.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,20 @@ import { firebaseAuth } from 'src/lib/firebase'
66
export const useAuth = () => {
77
const authModalStore = AuthModalStore()
88
const authStore = AuthStore()
9-
const { user, providerId, initState, clear } = authStore
109

11-
const isConnected = user != null
10+
const isConnected = authStore.user != null
1211

1312
const logout = async () => {
1413
trackUserDisconnect()
1514
signOut(firebaseAuth)
16-
clear()
15+
authStore.clear()
1716
return await firebaseAuth.signOut()
1817
}
1918

2019
return {
2120
...authModalStore,
22-
initState,
21+
...authStore,
2322
isConnected,
2423
logout,
25-
user,
26-
providerId,
2724
}
2825
}

src/features/auth/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ export * from './hooks/useAuth'
33
export * from './stores/authModalStore'
44
export * from './stores/authStore'
55
export * from './types'
6+
export * from './utils/auth'

src/features/auth/stores/authStore.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ type AuthState = {
99

1010
type AuthActions = {
1111
initState: (state: AuthState) => void
12+
setStreak: (streak: number) => void
1213
clear: () => void
1314
}
1415

@@ -22,6 +23,13 @@ export const AuthStore = create(
2223
user: newState.user,
2324
providerId: newState.providerId,
2425
}),
26+
setStreak: (streak: number) =>
27+
set((state) => ({
28+
user: {
29+
...state.user!,
30+
streak,
31+
},
32+
})),
2533
clear: () => set({ user: null }),
2634
}),
2735
{

src/features/auth/types/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export type User = {
22
name: string
33
imageURL?: string
4+
streak?: number
45
}

src/features/auth/utils/auth.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { firebaseAuth } from 'src/lib/firebase'
2+
3+
export const getUserToken = async () => {
4+
return new Promise((resolve, _) => {
5+
const unsub = firebaseAuth.onAuthStateChanged(async (user) => {
6+
if (user) {
7+
const token = await user.getIdToken()
8+
resolve(token)
9+
} else {
10+
console.log('User not logged in')
11+
resolve(null)
12+
}
13+
unsub()
14+
})
15+
})
16+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { useMutation } from '@tanstack/react-query'
2+
import { axios } from 'src/lib/axios'
3+
import { MutationConfig } from 'src/lib/react-query'
4+
import { Streak } from '../types'
5+
6+
const postStreak = async (): Promise<Streak> => {
7+
return axios.post('/engine/user/streak', {})
8+
}
9+
10+
type UsePostStreakOptions = {
11+
config?: MutationConfig<typeof postStreak>
12+
}
13+
export const usePostStreak = ({ config }: UsePostStreakOptions = {}) => {
14+
return useMutation({
15+
...config,
16+
mutationFn: postStreak,
17+
})
18+
}

src/features/hits/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './api/postStreak'
2+
export * from './types'

0 commit comments

Comments
 (0)