11// @flow
22import * as ICONS from 'constants/icons' ;
33import * as PAGES from 'constants/pages' ;
4- import React , { useRef } from 'react' ;
4+ import React , { useRef , useLayoutEffect } from 'react' ;
55import { parseURI } from 'util/lbryURI' ;
66import Button from 'component/button' ;
7- import useHover from 'effects/use-hover' ;
7+ import Icon from 'component/common/icon' ;
8+
89import { useIsMobile } from 'effects/use-screensize' ;
910import { ENABLE_UI_NOTIFICATIONS } from 'config' ;
1011import { EmbedContext } from 'contexts/embed' ;
@@ -47,8 +48,9 @@ export default function SubscribeButton(props: Props) {
4748 const isEmbed = React . useContext ( EmbedContext ) ;
4849
4950 const buttonRef = useRef ( ) ;
51+ const prevWidthRef = useRef ( null ) ;
5052 const isMobile = useIsMobile ( ) ;
51- let isHovering = useHover ( buttonRef ) ;
53+
5254 const uiNotificationsEnabled = ( user && user . experimental_ui ) || ENABLE_UI_NOTIFICATIONS ;
5355
5456 const { channelName : rawChannelName } = parseURI ( uri ) ;
@@ -67,12 +69,32 @@ export default function SubscribeButton(props: Props) {
6769
6870 const { pushSupported, pushEnabled, pushRequest, pushErrorModal } = useBrowserNotifications ( ) ;
6971
72+ useLayoutEffect ( ( ) => {
73+ const btn = buttonRef . current ;
74+ const prev = prevWidthRef . current ;
75+ if ( btn && prev !== null ) {
76+ const newWidth = Math . round ( btn . getBoundingClientRect ( ) . width ) ;
77+ const prevWidth = Math . round ( prev ) ;
78+ prevWidthRef . current = null ;
79+ if ( prevWidth !== newWidth ) {
80+ // $FlowFixMe - WAAPI
81+ const anim = btn . animate ( [ { width : prevWidth + 'px' } , { width : newWidth + 'px' } ] , {
82+ duration : 300 ,
83+ easing : 'ease' ,
84+ } ) ;
85+ anim . onfinish = ( ) => {
86+ if ( buttonRef . current ) buttonRef . current . style . width = '' ;
87+ } ;
88+ }
89+ }
90+ } , [ isSubscribed ] ) ;
91+
7092 const subscriptionHandler = isSubscribed ? doChannelUnsubscribe : doChannelSubscribe ;
7193
7294 const subscriptionLabel = isSubscribed
7395 ? __ ( 'Following --[button label indicating a channel has been followed]--' )
7496 : __ ( 'Follow' ) ;
75- const unfollowOverride = isSubscribed && isHovering && __ ( 'Unfollow' ) ;
97+ const unfollowOverride = false ;
7698
7799 const label = isMobile && shrinkOnMobile ? '' : unfollowOverride || subscriptionLabel ;
78100 const titlePrefix = isSubscribed ? __ ( 'Unfollow this channel' ) : __ ( 'Follow this channel' ) ;
@@ -116,9 +138,8 @@ export default function SubscribeButton(props: Props) {
116138 < Button
117139 ref = { buttonRef }
118140 iconColor = "red"
119- className = { isSubscribed ? 'button-following' : '' }
120- largestLabel = { isMobile && shrinkOnMobile ? '' : subscriptionLabel }
121- icon = { unfollowOverride ? ICONS . UNSUBSCRIBE : isSubscribed ? ICONS . SUBSCRIBED : ICONS . SUBSCRIBE }
141+ className = { `button-following${ isSubscribed ? ' button-following--active' : '' } ` }
142+ icon = { isSubscribed ? ICONS . SUBSCRIBED : ICONS . SUBSCRIBE }
122143 button = { 'alt' }
123144 requiresAuth = { IS_WEB }
124145 label = { label }
@@ -129,6 +150,47 @@ export default function SubscribeButton(props: Props) {
129150 ? ( e ) => {
130151 e . stopPropagation ( ) ;
131152
153+ if ( buttonRef . current ) {
154+ prevWidthRef . current = buttonRef . current . getBoundingClientRect ( ) . width ;
155+ }
156+
157+ if ( ! isSubscribed && buttonRef . current ) {
158+ // $FlowFixMe - WAAPI
159+ buttonRef . current . animate (
160+ [
161+ { boxShadow : 'inset 0 0 0 999px transparent' } ,
162+ { boxShadow : 'inset 0 0 0 999px #e9ea69' , offset : 0.25 } ,
163+ { boxShadow : 'inset 0 0 0 999px #db6a66' , offset : 0.75 } ,
164+ { boxShadow : 'inset 0 0 0 999px transparent' } ,
165+ ] ,
166+ { duration : 1000 , easing : 'linear' }
167+ ) ;
168+
169+ const btn = buttonRef . current ;
170+ const group = btn ? btn . closest ( '.button-group' ) : null ;
171+ if ( btn && group ) {
172+ group . style . position = 'relative' ;
173+ group . querySelectorAll ( '.button-following__heart-particle' ) . forEach ( ( el ) => el . remove ( ) ) ;
174+ const icon = btn . querySelector ( '.icon' ) ;
175+ if ( icon ) {
176+ const groupRect = group . getBoundingClientRect ( ) ;
177+ const iconRect = icon . getBoundingClientRect ( ) ;
178+ const cx = iconRect . left - groupRect . left + iconRect . width / 2 ;
179+ const cy = iconRect . top - groupRect . top ;
180+ for ( let i = 0 ; i < 5 ; i ++ ) {
181+ const heart = document . createElement ( 'span' ) ;
182+ heart . textContent = '\u2764' ;
183+ heart . className = 'button-following__heart-particle' ;
184+ heart . style . left = cx + ( Math . random ( ) * 20 - 10 ) + 'px' ;
185+ heart . style . top = cy + 'px' ;
186+ heart . style . animationDelay = Math . random ( ) * 0.3 + 's' ;
187+ heart . style . fontSize = 12 + Math . random ( ) * 10 + 'px' ;
188+ group . appendChild ( heart ) ;
189+ }
190+ }
191+ }
192+ }
193+
132194 subscriptionHandler (
133195 {
134196 channelName : claimName ,
@@ -140,15 +202,20 @@ export default function SubscribeButton(props: Props) {
140202 }
141203 : undefined
142204 }
143- />
144- { isSubscribed && uiNotificationsEnabled && (
145- < >
146- < Button
147- button = "alt"
148- icon = { notificationsDisabled ? ICONS . BELL : ICONS . BELL_ON }
149- className = { isSubscribed ? 'button-following' : '' }
205+ >
206+ { isSubscribed && uiNotificationsEnabled && (
207+ < span
208+ className = "button-following__bell"
209+ role = "button"
210+ tabIndex = { 0 }
150211 aria-label = { notificationsDisabled ? __ ( 'Turn on notifications' ) : __ ( 'Turn off notifications' ) }
151- onClick = { ( ) => {
212+ onClick = { ( e ) => {
213+ e . stopPropagation ( ) ;
214+ const bell = e . currentTarget ;
215+ bell . classList . add ( 'button-following__bell--ringing' ) ;
216+ bell . addEventListener ( 'animationend' , ( ) => bell . classList . remove ( 'button-following__bell--ringing' ) , {
217+ once : true ,
218+ } ) ;
152219 const newNotificationsDisabled = ! notificationsDisabled ;
153220
154221 doChannelSubscribe (
@@ -173,10 +240,12 @@ export default function SubscribeButton(props: Props) {
173240 pushRequest ( ) ;
174241 }
175242 } }
176- />
177- { pushErrorModal ( ) }
178- </ >
179- ) }
243+ >
244+ < Icon icon = { notificationsDisabled ? ICONS . BELL : ICONS . BELL_ON } size = { 16 } />
245+ </ span >
246+ ) }
247+ </ Button >
248+ { pushErrorModal ( ) }
180249 </ div >
181250 ) : null ;
182251}
0 commit comments