mirror of
https://git.vectorsigma.ru/public/photoview.git
synced 2026-08-03 20:49:02 +00:00
Work on shared pages
This commit is contained in:
@@ -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>
|
||||
|
||||
88
ui/src/Pages/SharePage/PasswordProtectedShare.tsx
Normal file
88
ui/src/Pages/SharePage/PasswordProtectedShare.tsx
Normal 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
|
||||
@@ -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 })
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user