Skip to content

Commit 12d3c69

Browse files
committed
Add DirectMessages Banner and Update All Banners (#3851)
1 parent e4ec63f commit 12d3c69

32 files changed

+427
-390
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
.root {
2+
flex-shrink: 0;
3+
}
4+
5+
.root > div:first-child {
6+
position: static;
7+
}
8+
9+
.isMobile {
10+
position: fixed;
11+
top: 0;
12+
left: 0;
13+
right: 0;
14+
z-index: 15;
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import cn from 'classnames'
2+
3+
import { isMobile } from 'utils/clientUtil'
4+
5+
import styles from './AppBannerWrapper.module.css'
6+
7+
/**
8+
* Contains all the banners. Useful for mobile web to allow this to switch to fixed positioning,
9+
* but also would like to see this control the banner list instead of App.js if it's possible to decouple some of the logic
10+
*/
11+
export const AppBannerWrapper = ({
12+
children
13+
}: {
14+
children: React.ReactNode
15+
}) => (
16+
<div className={cn(styles.root, { [styles.isMobile]: isMobile() })}>
17+
{children}
18+
</div>
19+
)
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,29 @@
11
.banner {
2-
height: var(--banner-margin);
3-
background: var(--page-header-gradient);
42
position: fixed;
3+
min-height: 56px;
54
top: 0;
6-
right: 0;
7-
left: 0;
8-
9-
z-index: 14;
5+
width: 100%;
6+
background: var(--page-header-gradient);
107
display: flex;
118
align-items: center;
129
justify-content: center;
13-
}
14-
15-
.banner.alert {
16-
background: var(--accent-red-gradient);
10+
padding: 0 var(--unit-10);
1711
}
1812

1913
.banner.isElectron {
2014
padding-left: 240px;
2115
}
2216

23-
.banner.isMobile {
24-
height: 72px;
17+
.banner.isElectron .iconRemove {
18+
left: calc(240px + var(--unit-4));
2519
}
2620

2721
.iconRemove {
28-
height: 24px;
29-
width: 24px;
22+
height: var(--unit-6);
23+
width: var(--unit-6);
3024
position: absolute;
31-
left: 8px;
25+
top: var(--unit-4);
26+
left: var(--unit-4);
3227
cursor: pointer;
3328
}
3429
.iconRemove:hover {
@@ -43,6 +38,6 @@
4338
}
4439

4540
.isMobile .iconRemove {
46-
align-self: flex-start;
47-
margin-top: 8px;
41+
top: var(--unit-2);
42+
left: var(--unit-2);
4843
}
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,25 @@
11
import cn from 'classnames'
2-
import PropTypes from 'prop-types'
32

43
import { ReactComponent as IconRemove } from 'assets/img/iconRemove.svg'
54

65
import styles from './Banner.module.css'
76

8-
const Banner = (props) => {
7+
export type BannerProps = {
8+
className?: string
9+
onClose: () => void
10+
isElectron?: boolean
11+
isMobile?: boolean
12+
children: React.ReactNode
13+
}
14+
15+
export const Banner = (props: BannerProps) => {
916
return (
1017
<div
1118
className={cn(
1219
styles.banner,
1320
{
1421
[styles.isElectron]: props.isElectron,
15-
[styles.isMobile]: props.isMobile,
16-
[styles.alert]: props.alert
22+
[styles.isMobile]: props.isMobile
1723
},
1824
props.className
1925
)}
@@ -23,17 +29,3 @@ const Banner = (props) => {
2329
</div>
2430
)
2531
}
26-
27-
Banner.propTypes = {
28-
className: PropTypes.string,
29-
onClose: PropTypes.func,
30-
isElectron: PropTypes.bool,
31-
isMobile: PropTypes.bool,
32-
alert: PropTypes.bool
33-
}
34-
35-
Banner.defaultProps = {
36-
alert: false
37-
}
38-
39-
export default Banner

apps/audius-client/packages/web/src/components/banner/CTABanner.tsx

-41
This file was deleted.

apps/audius-client/packages/web/src/components/banner/CTABanner.module.css apps/audius-client/packages/web/src/components/banner/CallToActionBanner.module.css

+20-8
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,18 @@
55
cursor: pointer;
66
display: flex;
77
justify-content: center;
8-
background: linear-gradient(358.41deg, #c049f9 0.17%, #8200e8 100%);
98
}
109

1110
.ctaBanner .content {
1211
display: flex;
1312
align-items: center;
1413
justify-content: center;
14+
gap: var(--unit-4);
15+
padding: var(--unit-4) 0;
16+
}
17+
18+
.ctaBanner.isMobile .content {
19+
gap: var(--unit-2);
1520
}
1621

1722
.ctaBanner .contentSelection {
@@ -20,15 +25,16 @@
2025
justify-content: center;
2126
transition: all ease-in-out 0.07s;
2227
border-bottom: 1px solid rgba(0, 0, 0, 0);
28+
gap: var(--unit-2);
2329
}
2430

2531
.ctaBanner:hover .contentSelection {
2632
border-bottom: 1px solid var(--white);
2733
}
2834

2935
.pill {
36+
pointer-events: none;
3037
background-color: rgba(255, 255, 255, 0.4);
31-
margin-right: 24px;
3238
}
3339

3440
.pill .pillText {
@@ -39,19 +45,25 @@
3945

4046
.text {
4147
color: var(--static-white);
42-
font-size: var(--font-m);
48+
font-size: var(--font-l);
4349
font-weight: var(--font-bold);
50+
line-height: 130%;
4451
text-align: center;
4552
}
4653

47-
.celebration {
48-
padding-right: 8px;
54+
.isMobile .text {
55+
font-size: var(--font-s);
4956
}
5057

5158
.arrow {
52-
padding-left: 8px;
53-
height: 20px;
54-
width: 20px;
59+
height: var(--unit-5);
60+
width: var(--unit-5);
61+
flex-shrink: 0;
62+
}
63+
64+
.isMobile .arrow {
65+
height: var(--unit-4);
66+
width: var(--unit-4);
5567
}
5668

5769
.arrow g path {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { ReactNode } from 'react'
2+
3+
import { Client } from '@audius/common'
4+
import { IconArrowWhite } from '@audius/stems'
5+
import cn from 'classnames'
6+
7+
import { Banner, BannerProps } from 'components/banner/Banner'
8+
import Pill from 'components/pill/Pill'
9+
import { getClient } from 'utils/clientUtil'
10+
11+
import styles from './CallToActionBanner.module.css'
12+
13+
export type CallToActionBannerProps = Pick<
14+
BannerProps,
15+
'onClose' | 'className'
16+
> & {
17+
onAccept: () => void
18+
emoji?: string
19+
pill?: string
20+
pillPosition?: 'left' | 'right'
21+
text: ReactNode
22+
}
23+
24+
export const CallToActionBanner = (props: CallToActionBannerProps) => {
25+
const {
26+
emoji,
27+
pill,
28+
pillPosition = 'left',
29+
text,
30+
onAccept,
31+
...bannerProps
32+
} = props
33+
34+
const client = getClient()
35+
36+
return (
37+
<Banner
38+
isElectron={client === Client.ELECTRON}
39+
isMobile={client === Client.MOBILE}
40+
{...bannerProps}
41+
>
42+
<div
43+
className={cn(styles.ctaBanner, {
44+
[styles.isMobile]: client === Client.MOBILE
45+
})}
46+
onClick={onAccept}
47+
>
48+
<div className={styles.content}>
49+
{pill && pillPosition === 'left' ? (
50+
<Pill
51+
className={styles.pill}
52+
textClassName={styles.pillText}
53+
showIcon={false}
54+
clickable={false}
55+
text={pill}
56+
/>
57+
) : null}
58+
<div className={styles.contentSelection}>
59+
{emoji ? <i className={cn('emoji', emoji)} /> : null}
60+
<div className={styles.text}>{text}</div>
61+
<IconArrowWhite className={styles.arrow} />
62+
</div>
63+
{pill && pillPosition === 'right' ? (
64+
<Pill
65+
className={styles.pill}
66+
textClassName={styles.pillText}
67+
showIcon={false}
68+
clickable={false}
69+
text={pill}
70+
/>
71+
) : null}
72+
</div>
73+
</div>
74+
</Banner>
75+
)
76+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { useCallback, useState } from 'react'
2+
3+
import { Client, accountSelectors } from '@audius/common'
4+
import { push as pushRoute } from 'connected-react-router'
5+
import { useDispatch } from 'react-redux'
6+
7+
import { useSelector } from 'common/hooks/useSelector'
8+
import { getClient } from 'utils/clientUtil'
9+
import { CHATS_PAGE } from 'utils/route'
10+
11+
import { CallToActionBanner } from './CallToActionBanner'
12+
13+
const messages = {
14+
text: 'Direct Messaging Now Available!',
15+
pill: 'New'
16+
}
17+
18+
const { getHasAccount } = accountSelectors
19+
20+
const DIRECT_MESSAGES_BANNER_LOCAL_STORAGE_KEY = 'dismissDirectMessagesBanner'
21+
22+
/**
23+
* Displays a CTA Banner announcing the launch of Direct Messaging
24+
* for logged in users on desktop web and desktop app (since logged out users can't use Direct Messages)
25+
*/
26+
export const DirectMessagesBanner = () => {
27+
const dispatch = useDispatch()
28+
const signedIn = useSelector(getHasAccount)
29+
const isMobile = getClient() === Client.MOBILE
30+
const hasDismissed = window.localStorage.getItem(
31+
DIRECT_MESSAGES_BANNER_LOCAL_STORAGE_KEY
32+
)
33+
const [isVisible, setIsVisible] = useState(
34+
!hasDismissed && !isMobile && signedIn
35+
)
36+
37+
const handleClose = useCallback(() => {
38+
setIsVisible(false)
39+
window.localStorage.setItem(
40+
DIRECT_MESSAGES_BANNER_LOCAL_STORAGE_KEY,
41+
'true'
42+
)
43+
}, [])
44+
45+
const handleAccept = useCallback(() => {
46+
dispatch(pushRoute(CHATS_PAGE))
47+
handleClose()
48+
}, [dispatch, handleClose])
49+
50+
return isVisible ? (
51+
<CallToActionBanner
52+
text={messages.text}
53+
pill={messages.pill}
54+
emoji={'speech-balloon'}
55+
onClose={handleClose}
56+
onAccept={handleAccept}
57+
/>
58+
) : null
59+
}

0 commit comments

Comments
 (0)