-
Notifications
You must be signed in to change notification settings - Fork 289
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: redesign join conversation page #15584
Changes from 2 commits
1b47b1f
024d5f7
2bb282b
9df7d5b
4ef50c6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,64 +21,92 @@ import React, {useEffect, useState} from 'react'; | |
|
||
import type {RegisterData} from '@wireapp/api-client/lib/auth'; | ||
import {BackendErrorLabel} from '@wireapp/api-client/lib/http'; | ||
import {FormattedMessage, useIntl} from 'react-intl'; | ||
import {useIntl} from 'react-intl'; | ||
import {connect} from 'react-redux'; | ||
import {Navigate} from 'react-router-dom'; | ||
import {AnyAction, Dispatch} from 'redux'; | ||
|
||
import {UrlUtil} from '@wireapp/commons'; | ||
import { | ||
ArrowIcon, | ||
Button, | ||
Column, | ||
Columns, | ||
Container, | ||
ContainerXS, | ||
Form, | ||
H1, | ||
H2, | ||
Input, | ||
InputBlock, | ||
InputSubmitCombo, | ||
Link, | ||
RoundIconButton, | ||
Small, | ||
Text, | ||
Loading, | ||
Muted, | ||
QUERY, | ||
useMatchMedia, | ||
} from '@wireapp/react-ui-kit'; | ||
|
||
import {noop} from 'Util/util'; | ||
|
||
import {ConversationJoinFull, ConversationJoinInvalid} from './ConversationJoinInvalid'; | ||
import {EntropyContainer} from './EntropyContainer'; | ||
import {Login} from './Login'; | ||
import {Page} from './Page'; | ||
|
||
import {Config} from '../../Config'; | ||
import {conversationJoinStrings} from '../../strings'; | ||
import {AppAlreadyOpen} from '../component/AppAlreadyOpen'; | ||
import {RouterLink} from '../component/RouterLink'; | ||
import {UnsupportedBrowser} from '../component/UnsupportedBrowser'; | ||
import {WirelessContainer} from '../component/WirelessContainer'; | ||
import {EXTERNAL_ROUTE} from '../externalRoute'; | ||
import {actionRoot as ROOT_ACTIONS} from '../module/action/'; | ||
import {actionRoot as ROOT_ACTIONS} from '../module/action'; | ||
import {ValidationError} from '../module/action/ValidationError'; | ||
import {bindActionCreators, RootState} from '../module/reducer'; | ||
import * as AuthSelector from '../module/selector/AuthSelector'; | ||
import * as ConversationSelector from '../module/selector/ConversationSelector'; | ||
import * as SelfSelector from '../module/selector/SelfSelector'; | ||
import {QUERY_KEY, ROUTE} from '../route'; | ||
import {QUERY_KEY} from '../route'; | ||
import * as AccentColor from '../util/AccentColor'; | ||
import {parseError, parseValidationErrors} from '../util/errorUtil'; | ||
import * as StringUtil from '../util/stringUtil'; | ||
|
||
type Props = React.HTMLProps<HTMLDivElement>; | ||
|
||
const Separator = () => { | ||
const isMobile = useMatchMedia(QUERY.mobile); | ||
const Line = () => ( | ||
<div | ||
style={{ | ||
flex: 1, | ||
height: '1px', | ||
backgroundColor: '#696c6e', | ||
minWidth: '24rem', | ||
}} | ||
></div> | ||
); | ||
return ( | ||
<div | ||
style={{ | ||
display: 'flex', | ||
alignItems: 'center', | ||
maxWidth: `${!isMobile ? '4rem' : '100%'}`, | ||
justifyContent: 'center', | ||
transform: `rotate(${!isMobile ? '90' : '0'}deg)`, | ||
marginLeft: `${!isMobile ? '' : '16px'}`, | ||
}} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. let's extract that to a |
||
> | ||
<Line /> | ||
</div> | ||
); | ||
}; | ||
|
||
const ConversationJoinComponent = ({ | ||
doCheckConversationCode, | ||
doJoinConversationByCode, | ||
doInit, | ||
doRegisterWireless, | ||
setLastEventDate, | ||
doLogout, | ||
isAuthenticated, | ||
isTemporaryGuest, | ||
selfName, | ||
conversationError, | ||
}: Props & ConnectedProps & DispatchProps) => { | ||
const nameInput = React.useRef<HTMLInputElement>(); | ||
const nameInput = React.useRef<HTMLInputElement>(null); | ||
const {formatMessage: _} = useIntl(); | ||
|
||
const [accentColor] = useState(AccentColor.random()); | ||
|
@@ -87,7 +115,6 @@ const ConversationJoinComponent = ({ | |
const [enteredName, setEnteredName] = useState<string>(''); | ||
const [error, setError] = useState<any>(); | ||
const [expiresIn, setExpiresIn] = useState<number>(); | ||
const [forceNewTemporaryGuestAccount, setForceNewTemporaryGuestAccount] = useState(false); | ||
const [isValidLink, setIsValidLink] = useState(true); | ||
const [isValidName, setIsValidName] = useState(true); | ||
const [isSubmitingName, setIsSubmitingName] = useState(false); | ||
|
@@ -201,132 +228,103 @@ const ConversationJoinComponent = ({ | |
setEnteredName(event.target.value); | ||
}; | ||
|
||
if (!isValidLink) { | ||
return <ConversationJoinInvalid />; | ||
} | ||
|
||
const isFullConversation = | ||
conversationError && conversationError.label && conversationError.label === BackendErrorLabel.TOO_MANY_MEMBERS; | ||
const renderTemporaryGuestAccountCreation = !isAuthenticated || isTemporaryGuest || forceNewTemporaryGuestAccount; | ||
|
||
if (!isValidLink) { | ||
return <Navigate to={ROUTE.CONVERSATION_JOIN_INVALID} replace />; | ||
if (isFullConversation) { | ||
return <ConversationJoinFull />; | ||
} | ||
|
||
return ( | ||
<UnsupportedBrowser isTemporaryGuest> | ||
<WirelessContainer | ||
showCookiePolicyBanner={showCookiePolicyBanner} | ||
onCookiePolicyBannerClose={() => setShowCookiePolicyBanner(false)} | ||
> | ||
{isEntropyRequired && showEntropyForm ? ( | ||
<EntropyContainer onSetEntropy={handleSubmit} /> | ||
) : isFullConversation ? ( | ||
<ContainerXS style={{margin: 'auto 0'}}> | ||
<H2 style={{fontWeight: 500, marginBottom: '10px', marginTop: '0'}} data-uie-name="status-full-headline"> | ||
<FormattedMessage {...conversationJoinStrings.fullConversationHeadline} /> | ||
</H2> | ||
<Text style={{fontSize: '1rem', marginTop: '10px'}} data-uie-name="status-full-text"> | ||
{_(conversationJoinStrings.fullConversationSubhead)} | ||
</Text> | ||
</ContainerXS> | ||
) : renderTemporaryGuestAccountCreation ? ( | ||
<div> | ||
<ContainerXS style={{margin: 'auto 0'}}> | ||
<AppAlreadyOpen /> | ||
<H2 style={{fontWeight: 500, marginBottom: '10px', marginTop: '0'}}> | ||
<FormattedMessage | ||
{...conversationJoinStrings.headline} | ||
values={{ | ||
brandName: Config.getConfig().BRAND_NAME, | ||
}} | ||
/> | ||
</H2> | ||
<Text style={{fontSize: '1rem', marginTop: '10px'}}> | ||
<FormattedMessage {...conversationJoinStrings.subhead} /> | ||
</Text> | ||
<Form style={{marginTop: 30}}> | ||
<InputBlock> | ||
<InputSubmitCombo> | ||
<Input | ||
id="enter-name" | ||
name="name" | ||
autoComplete="username" | ||
value={enteredName} | ||
ref={nameInput} | ||
onChange={onNameChange} | ||
placeholder={_(conversationJoinStrings.namePlaceholder)} | ||
maxLength={64} | ||
minLength={2} | ||
pattern=".{2,64}" | ||
required | ||
data-uie-name="enter-name" | ||
/> | ||
<RoundIconButton | ||
disabled={!enteredName || !isValidName || isSubmitingName} | ||
type="submit" | ||
formNoValidate | ||
onClick={checkNameValidity} | ||
data-uie-name="do-next" | ||
> | ||
<ArrowIcon /> | ||
</RoundIconButton> | ||
</InputSubmitCombo> | ||
</InputBlock> | ||
{error ? parseValidationErrors(error) : parseError(conversationError)} | ||
</Form> | ||
<Small block> | ||
{`${_(conversationJoinStrings.hasAccount)} `} | ||
<RouterLink | ||
to={`${ROUTE.LOGIN}/${conversationKey}/${conversationCode}`} | ||
textTransform={'none'} | ||
data-uie-name="go-login" | ||
> | ||
{_(conversationJoinStrings.loginLink)} | ||
</RouterLink> | ||
</Small> | ||
</ContainerXS> | ||
</div> | ||
) : ( | ||
<ContainerXS style={{margin: 'auto 0'}}> | ||
<AppAlreadyOpen /> | ||
<H2 style={{fontWeight: 500, marginBottom: '10px', marginTop: '0'}} data-uie-name="status-join-headline"> | ||
{selfName | ||
? _(conversationJoinStrings.existentAccountHeadline, { | ||
brandName: Config.getConfig().BRAND_NAME, | ||
name: StringUtil.capitalize(selfName), | ||
}) | ||
: _(conversationJoinStrings.headline, {brandName: Config.getConfig().BRAND_NAME})} | ||
</H2> | ||
<Text block style={{fontSize: '1rem', marginTop: '10px'}}> | ||
{_(conversationJoinStrings.existentAccountSubhead)} | ||
</Text> | ||
<Button | ||
type="button" | ||
style={{marginTop: 16}} | ||
onClick={async () => { | ||
try { | ||
const conversationEvent = await doJoinConversationByCode(conversationKey, conversationCode); | ||
routeToApp(conversationEvent.conversation, conversationEvent.qualified_conversation?.domain ?? ''); | ||
} catch (error) { | ||
console.warn('Unable to join conversation with existing account', error); | ||
} | ||
}} | ||
data-uie-name="do-open" | ||
> | ||
{_(conversationJoinStrings.existentAccountOpenButton, {brandName: Config.getConfig().BRAND_NAME})} | ||
</Button> | ||
{error ? parseValidationErrors(error) : parseError(conversationError)} | ||
<Small block> | ||
{_(conversationJoinStrings.existentAccountJoinWithoutText, { | ||
existentAccountJoinWithoutLink: ( | ||
<Link | ||
onClick={() => setForceNewTemporaryGuestAccount(true)} | ||
textTransform={'none'} | ||
data-uie-name="go-join" | ||
> | ||
{_(conversationJoinStrings.existentAccountJoinWithoutLink)} | ||
</Link> | ||
), | ||
})} | ||
</Small> | ||
</ContainerXS> | ||
)} | ||
<AppAlreadyOpen /> | ||
<div style={{display: 'flex', alignItems: 'center', flexDirection: 'column', marginBottom: '2rem'}}> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. would probably be beneficial to split that huge jsx part into mutliple smaller components |
||
<H1 style={{fontWeight: 500, marginTop: '0', marginBottom: '1rem'}} data-uie-name="status-join-headline"> | ||
{_(conversationJoinStrings.mainHeadline)} | ||
</H1> | ||
<Muted data-uie-name="status-join-subhead"> | ||
{selfName | ||
? _(conversationJoinStrings.existentAccountHeadline, { | ||
brandName: Config.getConfig().BRAND_NAME, | ||
name: `${selfName.charAt(0).toUpperCase()}${selfName.slice(1)}`, | ||
}) | ||
: _(conversationJoinStrings.headline, {brandName: Config.getConfig().BRAND_NAME})} | ||
</Muted> | ||
</div> | ||
<Columns style={{display: 'flex', gap: '2rem', alignSelf: 'center', maxWidth: '100%'}}> | ||
<Column> | ||
<Login embedded /> | ||
</Column> | ||
<Separator /> | ||
<Column> | ||
<Page> | ||
{isEntropyRequired && showEntropyForm ? ( | ||
<EntropyContainer onSetEntropy={handleSubmit} /> | ||
) : ( | ||
<Container centerText verticalCenter style={{width: '100%'}}> | ||
<Columns> | ||
<Column style={{flexBasis: 384, flexGrow: 0, padding: 0}}> | ||
<ContainerXS | ||
centerText | ||
style={{ | ||
display: 'flex', | ||
flexDirection: 'column', | ||
justifyContent: 'space-between', | ||
}} | ||
> | ||
<> | ||
<H2 center>{_(conversationJoinStrings.noAccountHead)}</H2> | ||
<Muted>{_(conversationJoinStrings.subhead)}</Muted> | ||
<Form style={{marginTop: 30}}> | ||
<InputBlock> | ||
<Input | ||
id="enter-name" | ||
name="name" | ||
autoComplete="username" | ||
value={enteredName} | ||
ref={nameInput} | ||
onChange={onNameChange} | ||
placeholder={_(conversationJoinStrings.namePlaceholder)} | ||
maxLength={64} | ||
minLength={2} | ||
pattern=".{2,64}" | ||
required | ||
data-uie-name="enter-name" | ||
/> | ||
</InputBlock> | ||
{error ? parseValidationErrors(error) : parseError(conversationError)} | ||
{isSubmitingName ? ( | ||
<Loading size={32} /> | ||
) : ( | ||
<Button | ||
block | ||
type="submit" | ||
disabled={!enteredName || !isValidName || isSubmitingName} | ||
formNoValidate | ||
onClick={checkNameValidity} | ||
aria-label={_(conversationJoinStrings.joinButton)} | ||
data-uie-name="do-join-as-guest" | ||
> | ||
{_(conversationJoinStrings.joinButton)} | ||
</Button> | ||
)} | ||
</Form> | ||
</> | ||
</ContainerXS> | ||
</Column> | ||
</Columns> | ||
</Container> | ||
)} | ||
</Page> | ||
</Column> | ||
</Columns> | ||
</WirelessContainer> | ||
</UnsupportedBrowser> | ||
); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's extract that to a style file and put it in the css property (not style)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
probably a question i will regret asking:
the rest of the file has all inline styling. it doesnt make sense to just move one style to a separate file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shall i leave all of them inline, or change all of them to a separate file 😨
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for some reason
css
is being parsed differently thanstyle
, breaking the page. as such i am leaving it as is for now.