Work on shared pages

This commit is contained in:
viktorstrate
2021-07-06 16:35:10 +02:00
parent 277766cb49
commit 84ea622bc9
4 changed files with 108 additions and 71 deletions

View File

@@ -13,13 +13,13 @@ import { MediaType } from '../../../__generated__/globalTypes'
import { exhaustiveCheck } from '../../helpers/utils'
const DisplayPhoto = styled(ProtectedImage)`
width: 100%;
/* width: 100%; */
max-height: calc(80vh);
object-fit: contain;
`
const DisplayVideo = styled(ProtectedVideo)`
width: 100%;
/* width: 100%; */
max-height: calc(80vh);
`
@@ -54,7 +54,7 @@ const MediaSharePage = ({ media }: MediaSharePageType) => {
return (
<Layout title={t('share_page.media.title', 'Shared media')}>
<div data-testid="MediaSharePage">
<h1>{media.title}</h1>
<h1 className="font-semibold text-xl mb-4">{media.title}</h1>
<MediaView media={media} />
</div>
</Layout>

View File

@@ -0,0 +1,88 @@
import React, { useState } from 'react'
import { useForm } from 'react-hook-form'
import { useTranslation } from 'react-i18next'
import { TextField } from '../../primitives/form/Input'
import { MessageContainer } from './SharePage'
// import { Message, Header, Form, Input, Icon } from 'semantic-ui-react'
type ProtectedTokenEnterPasswordProps = {
refetchWithPassword(password: string): void
loading: boolean
}
const PasswordProtectedShare = ({
refetchWithPassword,
loading = false,
}: ProtectedTokenEnterPasswordProps) => {
const { t } = useTranslation()
const {
register,
watch,
formState: { errors },
handleSubmit,
} = useForm()
// const [passwordValue, setPasswordValue] = useState('')
const [invalidPassword, setInvalidPassword] = useState(false)
const onSubmit = () => {
refetchWithPassword(watch('password'))
setInvalidPassword(true)
}
let errorMessage = undefined
if (invalidPassword && !loading) {
errorMessage = t(
'share_page.wrong_password',
'Wrong password, please try again.'
)
} else if (errors.password?.type === 'required') {
errorMessage = t(
'share_page.protected_share.password_required_error',
'Password is required'
)
}
return (
<MessageContainer>
<h1 className="text-xl">
{t('share_page.protected_share.title', 'Protected share')}
</h1>
<p className="mb-4">
{t(
'share_page.protected_share.description',
'This share is protected with a password.'
)}
</p>
<TextField
{...register('password', { required: true })}
label={t('login_page.field.password', 'Password')}
type="password"
loading={loading}
disabled={loading}
action={handleSubmit(onSubmit)}
error={errorMessage}
fullWidth={true}
sizeVariant="big"
/>
{/* <Form.Field>
<label>{t('login_page.field.password', 'Password')}</label>
<Input
loading={loading}
disabled={loading}
onKeyUp={(event: KeyboardEvent) =>
event.key == 'Enter' && onSubmit()
}
onChange={e => setPasswordValue(e.target.value)}
placeholder={t('login_page.field.password', 'Password')}
type="password"
icon={<Icon onClick={onSubmit} link name="arrow right" />}
/>
</Form.Field>
{errorMessage} */}
</MessageContainer>
)
}
export default PasswordProtectedShare

View File

@@ -1,8 +1,7 @@
import PropTypes from 'prop-types'
import React, { useState } from 'react'
import React from 'react'
import { useQuery, gql } from '@apollo/client'
import { match as MatchType, Route, Switch } from 'react-router-dom'
import { Form, Header, Icon, Input, Message } from 'semantic-ui-react'
import styled from 'styled-components'
import {
getSharePassword,
@@ -11,6 +10,7 @@ import {
import AlbumSharePage from './AlbumSharePage'
import MediaSharePage from './MediaSharePage'
import { useTranslation } from 'react-i18next'
import PasswordProtectedShare from './PasswordProtectedShare'
export const SHARE_TOKEN_QUERY = gql`
query SharePageToken($token: String!, $password: String) {
@@ -133,73 +133,11 @@ AuthorizedTokenRoute.propTypes = {
match: PropTypes.object.isRequired,
}
const MessageContainer = styled.div`
export const MessageContainer = styled.div`
max-width: 400px;
margin: 100px auto 0;
`
type ProtectedTokenEnterPasswordProps = {
refetchWithPassword(password: string): void
loading: boolean
}
const ProtectedTokenEnterPassword = ({
refetchWithPassword,
loading = false,
}: ProtectedTokenEnterPasswordProps) => {
const { t } = useTranslation()
const [passwordValue, setPasswordValue] = useState('')
const [invalidPassword, setInvalidPassword] = useState(false)
const onSubmit = () => {
refetchWithPassword(passwordValue)
setInvalidPassword(true)
}
let errorMessage = null
if (invalidPassword && !loading) {
errorMessage = (
<Message negative>
<Message.Content>
{t('share_page.wrong_password', 'Wrong password, please try again.')}
</Message.Content>
</Message>
)
}
return (
<MessageContainer>
<Header as="h1" style={{ fontWeight: 400 }}>
{t('share_page.protected_share.title', 'Protected share')}
</Header>
<p>
{t(
'share_page.protected_share.description',
'This share is protected with a password.'
)}
</p>
<Form>
<Form.Field>
<label>{t('login_page.field.password', 'Password')}</label>
<Input
loading={loading}
disabled={loading}
onKeyUp={(event: KeyboardEvent) =>
event.key == 'Enter' && onSubmit()
}
onChange={e => setPasswordValue(e.target.value)}
placeholder={t('login_page.field.password', 'Password')}
type="password"
icon={<Icon onClick={onSubmit} link name="arrow right" />}
/>
</Form.Field>
{errorMessage}
</Form>
</MessageContainer>
)
}
interface TokenRouteMatch {
token: string
}
@@ -248,7 +186,7 @@ const TokenRoute = ({ match }: MatchProps<TokenRouteMatch>) => {
if (data && data.shareTokenValidatePassword == false) {
return (
<ProtectedTokenEnterPassword
<PasswordProtectedShare
refetchWithPassword={password => {
saveSharePassword(token, password)
refetch({ token, password })

View File

@@ -11,6 +11,7 @@ type TextFieldProps = {
className?: ClassNamesArg
wrapperClassName?: ClassNamesArg
sizeVariant?: 'default' | 'big'
fullWidth?: boolean
action?: () => void
loading?: boolean
} & Omit<React.InputHTMLAttributes<HTMLInputElement>, 'className'>
@@ -23,6 +24,7 @@ export const TextField = forwardRef(
className,
wrapperClassName,
sizeVariant,
fullWidth,
action,
loading,
...inputProps
@@ -58,6 +60,7 @@ export const TextField = forwardRef(
'block border rounded-md focus:ring-2 focus:outline-none px-2',
variant,
sizeVariant == 'big' ? 'py-2' : 'py-1',
{ 'w-full': fullWidth },
className
)}
{...inputProps}
@@ -77,7 +80,11 @@ export const TextField = forwardRef(
)
} else if (action) {
input = (
<div className="relative inline-block">
<div
className={classNames('relative inline-block', {
'w-full': fullWidth,
})}
>
{input}
<button
disabled={disabled}
@@ -87,7 +94,11 @@ export const TextField = forwardRef(
)}
onClick={() => action()}
>
<ActionArrowIcon />
<ActionArrowIcon
className={classNames(
sizeVariant == 'big' && 'w-4 h-4 mt-1 mr-1'
)}
/>
</button>
</div>
)