Skip to content

Commit 8fb8351

Browse files
timothyisskllcrn
authored andcommitted
Add upgraded user header and add blog link (#1038)
* Add blog link to header * Upgrade account dropdown on docs * Remove duplicate files * Fix dropdown styling
1 parent 4721a39 commit 8fb8351

13 files changed

+611
-909
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
1+
import React from 'react'
2+
import Link from 'next/link'
3+
import Router from 'next/router'
4+
import PropTypes from 'prop-types'
5+
6+
import Avatar from '~/components/avatar'
7+
import * as PopOver from '~/components/popover'
8+
import PopOverLink from '~/components/popover/popover-link'
9+
import PlusIcon from '~/components/icons/plus'
10+
import FaceIcon from '~/components/icons/emoji'
11+
12+
function ProfileItem({ user, onEditClick }) {
13+
let profileItem = null
14+
let icon = null
15+
16+
if (user.username) {
17+
// username (with or without a custom avatar)
18+
profileItem = (
19+
<div className="profile">
20+
<span className="avatar">
21+
<Avatar
22+
uid={user.uid}
23+
size={48}
24+
hash={user.avatar}
25+
title={user.username}
26+
/>
27+
</span>
28+
<div>
29+
<span className="username">{user.username}</span>
30+
</div>
31+
<style jsx>{`
32+
.profile {
33+
display: flex;
34+
}
35+
.avatar {
36+
display: inline-block;
37+
margin-right: 15px;
38+
}
39+
.profile div {
40+
display: flex;
41+
flex-direction: column;
42+
justify-content: center;
43+
}
44+
.username {
45+
font-size: 16px;
46+
color: var(--accents-7);
47+
text-decoration: none;
48+
font-weight: 500;
49+
line-height: 24px;
50+
overflow: hidden;
51+
max-width: 75px;
52+
white-space: nowrap;
53+
text-overflow: ellipsis;
54+
}
55+
.edit-profile {
56+
font-size: 14px;
57+
font-weight: 400;
58+
text-decoration: none;
59+
color: var(--geist-success);
60+
line-height: 20px;
61+
}
62+
.username,
63+
.edit-profile,
64+
.avatar {
65+
transition: opacity 0.2s ease;
66+
}
67+
`}</style>
68+
</div>
69+
)
70+
} else if (user.avatar) {
71+
// avatar (without a username)
72+
const urlEdit = `/profile`
73+
74+
profileItem = (
75+
<div className="profile">
76+
<Link href={urlEdit}>
77+
<a onClick={onEditClick} className="edit-profile">
78+
<span className="avatar">
79+
<Avatar uid={user.uid} size={32} hash={user.avatar} />
80+
</span>
81+
<span>Edit Profile</span>
82+
</a>
83+
</Link>
84+
<style jsx>{`
85+
.edit-profile {
86+
display: flex;
87+
align-items: center;
88+
}
89+
.avatar {
90+
display: inline-block;
91+
margin-right: 12px;
92+
}
93+
.edit-profile {
94+
transition: opacity 0.2s ease;
95+
font-size: 14px;
96+
color: var(--accents-2);
97+
text-decoration: none;
98+
font-weight: 400;
99+
line-height: 20px;
100+
}
101+
.edit-profile:hover {
102+
opacity: 0.8;
103+
}
104+
`}</style>
105+
</div>
106+
)
107+
} else {
108+
const urlEdit = `/profile`
109+
110+
profileItem = (
111+
<Link href={urlEdit}>
112+
<a onClick={onEditClick} className="edit-profile">
113+
Edit Profile
114+
</a>
115+
</Link>
116+
)
117+
118+
icon = <FaceIcon />
119+
}
120+
121+
return <PopOver.Item icon={icon}>{profileItem}</PopOver.Item>
122+
}
123+
124+
export default class AvatarPopOverLink extends React.Component {
125+
static contextTypes = {
126+
darkBg: PropTypes.bool
127+
}
128+
129+
constructor(props) {
130+
super(props)
131+
this.onPopOverOpen = this.onPopOverOpen.bind(this)
132+
this.state = {}
133+
}
134+
135+
onPopOverOpen() {
136+
Router.prefetch('/account/identity')
137+
Router.prefetch('/dashboard')
138+
139+
// in case user logs out, we prefetch
140+
// the page they get redirected to
141+
Router.prefetch('/login')
142+
}
143+
144+
render() {
145+
const section = this.props.pathname
146+
.split('/')
147+
.slice(0, 2)
148+
.join('/')
149+
const { user, sticky } = this.props
150+
151+
if (!user) return null
152+
153+
const top = this.props.top || 43
154+
const avatarSize = this.props.avatarSize || 30
155+
156+
return (
157+
<span className="avatar">
158+
<PopOverLink
159+
noIcon
160+
hideOnClick
161+
fixed={sticky}
162+
offsetTop={17}
163+
offsetLeft={-168}
164+
top={sticky ? '20px' : top}
165+
inline={false}
166+
onOpen={this.onPopOverOpen}
167+
ref={ref => (this.popover = ref)}
168+
to={
169+
<div className="avatar-menu">
170+
<PopOver.Menu tipOffset={173}>
171+
<ProfileItem user={this.props.user} />
172+
<PopOver.Item
173+
key="0"
174+
active={this.props.pathname === '/dashboard'}
175+
>
176+
<Link href="/dashboard">
177+
<a>Dashboard</a>
178+
</Link>
179+
</PopOver.Item>
180+
<PopOver.Item
181+
className
182+
key="1"
183+
active={this.props.pathname === '/teams/settings'}
184+
icon={<PlusIcon />}
185+
>
186+
<Link href="/teams/settings?isCreating=1" as="/teams/create">
187+
<a>New Team</a>
188+
</Link>
189+
</PopOver.Item>
190+
<PopOver.Item key="2">
191+
<Link href="/account">
192+
<a>Settings</a>
193+
</Link>
194+
</PopOver.Item>
195+
<PopOver.Item key="4">
196+
<a
197+
className={`logout ${
198+
this.state.loggingOut ? 'disabled' : ''
199+
}`}
200+
onClick={() => {
201+
this.setState({ loggingOut: true })
202+
this.props.onLogout && this.props.onLogout()
203+
}}
204+
>
205+
{this.state.loggingOut ? 'Logging out...' : 'Logout'}
206+
</a>
207+
</PopOver.Item>
208+
</PopOver.Menu>
209+
</div>
210+
}
211+
>
212+
<a
213+
href="/account"
214+
onClick={e => {
215+
if (!e.metaKey) e.preventDefault()
216+
}}
217+
className={
218+
'avatar-link ' + ('/account' === section ? 'active' : '')
219+
}
220+
>
221+
<Avatar
222+
uid={this.props.user.uid}
223+
title={this.props.user.username || this.props.user.email}
224+
size={avatarSize}
225+
hash={this.props.user.avatar}
226+
/>
227+
</a>
228+
</PopOverLink>
229+
<style jsx>{`
230+
.avatar-menu :global(.item > a.active),
231+
.avatar-menu :global(.item > a:hover) {
232+
background: var(--accents-1);
233+
}
234+
.avatar-menu :global(.item) {
235+
width: 200px;
236+
}
237+
238+
.avatar-menu :global(.item.dark .username) {
239+
color: #eaeaea;
240+
}
241+
242+
.avatar-menu :global(.item:first-child) {
243+
padding-bottom: 16px;
244+
}
245+
246+
.avatar-menu :global(.item:not(:first-child)) {
247+
border-top: 1px solid var(--accents-2);
248+
padding-top: 16px;
249+
padding-bottom: 16px;
250+
}
251+
252+
.avatar-menu :global(.item:nth-child(3)) {
253+
border: none;
254+
padding-top: 0;
255+
}
256+
.avatar-menu :global(.item:nth-child(3) .icon) {
257+
height: 35px;
258+
}
259+
260+
.avatar-menu :global(.item:last-child > a) {
261+
padding-bottom: 8px;
262+
}
263+
264+
.avatar-menu :global(.menu) {
265+
padding-bottom: 0;
266+
}
267+
268+
.logout,
269+
.dark-mode {
270+
cursor: pointer;
271+
}
272+
span.settings {
273+
font-size: 12px;
274+
}
275+
276+
.dark-mode:hover :global(.badge) {
277+
color: #000;
278+
transition: 0.2s ease;
279+
}
280+
281+
.dark-switch {
282+
margin-right: 5px;
283+
}
284+
285+
.username-wrapper {
286+
white-space: nowrap;
287+
width: 100px;
288+
overflow: hidden;
289+
text-overflow: ellipsis;
290+
}
291+
292+
.disabled {
293+
opacity: 0.5;
294+
pointer-events: none;
295+
}
296+
`}</style>
297+
</span>
298+
)
299+
}
300+
}

0 commit comments

Comments
 (0)