Commit b4ede1e2 authored by Shpuld Shpludson's avatar Shpuld Shpludson

Merge branch 'feat/post-status-form-validation' into 'master'

Move out Error component to a separate file, add validation to post status form

See merge request !68
parents 127d53b3 eb42bd96
Pipeline #29014 passed with stages
in 13 minutes and 54 seconds
......@@ -227,6 +227,20 @@ body {
animation: size-up-down 0.5s ease-in-out 1;
}
@keyframes pop-out {
from {
height: 0;
opacity: 0;
} to {
height: 2rem;
opacity: 1;
}
}
.pop-out {
animation: pop-out .3s linear 1;
}
@responsive {
.max-w-time {
max-width: 4.375rem;
......
import React from 'react'
export const Error: React.FunctionComponent<{cssClass?: string, error?: string}> = props => (
<div className={`pop-out background-error mt-4 text-center ${props.cssClass}`}>{ props.error}</div>
)
......@@ -11,8 +11,7 @@ import { validate } from '../../utils/validators'
import Icons from '../common/icons/icons'
import { InstanceSwitcher } from './instance_switcher'
import { IsMobileContext } from '../../isMobileContext'
export const Error = ({ error }: { error: string }) => <div className='p-2 background-error mt-4 text-center'>{ error }</div>
import { Error } from '../common/error/error'
interface InstanceSelectionInterface extends RouteComponentProps{
config: any;
......
......@@ -4,7 +4,7 @@ import { withRouter, Redirect } from 'react-router'
import { Link, RouteComponentProps } from 'react-router-dom'
import { oauthThunks } from '../../thunks/oauth'
import { FormattedMessage } from 'react-intl'
import { Error } from '../instance_selection/instance_selection'
import { Error } from '../common/error/error'
import { Input, generateErrorMessage } from '../input/input'
import { validate } from '../../utils/validators'
import Icons from '../common/icons/icons'
......
......@@ -15,13 +15,8 @@ import debounce from 'lodash/debounce'
import Attachment from '../../entities/attachment'
import { Attachments } from '../attachments/attachments'
import { getUserSuggestions } from '../../utils/suggestor'
export const statusVisibilityOptions: { value: string, text?: string, textId: string, icon: Component }[] = [
{ value: 'public', textId: 'postStatusForm.scope.public', icon: Icons.Globe },
{ value: 'unlisted', textId: 'postStatusForm.scope.unlisted', icon: Icons.OpenLock },
{ value: 'private', textId: 'postStatusForm.scope.private', icon: Icons.Lock },
{ value: 'direct', textId: 'postStatusForm.scope.direct', icon: Icons.Envelope }
]
import { Error as ErrorLabel } from '../common/error/error'
import { STATUS_VISIBILITY_OPTIONS } from '../../constants'
interface PostStatusFormInterface extends WrappedComponentProps{
repliedStatus?: Status,
......@@ -46,7 +41,8 @@ type PostStatusFormState = {
},
suggestions: Account[],
autocompleteRequest: string,
searchRequestsCounter: number
searchRequestsCounter: number,
errorText: string
}
const defaultFormData = {
spoiler_text: '',
......@@ -59,7 +55,8 @@ const defaultFormData = {
poll: {},
suggestions: [],
autocompleteRequest: '',
searchRequestsCounter: 0
searchRequestsCounter: 0,
errorText: ''
}
class PostStatusFormComponent extends Component<PostStatusFormInterface, PostStatusFormState> {
constructor (props: PostStatusFormInterface) {
......@@ -97,8 +94,11 @@ class PostStatusFormComponent extends Component<PostStatusFormInterface, PostSta
}
getStatus = () => {
const { spoiler_text, status, media, visibility, in_reply_to_id }: Partial<Status> = this.state
const { spoiler_text, status, media, visibility, in_reply_to_id, poll }: Partial<Status> = this.state
if (!status && !media.length) {
throw new Error(this.props.intl.formatMessage({ id: 'postStatusForm.emptyStatusError' }))
}
const statusObj: Partial<Status> = {
spoiler_text,
status,
......@@ -106,10 +106,10 @@ class PostStatusFormComponent extends Component<PostStatusFormInterface, PostSta
in_reply_to_id,
visibility,
}
if (Object.keys(this.state.poll).length) {
if (Object.keys(poll).length) {
statusObj.poll = {
...this.state.poll,
options: this.state.poll.options.filter(option => option.length)
...poll,
options: poll.options.filter((option: any) => option.length)
}
}
return statusObj
......@@ -117,9 +117,17 @@ class PostStatusFormComponent extends Component<PostStatusFormInterface, PostSta
postStatus = async () => {
const { postStatus, onPost } = this.props
postStatus && await postStatus(this.getStatus())
this.close()
onPost && onPost()
try {
postStatus && await postStatus(this.getStatus())
this.close()
onPost && onPost()
} catch (e) {
this.setState({ errorText: e.message }, () => {
setTimeout(() => {
this.setState({ errorText: '' })
}, 4000)
})
}
}
clear = () => {
......@@ -242,8 +250,8 @@ class PostStatusFormComponent extends Component<PostStatusFormInterface, PostSta
render() {
const { repliedStatus, intl } = this.props
const { isOpen, spoiler_text, status, visibility, showPollPanel, in_reply_to_id, media } = this.state
const visibilityOptions = statusVisibilityOptions.map(item => ({
const { isOpen, spoiler_text, status, visibility, showPollPanel, in_reply_to_id, media, errorText } = this.state
const visibilityOptions = STATUS_VISIBILITY_OPTIONS.map(item => ({
...item,
text: intl.formatMessage({ id: item.textId })
}))
......@@ -273,6 +281,7 @@ class PostStatusFormComponent extends Component<PostStatusFormInterface, PostSta
<FormattedMessage id='postStatusForm.publish' defaultMessage='Publish' />
</button>
</div>
{!!errorText && <ErrorLabel cssClass='mb-4' error={errorText} />}
<input
className='w-full input focus:outline-none mb-4 rounded-sm text-lg text'
value={spoiler_text}
......@@ -336,8 +345,9 @@ const mapStateToProps = ({ users }: { users: any }) => ({
const mapDispatchToProps = (dispatch: Function) => ({ dispatch })
const mergeProps = (stateProps: any, { dispatch }: { dispatch: Function }, ownProps: any) => ({
postStatus: async (params: any) => {
await dispatch(Pleroma.thunks.statuses.postStatus({ params }))
const response = await dispatch(Pleroma.thunks.statuses.postStatus({ params }))
dispatch(Pleroma.reducers.users.actions.updateCurrentUser({ ...stateProps.currentUser, statuses_count: stateProps.currentUser.statuses_count + 1 }))
return response
},
updateThread: (id: string) => dispatch(Pleroma.thunks.statuses.getStatusWithContext({ params: { id } })),
searchUsers: (q: string) => dispatch(Pleroma.thunks.users.searchUsers({ queries: { q } })),
......
......@@ -9,7 +9,7 @@ import { oauthThunks } from '../../thunks/oauth'
import { validate } from '../../utils/validators'
import { SignUpData, SignUpType, SignUpStateType } from './sign_up_types'
import { InstanceSwitcher } from '../instance_selection/instance_switcher'
import { Error } from '../instance_selection/instance_selection'
import { Error } from '../common/error/error'
class SignUpComponent extends Component<SignUpType, SignUpStateType> {
constructor(props: SignUpType) {
......
......@@ -6,7 +6,7 @@ import { PostStatusForm } from '../post_status_form/post_status_form.tsx'
import { FormattedMessage } from 'react-intl'
import { Popover, openHandler } from '../common/popover/popover'
import { Tooltip } from '../common/tooltip/tooltip'
import { statusVisibilityOptions } from '../post_status_form/post_status_form'
import { STATUS_VISIBILITY_OPTIONS } from '../../constants'
const StatusActionsComponent = ({
status,
......@@ -75,7 +75,7 @@ const StatusActionsComponent = ({
}
const renderVisibilityIndicator = () => {
const visibility = statusVisibilityOptions.find(({ value }) => value === status.visibility)
const visibility = STATUS_VISIBILITY_OPTIONS.find(({ value }) => value === status.visibility)
return <Tooltip
target={React.createElement(visibility.icon, { className: 'h-8 w-8 text-secondary opacity-50' })}
......
import { Component } from 'react';
import Icons from './components/common/icons/icons'
export const APP_ID = 'kenoma'
export const GITLAB_REPO_URL = 'https://git.pleroma.social/lambadalambda/kenoma/commit'
export const STATUS_VISIBILITY_OPTIONS: { value: string, text?: string, textId: string, icon: Component }[] = [
{ value: 'public', textId: 'postStatusForm.scope.public', icon: Icons.Globe },
{ value: 'unlisted', textId: 'postStatusForm.scope.unlisted', icon: Icons.OpenLock },
{ value: 'private', textId: 'postStatusForm.scope.private', icon: Icons.Lock },
{ value: 'direct', textId: 'postStatusForm.scope.direct', icon: Icons.Envelope }
]
......@@ -29,6 +29,7 @@
"postStatusForm.poll.addOption": "Add Option",
"postStatusForm.poll.multiple": "Allow users to choose multiple options",
"postStatusForm.poll.endDate": "End date",
"postStatusForm.emptyStatusError": "Cannot post an empty status with no files",
"profileCard.statuses": "Statuses",
"profileCard.followers": "Followers",
"profileCard.following": "Following",
......
......@@ -29,6 +29,7 @@
"postStatusForm.poll.addOption": "Добавить ответ",
"postStatusForm.poll.multiple": "Разрешить пользователям множественный выбор",
"postStatusForm.poll.endDate": "Дата окончания",
"postStatusForm.emptyStatusError": "Нельзя отправить пустой статус без приложений",
"profileCard.statuses": "Статусы",
"profileCard.followers": "Читатели",
"profileCard.following": "Читаемые",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment