Updated Introduction/onboarding flow
• Updated: - Introduction/onboarding flow • Added: - autoJoinGroup to default "Introduce Yourself" group in gab.com if you post the welcome post in introduction last slide
This commit is contained in:
parent
0eb3ae2dfa
commit
de834cd586
@ -56,6 +56,7 @@ class Api::V1::StatusesController < Api::BaseController
|
|||||||
@status = PostStatusService.new.call(current_user.account,
|
@status = PostStatusService.new.call(current_user.account,
|
||||||
text: status_params[:status],
|
text: status_params[:status],
|
||||||
markdown: markdown,
|
markdown: markdown,
|
||||||
|
autoJoinGroup: status_params[:autoJoinGroup],
|
||||||
thread: status_params[:in_reply_to_id].blank? ? nil : Status.find(status_params[:in_reply_to_id]),
|
thread: status_params[:in_reply_to_id].blank? ? nil : Status.find(status_params[:in_reply_to_id]),
|
||||||
media_ids: status_params[:media_ids],
|
media_ids: status_params[:media_ids],
|
||||||
sensitive: status_params[:sensitive],
|
sensitive: status_params[:sensitive],
|
||||||
@ -109,6 +110,7 @@ class Api::V1::StatusesController < Api::BaseController
|
|||||||
params.permit(
|
params.permit(
|
||||||
:status,
|
:status,
|
||||||
:markdown,
|
:markdown,
|
||||||
|
:autoJoinGroup,
|
||||||
:in_reply_to_id,
|
:in_reply_to_id,
|
||||||
:quote_of_id,
|
:quote_of_id,
|
||||||
:sensitive,
|
:sensitive,
|
||||||
|
@ -7,6 +7,7 @@ import { isMobile } from '../utils/is_mobile'
|
|||||||
import { search as emojiSearch } from '../components/emoji/emoji_mart_search_light';
|
import { search as emojiSearch } from '../components/emoji/emoji_mart_search_light';
|
||||||
import { urlRegex } from '../features/ui/util/url_regex'
|
import { urlRegex } from '../features/ui/util/url_regex'
|
||||||
import { tagHistory } from '../settings';
|
import { tagHistory } from '../settings';
|
||||||
|
import { joinGroup } from './groups'
|
||||||
import { useEmoji } from './emojis';
|
import { useEmoji } from './emojis';
|
||||||
import resizeImage from '../utils/resize_image';
|
import resizeImage from '../utils/resize_image';
|
||||||
import { importFetchedAccounts } from './importer';
|
import { importFetchedAccounts } from './importer';
|
||||||
@ -260,10 +261,12 @@ export function handleComposeSubmit(dispatch, getState, response, status) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function submitCompose(groupId, replyToId = null, router, isStandalone) {
|
export function submitCompose(groupId, replyToId = null, router, isStandalone, autoJoinGroup) {
|
||||||
return function (dispatch, getState) {
|
return function (dispatch, getState) {
|
||||||
if (!me) return;
|
if (!me) return;
|
||||||
|
|
||||||
|
if (autoJoinGroup) dispatch(joinGroup(groupId))
|
||||||
|
|
||||||
let status = getState().getIn(['compose', 'text'], '');
|
let status = getState().getIn(['compose', 'text'], '');
|
||||||
let markdown = getState().getIn(['compose', 'markdown'], '');
|
let markdown = getState().getIn(['compose', 'markdown'], '');
|
||||||
const media = getState().getIn(['compose', 'media_attachments']);
|
const media = getState().getIn(['compose', 'media_attachments']);
|
||||||
@ -304,6 +307,7 @@ export function submitCompose(groupId, replyToId = null, router, isStandalone) {
|
|||||||
status,
|
status,
|
||||||
markdown,
|
markdown,
|
||||||
scheduled_at,
|
scheduled_at,
|
||||||
|
autoJoinGroup,
|
||||||
in_reply_to_id: inReplyToId,
|
in_reply_to_id: inReplyToId,
|
||||||
quote_of_id: getState().getIn(['compose', 'quote_of_id'], null),
|
quote_of_id: getState().getIn(['compose', 'quote_of_id'], null),
|
||||||
media_ids: media.map(item => item.get('id')),
|
media_ids: media.map(item => item.get('id')),
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
import moment from 'moment-mini'
|
|
||||||
import { changeSetting, saveSettings } from './settings'
|
import { changeSetting, saveSettings } from './settings'
|
||||||
|
|
||||||
export const MIN_ACCOUNT_CREATED_AT_ONBOARDING = moment('2020-07-14').valueOf()
|
|
||||||
|
|
||||||
export const saveShownOnboarding = () => (dispatch) => {
|
export const saveShownOnboarding = () => (dispatch) => {
|
||||||
dispatch(changeSetting(['shownOnboarding'], true))
|
dispatch(changeSetting(['shownOnboarding'], true))
|
||||||
dispatch(saveSettings())
|
dispatch(saveSettings())
|
||||||
|
@ -109,3 +109,6 @@ export const NOTIFICATION_FILTERS = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
export const GAB_COM_INTRODUCE_YOURSELF_GROUP_ID = '12'
|
export const GAB_COM_INTRODUCE_YOURSELF_GROUP_ID = '12'
|
||||||
|
|
||||||
|
// export const MIN_ACCOUNT_CREATED_AT_ONBOARDING = 1594789200000 // 2020-07-15
|
||||||
|
export const MIN_ACCOUNT_CREATED_AT_ONBOARDING = 1594782839825 // 2020-07-14
|
@ -3,18 +3,21 @@
|
|||||||
import { Provider } from 'react-redux'
|
import { Provider } from 'react-redux'
|
||||||
import configureStore from '../store/configureStore'
|
import configureStore from '../store/configureStore'
|
||||||
import { BrowserRouter, Route } from 'react-router-dom'
|
import { BrowserRouter, Route } from 'react-router-dom'
|
||||||
|
import moment from 'moment-mini'
|
||||||
import { ScrollContext } from 'react-router-scroll-4'
|
import { ScrollContext } from 'react-router-scroll-4'
|
||||||
import { IntlProvider, addLocaleData } from 'react-intl'
|
import { IntlProvider, addLocaleData } from 'react-intl'
|
||||||
import { fetchCustomEmojis } from '../actions/custom_emojis'
|
import { fetchCustomEmojis } from '../actions/custom_emojis'
|
||||||
import { hydrateStore } from '../actions/store'
|
import { hydrateStore } from '../actions/store'
|
||||||
|
import { MIN_ACCOUNT_CREATED_AT_ONBOARDING } from '../constants'
|
||||||
import {
|
import {
|
||||||
connectUserStream,
|
connectUserStream,
|
||||||
connectStatusUpdateStream,
|
connectStatusUpdateStream,
|
||||||
} from '../actions/streaming'
|
} from '../actions/streaming'
|
||||||
import { getLocale } from '../locales'
|
import { getLocale } from '../locales'
|
||||||
import initialState from '../initial_state'
|
import initialState from '../initial_state'
|
||||||
import { me } from '../initial_state'
|
import { me, isFirstSession } from '../initial_state'
|
||||||
import UI from '../features/ui'
|
import UI from '../features/ui'
|
||||||
|
import IntroductionPage from '../pages/introduction_page'
|
||||||
import ErrorBoundary from '../components/error_boundary'
|
import ErrorBoundary from '../components/error_boundary'
|
||||||
import Display from './display'
|
import Display from './display'
|
||||||
|
|
||||||
@ -27,9 +30,45 @@ const hydrateAction = hydrateStore(initialState)
|
|||||||
store.dispatch(hydrateAction)
|
store.dispatch(hydrateAction)
|
||||||
store.dispatch(fetchCustomEmojis())
|
store.dispatch(fetchCustomEmojis())
|
||||||
|
|
||||||
|
const mapStateToProps = (state) => ({
|
||||||
|
accountCreatedAt: !!me ? state.getIn(['accounts', me, 'created_at']) : undefined,
|
||||||
|
shownOnboarding: state.getIn(['settings', 'shownOnboarding']),
|
||||||
|
})
|
||||||
|
|
||||||
|
@connect(mapStateToProps)
|
||||||
class GabSocialMount extends PureComponent {
|
class GabSocialMount extends PureComponent {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
shownOnboarding: PropTypes.bool.isRequired,
|
||||||
|
accountCreatedAt: PropTypes.string,
|
||||||
|
}
|
||||||
|
|
||||||
|
state = {
|
||||||
|
shownOnboarding: this.props.shownOnboarding,
|
||||||
|
shouldShow: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
if (!!me && this.props.accountCreatedAt) {
|
||||||
|
//If first time opening app, and is new user, show onboarding
|
||||||
|
const accountCreatedAtValue = moment(this.props.accountCreatedAt).valueOf()
|
||||||
|
const shouldShow = isFirstSession && !this.state.shownOnboarding && accountCreatedAtValue > MIN_ACCOUNT_CREATED_AT_ONBOARDING
|
||||||
|
|
||||||
|
if (shouldShow) this.setState({ shouldShow })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
|
const { shownOnboarding, shouldShow } = this.state
|
||||||
|
|
||||||
|
if (!shownOnboarding && shouldShow) {
|
||||||
|
return (
|
||||||
|
<BrowserRouter>
|
||||||
|
<Route path='/' component={IntroductionPage} />
|
||||||
|
</BrowserRouter>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<ScrollContext>
|
<ScrollContext>
|
||||||
|
@ -90,6 +90,7 @@ class ComposeForm extends ImmutablePureComponent {
|
|||||||
selectedGifSrc: PropTypes.string,
|
selectedGifSrc: PropTypes.string,
|
||||||
isPro: PropTypes.bool,
|
isPro: PropTypes.bool,
|
||||||
hidePro: PropTypes.bool,
|
hidePro: PropTypes.bool,
|
||||||
|
autoJoinGroup: PropTypes.bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
@ -142,14 +143,14 @@ class ComposeForm extends ImmutablePureComponent {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
// Submit disabled:
|
// Submit disabled:
|
||||||
const { isSubmitting, isChangingUpload, isUploading, anyMedia, groupId } = this.props;
|
const { isSubmitting, isChangingUpload, isUploading, anyMedia, groupId, autoJoinGroup } = this.props
|
||||||
const fulltext = [this.props.spoilerText, countableText(this.props.text)].join('');
|
const fulltext = [this.props.spoilerText, countableText(this.props.text)].join('');
|
||||||
|
|
||||||
if (isSubmitting || isUploading || isChangingUpload || length(fulltext) > MAX_POST_CHARACTER_COUNT || (fulltext.length !== 0 && fulltext.trim().length === 0 && !anyMedia)) {
|
if (isSubmitting || isUploading || isChangingUpload || length(fulltext) > MAX_POST_CHARACTER_COUNT || (fulltext.length !== 0 && fulltext.trim().length === 0 && !anyMedia)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.onSubmit(groupId, this.props.replyToId, this.context.router);
|
this.props.onSubmit(groupId, this.props.replyToId, this.context.router, autoJoinGroup)
|
||||||
}
|
}
|
||||||
|
|
||||||
onSuggestionsClearRequested = () => {
|
onSuggestionsClearRequested = () => {
|
||||||
|
@ -102,8 +102,8 @@ const mapDispatchToProps = (dispatch, { isStandalone }) => ({
|
|||||||
dispatch(changeCompose(text, markdown, newReplyToId, isStandalone, position))
|
dispatch(changeCompose(text, markdown, newReplyToId, isStandalone, position))
|
||||||
},
|
},
|
||||||
|
|
||||||
onSubmit(groupId, replyToId, router) {
|
onSubmit(groupId, replyToId, router, autoJoinGroup) {
|
||||||
dispatch(submitCompose(groupId, replyToId, router, isStandalone))
|
dispatch(submitCompose(groupId, replyToId, router, isStandalone, autoJoinGroup))
|
||||||
},
|
},
|
||||||
|
|
||||||
onClearSuggestions() {
|
onClearSuggestions() {
|
||||||
|
@ -1,17 +1,22 @@
|
|||||||
|
import { Fragment } from 'react'
|
||||||
import ReactSwipeableViews from 'react-swipeable-views'
|
import ReactSwipeableViews from 'react-swipeable-views'
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||||
import { CX } from '../constants'
|
import {
|
||||||
|
CX,
|
||||||
|
BREAKPOINT_EXTRA_SMALL,
|
||||||
|
GAB_COM_INTRODUCE_YOURSELF_GROUP_ID,
|
||||||
|
} from '../constants'
|
||||||
import { me } from '../initial_state'
|
import { me } from '../initial_state'
|
||||||
import { saveShownOnboarding } from '../actions/onboarding'
|
import { saveShownOnboarding } from '../actions/onboarding'
|
||||||
import { fetchGroups } from '../actions/groups'
|
import { fetchGroups } from '../actions/groups'
|
||||||
|
import { saveUserProfileInformation } from '../actions/user'
|
||||||
import {
|
import {
|
||||||
changeCompose,
|
changeCompose,
|
||||||
clearCompose,
|
clearCompose,
|
||||||
} from '../actions/compose'
|
} from '../actions/compose'
|
||||||
import { makeGetAccount } from '../selectors'
|
import { makeGetAccount } from '../selectors'
|
||||||
import Button from '../components/button'
|
import Button from '../components/button'
|
||||||
import DisplayName from '../components/display_name'
|
|
||||||
import Divider from '../components/divider'
|
import Divider from '../components/divider'
|
||||||
import FileInput from '../components/file_input'
|
import FileInput from '../components/file_input'
|
||||||
import GroupListItem from '../components/group_list_item'
|
import GroupListItem from '../components/group_list_item'
|
||||||
@ -21,12 +26,15 @@ import Image from '../components/image'
|
|||||||
import Input from '../components/input'
|
import Input from '../components/input'
|
||||||
import Text from '../components/text'
|
import Text from '../components/text'
|
||||||
import ComposeFormContainer from './compose/containers/compose_form_container'
|
import ComposeFormContainer from './compose/containers/compose_form_container'
|
||||||
|
import Responsive from './ui/util/responsive_component'
|
||||||
|
|
||||||
class SlideWelcome extends PureComponent {
|
class SlideWelcome extends PureComponent {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className={[_s.default, _s.width100PC, _s.height100PC].join(' ')}>
|
<div className={[_s.default, _s.width100PC, _s.height100PC].join(' ')}>
|
||||||
|
<Image src='/headers/onboarding.png' alt='Welcome to Gab' />
|
||||||
|
|
||||||
<div className={[_s.default, _s.px15, _s.py15].join(' ')}>
|
<div className={[_s.default, _s.px15, _s.py15].join(' ')}>
|
||||||
|
|
||||||
<Text size='large'>Gab is the home of free speech online and a place where users shape their own experience. </Text>
|
<Text size='large'>Gab is the home of free speech online and a place where users shape their own experience. </Text>
|
||||||
@ -41,10 +49,6 @@ class SlideWelcome extends PureComponent {
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Image
|
|
||||||
className={[_s.mtAuto].join(' ')}
|
|
||||||
src='https://gab.com/system/media_attachments/files/008/707/779/original/42de809171745057.png?1568251173'
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -57,35 +61,72 @@ class SlidePhotos extends ImmutablePureComponent {
|
|||||||
account: ImmutablePropTypes.map.isRequired,
|
account: ImmutablePropTypes.map.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state = {
|
||||||
|
displayNameValue: this.props.account.get('display_name'),
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCoverPhotoChange = (e) => {
|
||||||
|
try {
|
||||||
|
this.props.onSave({ header: e.target.files[0] })
|
||||||
|
} catch (error) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleProfilePhotoChange = (e) => {
|
||||||
|
try {
|
||||||
|
this.props.onSave({ avatar: e.target.files[0] })
|
||||||
|
} catch (error) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleDisplayNameChange = (value) => {
|
||||||
|
this.setState({ displayNameValue: value })
|
||||||
|
}
|
||||||
|
|
||||||
|
handleDisplayNameBlur = () => {
|
||||||
|
this.props.onSave({
|
||||||
|
displayName: this.state.displayNameValue,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { account } = this.props
|
const { displayNameValue } = this.state
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={[_s.default, _s.width100PC].join(' ')}>
|
<div className={[_s.default, _s.width100PC].join(' ')}>
|
||||||
<div className={[_s.default, _s.px15, _s.py15, _s.alignItemsCenter].join(' ')}>
|
<div className={[_s.default, _s.px15, _s.py15, _s.alignItemsCenter].join(' ')}>
|
||||||
|
|
||||||
<div className={[_s.default, _s.py10, _s.width330PX].join(' ')}>
|
<div className={[_s.default, _s.py10, _s.maxWidth640PX].join(' ')}>
|
||||||
<Text size='large' align='center'>Set your cover photo, profile photo and enter your display name so people can find you.</Text>
|
<Text size='large' align='center'>Set your cover photo, profile photo and enter your display name so people can find you.</Text>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={[_s.default, _s.mt15, _s.width100PC, _s.alignItemsCenter].join(' ')}>
|
<div className={[_s.default, _s.mt15, _s.width100PC, _s.alignItemsCenter].join(' ')}>
|
||||||
<div className={[_s.default, _s.width330PX, _s.border1PX, _s.borderColorSecondary, _s.overflowHidden, _s.radiusSmall].join(' ')}>
|
<div className={[_s.default, _s.border1PX, _s.borderColorSecondary, _s.overflowHidden, _s.radiusSmall, _s.bgPrimary].join(' ')}>
|
||||||
<Image
|
<FileInput
|
||||||
width={330}
|
width='300px'
|
||||||
height={194}
|
height='140px'
|
||||||
src='http://localhost:3000/system/accounts/headers/000/000/001/original/0a49fe388d16f372.jpg?1562898139'
|
id='cover-photo'
|
||||||
|
onChange={this.handleCoverPhotoChange}
|
||||||
/>
|
/>
|
||||||
<div className={[_s.default, _s.mtNeg75PX, _s.alignItemsCenter, _s.justifyContentCenter].join(' ')}>
|
<div className={[_s.default, _s.mtNeg32PX, _s.alignItemsCenter, _s.justifyContentCenter].join(' ')}>
|
||||||
<Image
|
<FileInput
|
||||||
width={142}
|
width='124px'
|
||||||
height={142}
|
height='124px'
|
||||||
className={[_s.circle, _s.border6PX, _s.borderColorWhite].join(' ')}
|
id='profile-photo'
|
||||||
src='http://localhost:3000/system/accounts/avatars/000/000/001/original/260e8c96c97834da.jpeg?1562898139'
|
className={[_s.circle, _s.border6PX, _s.borderColorWhite, _s.bgPrimary].join(' ')}
|
||||||
|
onChange={this.handleProfilePhotoChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={[_s.default, _s.py5, _s.px15, _s.mt5, _s.mb15].join(' ')}>
|
<div className={[_s.default, _s.py5, _s.px15, _s.mt5, _s.mb15].join(' ')}>
|
||||||
<Input
|
<Input
|
||||||
placeholder='John Doe'
|
id='display-name'
|
||||||
|
title='Display name'
|
||||||
|
placeholder='Add your name...'
|
||||||
|
value={displayNameValue}
|
||||||
|
onChange={this.handleDisplayNameChange}
|
||||||
|
onBlur={this.handleDisplayNameBlur}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -119,6 +160,7 @@ class SlideGroups extends ImmutablePureComponent {
|
|||||||
groupIds.map((groupId, i) => (
|
groupIds.map((groupId, i) => (
|
||||||
<GroupListItem
|
<GroupListItem
|
||||||
isAddable
|
isAddable
|
||||||
|
isStatic
|
||||||
key={`group-collection-item-${i}`}
|
key={`group-collection-item-${i}`}
|
||||||
id={groupId}
|
id={groupId}
|
||||||
isLast={groupIds.count() - 1 === i}
|
isLast={groupIds.count() - 1 === i}
|
||||||
@ -136,21 +178,49 @@ class SlideGroups extends ImmutablePureComponent {
|
|||||||
class SlideFirstPost extends PureComponent {
|
class SlideFirstPost extends PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
submitted: PropTypes.bool.isRequired,
|
||||||
onNext: PropTypes.func.isRequired,
|
onNext: PropTypes.func.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const { submitted } = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={[_s.default, _s.width100PC].join(' ')}>
|
<div className={[_s.default, _s.width100PC].join(' ')}>
|
||||||
<div className={[_s.default, _s.py15, _s.px15].join(' ')}>
|
<div className={[_s.default, _s.py15, _s.px15].join(' ')}>
|
||||||
|
{
|
||||||
|
!submitted &&
|
||||||
|
<Fragment>
|
||||||
<Text size='large' className={_s.pb10}>Now let's make your very first Gab post! Please introduce yourself to the Gab community. How did you hear about Gab? What are you interested in?</Text>
|
<Text size='large' className={_s.pb10}>Now let's make your very first Gab post! Please introduce yourself to the Gab community. How did you hear about Gab? What are you interested in?</Text>
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<div className={[_s.default, _s.mt15, _s.boxShadowBlock, _s.radiusSmall].join(' ')}>
|
<div className={[_s.default, _s.mt15, _s.boxShadowBlock, _s.radiusSmall].join(' ')}>
|
||||||
<ComposeFormContainer hidePro autoFocus />
|
<ComposeFormContainer
|
||||||
|
groupId={GAB_COM_INTRODUCE_YOURSELF_GROUP_ID}
|
||||||
|
hidePro
|
||||||
|
autoFocus
|
||||||
|
autoJoinGroup
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</Fragment>
|
||||||
|
}
|
||||||
|
{
|
||||||
|
submitted &&
|
||||||
|
<Fragment>
|
||||||
|
<Text size='large' align='center'>Your gab was posted!</Text>
|
||||||
|
<br />
|
||||||
|
<Text size='large' align='center'>Click the checkbox in the top right to go to your home page.</Text>
|
||||||
|
<br />
|
||||||
|
<Button
|
||||||
|
href='/home'
|
||||||
|
onClick={this.props.onNext}
|
||||||
|
>
|
||||||
|
Finish
|
||||||
|
</Button>
|
||||||
|
</Fragment>
|
||||||
|
}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -163,13 +233,20 @@ const mapStateToProps = (state) => ({
|
|||||||
account: makeGetAccount()(state, me),
|
account: makeGetAccount()(state, me),
|
||||||
groupIds: state.getIn(['group_lists', 'featured', 'items']),
|
groupIds: state.getIn(['group_lists', 'featured', 'items']),
|
||||||
shownOnboarding: state.getIn(['settings', 'shownOnboarding'], false),
|
shownOnboarding: state.getIn(['settings', 'shownOnboarding'], false),
|
||||||
|
isSubmitting: state.getIn(['compose', 'is_submitting']),
|
||||||
})
|
})
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
onClearCompose: () => dispatch(clearCompose()),
|
onClearCompose: () => dispatch(clearCompose()),
|
||||||
onSaveShownOnboarding: () => dispatch(saveShownOnboarding()),
|
onSaveShownOnboarding: () => dispatch(saveShownOnboarding()),
|
||||||
onFetchFeaturedGroups: () => dispatch(fetchGroups('featured')),
|
onFetchFeaturedGroups: () => dispatch(fetchGroups('featured')),
|
||||||
setPlaceholderCompose: () => dispatch(changeCompose('Hello everyone, I just joined Gab! I’m looking forward to speaking freely and meeting new friends.'))
|
setPlaceholderCompose() {
|
||||||
|
const defaultMessage = 'Hello everyone, I just joined Gab! I’m looking forward to speaking freely and meeting new friends.'
|
||||||
|
dispatch(changeCompose(defaultMessage))
|
||||||
|
},
|
||||||
|
onSaveUserProfileInformation(data) {
|
||||||
|
dispatch(saveUserProfileInformation(data))
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
export default
|
export default
|
||||||
@ -178,26 +255,32 @@ class Introduction extends ImmutablePureComponent {
|
|||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
account: ImmutablePropTypes.map.isRequired,
|
account: ImmutablePropTypes.map.isRequired,
|
||||||
dispatch: PropTypes.func.isRequired,
|
|
||||||
groupIds: ImmutablePropTypes.list,
|
groupIds: ImmutablePropTypes.list,
|
||||||
|
isSubmitting: PropTypes.bool.isRequired,
|
||||||
shownOnboarding: PropTypes.bool.isRequired,
|
shownOnboarding: PropTypes.bool.isRequired,
|
||||||
setPlaceholderCompose: PropTypes.func.isRequired,
|
setPlaceholderCompose: PropTypes.func.isRequired,
|
||||||
onClearCompose: PropTypes.func.isRequired,
|
onClearCompose: PropTypes.func.isRequired,
|
||||||
onSaveShownOnboarding: PropTypes.func.isRequired,
|
onSaveShownOnboarding: PropTypes.func.isRequired,
|
||||||
onFetchFeaturedGroups: PropTypes.func.isRequired,
|
onFetchFeaturedGroups: PropTypes.func.isRequired,
|
||||||
|
onSaveUserProfileInformation: PropTypes.func.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
currentIndex: 0,
|
currentIndex: 0,
|
||||||
|
submittedFirstPost: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
if (!this.props.shownOnboarding) {
|
|
||||||
window.addEventListener('keyup', this.handleKeyUp)
|
window.addEventListener('keyup', this.handleKeyUp)
|
||||||
this.props.onFetchFeaturedGroups()
|
this.props.onFetchFeaturedGroups()
|
||||||
this.props.setPlaceholderCompose()
|
this.props.setPlaceholderCompose()
|
||||||
this.props.onSaveShownOnboarding()
|
this.props.onSaveShownOnboarding()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps) {
|
||||||
|
if (!this.state.submittedFirstPost && !prevProps.isSubmitting && this.props.isSubmitting) {
|
||||||
|
this.setState({ submittedFirstPost: true })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
@ -223,7 +306,7 @@ class Introduction extends ImmutablePureComponent {
|
|||||||
currentIndex: newIndex,
|
currentIndex: newIndex,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (newIndex === 3) {
|
if (newIndex === 4) {
|
||||||
this.props.onClearCompose()
|
this.props.onClearCompose()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -243,15 +326,25 @@ class Introduction extends ImmutablePureComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleOnSaveUserProfileInformation = (data) => {
|
||||||
|
this.props.onSaveUserProfileInformation(data)
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { account, groupIds } = this.props
|
const { account, groupIds } = this.props
|
||||||
const { currentIndex } = this.state
|
const { currentIndex, submittedFirstPost } = this.state
|
||||||
|
|
||||||
const pages = [
|
const pages = [
|
||||||
<SlideWelcome />,
|
<SlideWelcome />,
|
||||||
<SlidePhotos account={account} />,
|
<SlidePhotos
|
||||||
|
account={account}
|
||||||
|
onSave={this.handleOnSaveUserProfileInformation}
|
||||||
|
/>,
|
||||||
<SlideGroups groupIds={groupIds} />,
|
<SlideGroups groupIds={groupIds} />,
|
||||||
<SlideFirstPost />,
|
<SlideFirstPost
|
||||||
|
submitted={submittedFirstPost}
|
||||||
|
onNext={this.handleNext}
|
||||||
|
/>,
|
||||||
]
|
]
|
||||||
|
|
||||||
const titles = [
|
const titles = [
|
||||||
@ -284,20 +377,34 @@ class Introduction extends ImmutablePureComponent {
|
|||||||
return (
|
return (
|
||||||
<div className={[_s.default, _s.width100PC, _s.heightMax80VH].join(' ')}>
|
<div className={[_s.default, _s.width100PC, _s.heightMax80VH].join(' ')}>
|
||||||
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter, _s.justifyContentCenter, _s.borderBottom1PX, _s.borderColorSecondary, _s.height53PX, _s.px15].join(' ')}>
|
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter, _s.justifyContentCenter, _s.borderBottom1PX, _s.borderColorSecondary, _s.height53PX, _s.px15].join(' ')}>
|
||||||
|
<Responsive min={BREAKPOINT_EXTRA_SMALL}>
|
||||||
<Heading>
|
<Heading>
|
||||||
{title}
|
{title}
|
||||||
</Heading>
|
</Heading>
|
||||||
|
</Responsive>
|
||||||
|
<Responsive max={BREAKPOINT_EXTRA_SMALL}>
|
||||||
|
<Heading size='h2'>
|
||||||
|
{title}
|
||||||
|
</Heading>
|
||||||
|
</Responsive>
|
||||||
<div className={[_s.mlAuto].join(' ')}>
|
<div className={[_s.mlAuto].join(' ')}>
|
||||||
<Button
|
<Button
|
||||||
to={currentIndex === 3 ? '/home' : undefined}
|
href={currentIndex === 3 ? '/home' : undefined}
|
||||||
onClick={this.handleNext}
|
onClick={this.handleNext}
|
||||||
className={currentIndex !== 3 ? _s.px10 : undefined}
|
className={_s.px10}
|
||||||
icon={currentIndex !== 3 ? 'arrow-right' : undefined}
|
icon={currentIndex !== 3 ? 'arrow-right' : undefined}
|
||||||
iconSize={currentIndex !== 3 ? '18px' : undefined}
|
iconSize={currentIndex !== 3 ? '18px' : undefined}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
currentIndex === 3 &&
|
currentIndex === 3 &&
|
||||||
<Text color='white' className={[_s.pr5]}>Complete</Text>
|
<Fragment>
|
||||||
|
<Responsive min={BREAKPOINT_EXTRA_SMALL}>
|
||||||
|
<Text color='white' className={_s.px5}>Complete</Text>
|
||||||
|
</Responsive>
|
||||||
|
<Responsive max={BREAKPOINT_EXTRA_SMALL}>
|
||||||
|
<Icon id='check' size='14px' className={_s.fillWhite} />
|
||||||
|
</Responsive>
|
||||||
|
</Fragment>
|
||||||
}
|
}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@ -341,7 +448,7 @@ class Introduction extends ImmutablePureComponent {
|
|||||||
{pagination}
|
{pagination}
|
||||||
</ul>
|
</ul>
|
||||||
<Button
|
<Button
|
||||||
to={currentIndex === 3 ? '/home' : undefined}
|
href={currentIndex === 3 ? '/home' : undefined}
|
||||||
className={[_s.default, _s.width50PX, _s.mlAuto, _s.opacity05].join(' ')}
|
className={[_s.default, _s.width50PX, _s.mlAuto, _s.opacity05].join(' ')}
|
||||||
onClick={this.handleNext}
|
onClick={this.handleNext}
|
||||||
icon={currentIndex === 3 ? 'check' : 'arrow-right'}
|
icon={currentIndex === 3 ? 'check' : 'arrow-right'}
|
||||||
|
@ -1,20 +1,34 @@
|
|||||||
import Block from '../components/block'
|
import Block from '../components/block'
|
||||||
import NavigationBar from '../components/navigation_bar'
|
import Icon from '../components/icon'
|
||||||
|
import BundleColumnError from '../components/bundle_column_error'
|
||||||
|
import Bundle from '../features/ui/util/bundle'
|
||||||
|
import { Introduction } from '../features/ui/util/async_components'
|
||||||
|
|
||||||
export default class IntroductionLayout extends PureComponent {
|
export default class IntroductionLayout extends PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
renderError = (props) => {
|
||||||
children: PropTypes.node,
|
return <BundleColumnError {...props} />
|
||||||
title: PropTypes.string,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { children, title } = this.props
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={[_s.default, _s.width100PC, _s.heightMin100VH, _s.bgTertiary].join(' ')}>
|
<div className={[_s.default, _s.width100PC, _s.heightMin100VH, _s.bgTertiary].join(' ')}>
|
||||||
|
|
||||||
<NavigationBar title={title} noSearch noActions />
|
<div className={[_s.default, _s.z4, _s.heightMin53PX, _s.width100PC].join(' ')}>
|
||||||
|
<div className={[_s.default, _s.heightMin53PX, _s.bgNavigation, _s.alignItemsCenter, _s.z3, _s.top0, _s.right0, _s.left0, _s.posFixed].join(' ')} >
|
||||||
|
<div className={[_s.default, _s.saveAreaInsetPT, _s.saveAreaInsetPL, _s.saveAreaInsetPR, _s.flexRow, _s.width1255PX].join(' ')}>
|
||||||
|
<div className={[_s.default, _s.flexRow].join(' ')}>
|
||||||
|
|
||||||
|
<h1 className={[_s.default, _s.mr15].join(' ')}>
|
||||||
|
<div className={[_s.default, _s.justifyContentCenter, _s.noSelect, _s.noUnderline, _s.height53PX, _s.px10, _s.mr15].join(' ')}>
|
||||||
|
<Icon id='logo' className={_s.fillNavigationBrand} />
|
||||||
|
</div>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className={[_s.default, _s.flexRow, _s.width100PC].join(' ')}>
|
<div className={[_s.default, _s.flexRow, _s.width100PC].join(' ')}>
|
||||||
<div className={[_s.default, _s.width100PC].join(' ')}>
|
<div className={[_s.default, _s.width100PC].join(' ')}>
|
||||||
@ -23,7 +37,9 @@ export default class IntroductionLayout extends PureComponent {
|
|||||||
|
|
||||||
<div className={[_s.default, _s.width645PX, _s.maxWidth100PC42PX].join(' ')}>
|
<div className={[_s.default, _s.width645PX, _s.maxWidth100PC42PX].join(' ')}>
|
||||||
<Block>
|
<Block>
|
||||||
{children}
|
<Bundle fetchComponent={Introduction} error={this.renderError}>
|
||||||
|
{Component => (<Component />)}
|
||||||
|
</Bundle>
|
||||||
</Block>
|
</Block>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import uuid from '../utils/uuid'
|
|||||||
|
|
||||||
const initialState = ImmutableMap({
|
const initialState = ImmutableMap({
|
||||||
saved: true,
|
saved: true,
|
||||||
onboarded: false,
|
shownOnboarding: false,
|
||||||
skinTone: 1,
|
skinTone: 1,
|
||||||
commentSorting: COMMENT_SORTING_TYPE_OLDEST,
|
commentSorting: COMMENT_SORTING_TYPE_OLDEST,
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ class PostStatusService < BaseService
|
|||||||
@text = @options[:text] || ''
|
@text = @options[:text] || ''
|
||||||
@markdown = @options[:markdown] if @account.is_pro
|
@markdown = @options[:markdown] if @account.is_pro
|
||||||
@in_reply_to = @options[:thread]
|
@in_reply_to = @options[:thread]
|
||||||
|
@autoJoinGroup = @options[:autoJoinGroup] || false
|
||||||
|
|
||||||
return idempotency_duplicate if idempotency_given? && idempotency_duplicate?
|
return idempotency_duplicate if idempotency_given? && idempotency_duplicate?
|
||||||
|
|
||||||
@ -104,6 +105,7 @@ class PostStatusService < BaseService
|
|||||||
group_id = @options[:group_id]
|
group_id = @options[:group_id]
|
||||||
|
|
||||||
return if group_id.blank?
|
return if group_id.blank?
|
||||||
|
return if @autoJoinGroup
|
||||||
|
|
||||||
raise GabSocial::ValidationError, I18n.t('statuses.not_a_member_of_group') if not GroupAccount.where(account: @account, group_id: group_id).exists?
|
raise GabSocial::ValidationError, I18n.t('statuses.not_a_member_of_group') if not GroupAccount.where(account: @account, group_id: group_id).exists?
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user