2020-07-20 16:18:54 -07:00
import React from 'react' ;
import { connect } from 'react-redux' ;
import { defineMessages , injectIntl , FormattedMessage } from 'react-intl' ;
import ImmutablePureComponent from 'react-immutable-pure-component' ;
import PropTypes from 'prop-types' ;
import ImmutablePropTypes from 'react-immutable-proptypes' ;
import Column from '../ui/components/column' ;
import {
SimpleForm ,
FieldsGroup ,
2020-07-21 06:30:51 -07:00
TextInput ,
2021-06-10 11:48:43 -07:00
SimpleInput ,
2020-08-23 21:19:56 -07:00
SimpleTextarea ,
2020-08-02 12:07:13 -07:00
FileChooserLogo ,
2020-09-23 20:46:45 -07:00
FormPropTypes ,
2021-03-15 20:23:33 -07:00
Checkbox ,
2020-07-20 16:18:54 -07:00
} from 'soapbox/features/forms' ;
2020-08-23 21:19:56 -07:00
import { Map as ImmutableMap , List as ImmutableList , fromJS } from 'immutable' ;
2020-12-29 21:25:07 -08:00
import { updateConfig } from 'soapbox/actions/admin' ;
2020-08-23 13:04:32 -07:00
import Icon from 'soapbox/components/icon' ;
2021-08-23 12:14:47 -07:00
import { makeDefaultConfig } from 'soapbox/actions/soapbox' ;
import { getFeatures } from 'soapbox/utils/features' ;
2020-08-23 19:35:33 -07:00
import { uploadMedia } from 'soapbox/actions/media' ;
2020-09-23 20:46:45 -07:00
import { SketchPicker } from 'react-color' ;
import Overlay from 'react-overlays/lib/Overlay' ;
import { isMobile } from 'soapbox/is_mobile' ;
2020-10-15 07:10:45 -07:00
import { supportsPassiveEvents } from 'detect-passive-events' ;
2020-09-30 12:09:00 -07:00
import Accordion from '../ui/components/accordion' ;
2020-10-01 16:57:11 -07:00
import SitePreview from './components/site_preview' ;
2020-10-01 17:33:03 -07:00
import ThemeToggle from 'soapbox/features/ui/components/theme_toggle' ;
2020-10-16 09:55:35 -07:00
import IconPickerDropdown from './components/icon_picker_dropdown' ;
2021-06-10 11:48:43 -07:00
import snackbar from 'soapbox/actions/snackbar' ;
2020-07-20 16:18:54 -07:00
const messages = defineMessages ( {
2020-08-23 13:21:19 -07:00
heading : { id : 'column.soapbox_config' , defaultMessage : 'Soapbox config' } ,
2021-06-10 11:48:43 -07:00
saved : { id : 'soapbox_config.saved' , defaultMessage : 'Soapbox config saved!' } ,
2020-08-23 13:21:19 -07:00
copyrightFooterLabel : { id : 'soapbox_config.copyright_footer.meta_fields.label_placeholder' , defaultMessage : 'Copyright footer' } ,
promoItemIcon : { id : 'soapbox_config.promo_panel.meta_fields.icon_placeholder' , defaultMessage : 'Icon' } ,
promoItemLabel : { id : 'soapbox_config.promo_panel.meta_fields.label_placeholder' , defaultMessage : 'Label' } ,
promoItemURL : { id : 'soapbox_config.promo_panel.meta_fields.url_placeholder' , defaultMessage : 'URL' } ,
homeFooterItemLabel : { id : 'soapbox_config.home_footer.meta_fields.label_placeholder' , defaultMessage : 'Label' } ,
homeFooterItemURL : { id : 'soapbox_config.home_footer.meta_fields.url_placeholder' , defaultMessage : 'URL' } ,
2021-06-10 11:48:43 -07:00
cryptoAdressItemTicker : { id : 'soapbox_config.crypto_address.meta_fields.ticker_placeholder' , defaultMessage : 'Ticker' } ,
cryptoAdressItemAddress : { id : 'soapbox_config.crypto_address.meta_fields.address_placeholder' , defaultMessage : 'Address' } ,
cryptoAdressItemNote : { id : 'soapbox_config.crypto_address.meta_fields.note_placeholder' , defaultMessage : 'Note (optional)' } ,
cryptoDonatePanelLimitLabel : { id : 'soapbox_config.crypto_donate_panel_limit.meta_fields.limit_placeholder' , defaultMessage : 'Number of items to display in the crypto homepage widget' } ,
2020-08-23 13:21:19 -07:00
customCssLabel : { id : 'soapbox_config.custom_css.meta_fields.url_placeholder' , defaultMessage : 'URL' } ,
2020-09-30 12:09:00 -07:00
rawJSONLabel : { id : 'soapbox_config.raw_json_label' , defaultMessage : 'Advanced: Edit raw JSON data' } ,
rawJSONHint : { id : 'soapbox_config.raw_json_hint' , defaultMessage : 'Edit the settings data directly. Changes made directly to the JSON file will override the form fields above. Click "Save" to apply your changes.' } ,
2021-03-15 20:23:33 -07:00
verifiedCanEditNameLabel : { id : 'soapbox_config.verified_can_edit_name_label' , defaultMessage : 'Allow verified users to edit their own display name.' } ,
2021-04-10 15:39:33 -07:00
displayFqnLabel : { id : 'soapbox_config.display_fqn_label' , defaultMessage : 'Display domain (eg @user@domain) for local accounts.' } ,
2021-06-30 19:39:27 -07:00
greentextLabel : { id : 'soapbox_config.greentext_label' , defaultMessage : 'Enable greentext support' } ,
2021-07-19 08:28:28 -07:00
promoPanelIconsLink : { id : 'soapbox_config.hints.promo_panel_icons.link' , defaultMessage : 'Soapbox Icons List' } ,
2021-09-10 09:44:18 -07:00
authenticatedProfileLabel : { id : 'soapbox_config.authenticated_profile_label' , defaultMessage : 'Profiles require authentication' } ,
authenticatedProfileHint : { id : 'soapbox_config.authenticated_profile_hint' , defaultMessage : 'Users must be logged-in to view replies and media on user profiles.' } ,
2020-07-20 16:18:54 -07:00
} ) ;
2020-10-15 07:10:45 -07:00
const listenerOptions = supportsPassiveEvents ? { passive : true } : false ;
2020-09-23 20:46:45 -07:00
2020-08-23 18:29:29 -07:00
const templates = {
promoPanelItem : ImmutableMap ( { icon : '' , text : '' , url : '' } ) ,
footerItem : ImmutableMap ( { title : '' , url : '' } ) ,
2021-06-10 11:48:43 -07:00
cryptoAddress : ImmutableMap ( { ticker : '' , address : '' , note : '' } ) ,
2020-08-23 18:29:29 -07:00
} ;
2021-08-23 12:14:47 -07:00
const mapStateToProps = state => {
const instance = state . get ( 'instance' ) ;
return {
soapbox : state . get ( 'soapbox' ) ,
features : getFeatures ( instance ) ,
} ;
} ;
2020-07-21 06:30:51 -07:00
2020-07-20 16:18:54 -07:00
export default @ connect ( mapStateToProps )
@ injectIntl
2020-08-23 13:16:04 -07:00
class SoapboxConfig extends ImmutablePureComponent {
2020-07-20 16:18:54 -07:00
static propTypes = {
2020-08-23 18:29:29 -07:00
soapbox : ImmutablePropTypes . map . isRequired ,
2021-08-23 12:14:47 -07:00
features : PropTypes . object . isRequired ,
2020-07-20 16:18:54 -07:00
dispatch : PropTypes . func . isRequired ,
intl : PropTypes . object . isRequired ,
} ;
2020-07-21 06:30:51 -07:00
state = {
isLoading : false ,
2020-08-23 18:29:29 -07:00
soapbox : this . props . soapbox ,
2020-10-01 13:54:29 -07:00
jsonEditorExpanded : false ,
2020-08-23 21:45:17 -07:00
rawJSON : JSON . stringify ( this . props . soapbox , null , 2 ) ,
jsonValid : true ,
2020-07-21 06:30:51 -07:00
}
2020-08-23 18:29:29 -07:00
setConfig = ( path , value ) => {
const { soapbox } = this . state ;
const config = soapbox . setIn ( path , value ) ;
2020-08-23 21:45:17 -07:00
this . setState ( { soapbox : config , jsonValid : true } ) ;
2020-08-23 18:29:29 -07:00
} ;
2020-07-23 07:14:28 -07:00
2020-08-23 21:19:56 -07:00
putConfig = config => {
2020-08-23 21:45:17 -07:00
this . setState ( { soapbox : config , jsonValid : true } ) ;
2020-08-23 21:19:56 -07:00
} ;
2020-07-21 06:30:51 -07:00
getParams = ( ) => {
2020-08-23 18:29:29 -07:00
const { soapbox } = this . state ;
2020-12-29 21:25:07 -08:00
return [ {
group : ':pleroma' ,
key : ':frontend_configurations' ,
value : [ {
tuple : [ ':soapbox_fe' , soapbox . toJS ( ) ] ,
2020-07-29 17:09:44 -07:00
} ] ,
2020-12-29 21:25:07 -08:00
} ] ;
2020-07-21 06:30:51 -07:00
}
handleSubmit = ( event ) => {
2021-06-10 11:48:43 -07:00
const { dispatch , intl } = this . props ;
2020-12-29 21:25:07 -08:00
dispatch ( updateConfig ( this . getParams ( ) ) ) . then ( ( ) => {
2020-07-21 06:30:51 -07:00
this . setState ( { isLoading : false } ) ;
2021-06-10 11:48:43 -07:00
dispatch ( snackbar . success ( intl . formatMessage ( messages . saved ) ) ) ;
2020-07-21 06:30:51 -07:00
} ) . catch ( ( error ) => {
this . setState ( { isLoading : false } ) ;
} ) ;
this . setState ( { isLoading : true } ) ;
event . preventDefault ( ) ;
}
2020-08-23 18:29:29 -07:00
handleChange = ( path , getValue ) => {
return e => {
this . setConfig ( path , getValue ( e ) ) ;
2020-07-23 07:14:28 -07:00
} ;
2020-08-23 18:29:29 -07:00
} ;
2020-07-23 07:14:28 -07:00
2020-08-23 19:35:33 -07:00
handleFileChange = path => {
return e => {
const data = new FormData ( ) ;
data . append ( 'file' , e . target . files [ 0 ] ) ;
this . props . dispatch ( uploadMedia ( data ) ) . then ( ( { data } ) => {
this . handleChange ( path , e => data . url ) ( e ) ;
} ) . catch ( ( ) => { } ) ;
} ;
} ;
2020-08-23 18:29:29 -07:00
handleAddItem = ( path , template ) => {
return e => {
this . setConfig (
path ,
this . getSoapboxConfig ( ) . getIn ( path , ImmutableList ( ) ) . push ( template ) ,
) ;
2020-07-24 17:30:33 -07:00
} ;
2020-08-23 18:29:29 -07:00
} ;
2020-07-21 06:30:51 -07:00
2020-08-23 20:41:22 -07:00
handleDeleteItem = path => {
return e => {
const soapbox = this . state . soapbox . deleteIn ( path ) ;
this . setState ( { soapbox } ) ;
} ;
} ;
2020-10-18 11:41:38 -07:00
handleItemChange = ( path , key , field , template , getValue = e => e . target . value ) => {
2020-08-23 18:29:29 -07:00
return this . handleChange (
path , ( e ) =>
template
. merge ( field )
2020-10-14 07:46:45 -07:00
. set ( key , getValue ( e ) ) ,
2020-08-23 18:29:29 -07:00
) ;
} ;
2020-07-20 16:18:54 -07:00
2020-09-09 14:31:02 -07:00
handlePromoItemChange = ( index , key , field , getValue ) => {
2020-08-23 18:29:29 -07:00
return this . handleItemChange (
2020-10-14 07:46:45 -07:00
[ 'promoPanel' , 'items' , index ] , key , field , templates . promoPanelItem , getValue ,
2020-08-23 18:29:29 -07:00
) ;
} ;
2020-08-11 04:30:54 -07:00
2020-10-18 11:41:38 -07:00
handleHomeFooterItemChange = ( index , key , field , getValue ) => {
2020-08-23 18:29:29 -07:00
return this . handleItemChange (
2020-10-18 11:41:38 -07:00
[ 'navlinks' , 'homeFooter' , index ] , key , field , templates . footerItem , getValue ,
2020-08-23 18:29:29 -07:00
) ;
} ;
2020-07-25 21:31:39 -07:00
2021-06-10 11:48:43 -07:00
handleCryptoAdressItemChange = ( index , key , field , getValue ) => {
return this . handleItemChange (
[ 'cryptoAddresses' , index ] , key , field , templates . cryptoAddress , getValue ,
) ;
} ;
2020-08-23 21:19:56 -07:00
handleEditJSON = e => {
2020-08-23 21:45:17 -07:00
this . setState ( { rawJSON : e . target . value } ) ;
2020-08-23 21:19:56 -07:00
}
2020-08-23 18:29:29 -07:00
getSoapboxConfig = ( ) => {
2021-08-23 12:14:47 -07:00
const { features } = this . props ;
const { soapbox } = this . state ;
return makeDefaultConfig ( features ) . mergeDeep ( soapbox ) ;
2020-07-25 21:31:39 -07:00
}
2020-10-01 13:54:29 -07:00
toggleJSONEditor = ( value ) => this . setState ( { jsonEditorExpanded : value } ) ;
2020-08-23 18:29:29 -07:00
componentDidUpdate ( prevProps , prevState ) {
if ( prevProps . soapbox !== this . props . soapbox ) {
2020-08-23 21:19:56 -07:00
this . putConfig ( this . props . soapbox ) ;
2020-08-23 18:29:29 -07:00
}
2020-08-23 21:45:17 -07:00
if ( prevState . soapbox !== this . state . soapbox ) {
this . setState ( { rawJSON : JSON . stringify ( this . state . soapbox , null , 2 ) } ) ;
}
if ( prevState . rawJSON !== this . state . rawJSON ) {
try {
const data = fromJS ( JSON . parse ( this . state . rawJSON ) ) ;
this . putConfig ( data ) ;
} catch {
this . setState ( { jsonValid : false } ) ;
}
}
2020-07-25 21:31:39 -07:00
}
2020-07-20 16:18:54 -07:00
render ( ) {
2020-08-12 18:52:32 -07:00
const { intl } = this . props ;
2020-08-23 18:29:29 -07:00
const soapbox = this . getSoapboxConfig ( ) ;
2020-07-20 16:18:54 -07:00
return (
2021-10-14 11:38:16 -07:00
< Column icon = 'cog' heading = { intl . formatMessage ( messages . heading ) } >
2020-07-21 06:30:51 -07:00
< SimpleForm onSubmit = { this . handleSubmit } >
< fieldset disabled = { this . state . isLoading } >
2020-10-01 16:57:11 -07:00
< SitePreview soapbox = { soapbox } / >
2020-07-21 06:30:51 -07:00
< FieldsGroup >
2020-08-23 19:35:33 -07:00
< div className = 'fields-row file-picker' >
2020-07-21 06:30:51 -07:00
< div className = 'fields-row__column fields-row__column-6' >
2020-10-01 16:57:11 -07:00
< ColorWithPicker
buttonId = 'brand_color'
label = { < FormattedMessage id = 'soapbox_config.fields.brand_color_label' defaultMessage = 'Brand color' / > }
value = { soapbox . get ( 'brandColor' ) }
onChange = { this . handleChange ( [ 'brandColor' ] , ( e ) => e . hex ) }
/ >
2020-10-01 18:53:11 -07:00
< div className = 'input with_label toggle' >
< div className = 'label_input' >
2020-10-01 19:26:20 -07:00
< label > < FormattedMessage id = 'soapbox_config.fields.theme_label' defaultMessage = 'Default theme' / > < / l a b e l >
2020-10-01 18:53:11 -07:00
< ThemeToggle
onToggle = { this . handleChange ( [ 'defaultSettings' , 'themeMode' ] , value => value ) }
2021-07-01 18:50:03 -07:00
themeMode = { soapbox . getIn ( [ 'defaultSettings' , 'themeMode' ] ) }
intl = { intl }
2020-10-01 18:53:11 -07:00
/ >
< / d i v >
< / d i v >
2020-07-21 06:30:51 -07:00
< / d i v >
< div className = 'fields-row__column fields-group fields-row__column-6' >
2020-08-02 12:07:13 -07:00
< FileChooserLogo
2020-08-23 13:21:19 -07:00
label = { < FormattedMessage id = 'soapbox_config.fields.logo_label' defaultMessage = 'Logo' / > }
2020-07-23 17:45:34 -07:00
name = 'logo'
2020-08-23 20:44:13 -07:00
hint = { < FormattedMessage id = 'soapbox_config.hints.logo' defaultMessage = 'SVG. At most 2 MB. Will be displayed to 50px height, maintaining aspect ratio' / > }
2020-08-23 19:35:33 -07:00
onChange = { this . handleFileChange ( [ 'logo' ] ) }
2020-07-21 06:30:51 -07:00
/ >
2020-07-24 17:30:33 -07:00
< / d i v >
< / d i v >
< / F i e l d s G r o u p >
2020-08-02 17:44:04 -07:00
< FieldsGroup >
< TextInput
name = 'copyright'
label = { intl . formatMessage ( messages . copyrightFooterLabel ) }
placeholder = { intl . formatMessage ( messages . copyrightFooterLabel ) }
2020-08-23 18:29:29 -07:00
value = { soapbox . get ( 'copyright' ) }
onChange = { this . handleChange ( [ 'copyright' ] , ( e ) => e . target . value ) }
2020-08-02 17:44:04 -07:00
/ >
< / F i e l d s G r o u p >
2021-03-15 20:23:33 -07:00
< FieldsGroup >
< Checkbox
name = 'verifiedCanEditName'
label = { intl . formatMessage ( messages . verifiedCanEditNameLabel ) }
checked = { soapbox . get ( 'verifiedCanEditName' ) === true }
onChange = { this . handleChange ( [ 'verifiedCanEditName' ] , ( e ) => e . target . checked ) }
/ >
2021-04-10 14:21:10 -07:00
< Checkbox
2021-06-30 19:39:27 -07:00
name = 'displayFqn'
2021-04-10 14:21:10 -07:00
label = { intl . formatMessage ( messages . displayFqnLabel ) }
checked = { soapbox . get ( 'displayFqn' ) === true }
onChange = { this . handleChange ( [ 'displayFqn' ] , ( e ) => e . target . checked ) }
/ >
2021-06-30 19:39:27 -07:00
< Checkbox
name = 'greentext'
label = { intl . formatMessage ( messages . greentextLabel ) }
checked = { soapbox . get ( 'greentext' ) === true }
onChange = { this . handleChange ( [ 'greentext' ] , ( e ) => e . target . checked ) }
/ >
2021-09-10 09:44:18 -07:00
< Checkbox
name = 'authenticatedProfile'
label = { intl . formatMessage ( messages . authenticatedProfileLabel ) }
hint = { intl . formatMessage ( messages . authenticatedProfileHint ) }
checked = { soapbox . get ( 'authenticatedProfile' ) === true }
onChange = { this . handleChange ( [ 'authenticatedProfile' ] , ( e ) => e . target . checked ) }
/ >
2021-03-15 20:23:33 -07:00
< / F i e l d s G r o u p >
2020-08-12 15:24:14 -07:00
< FieldsGroup >
2020-10-14 07:46:45 -07:00
< div className = 'input with_block_label popup' >
2020-10-01 17:44:40 -07:00
< label > < FormattedMessage id = 'soapbox_config.fields.promo_panel_fields_label' defaultMessage = 'Promo panel items' / > < / l a b e l >
< span className = 'hint' >
2021-07-20 19:07:02 -07:00
< FormattedMessage id = 'soapbox_config.hints.promo_panel_fields' defaultMessage = 'You can have custom defined links displayed on the right panel of the timelines page.' / >
2020-10-01 17:44:40 -07:00
< / s p a n >
< span className = 'hint' >
2021-10-28 03:40:28 -07:00
< FormattedMessage id = 'soapbox_config.hints.promo_panel_icons' defaultMessage = '{ link }' values = { { link : < a target = '_blank' href = 'https://icons8.com/line-awesome' > { intl . formatMessage ( messages . promoPanelIconsLink ) } < /a> }} / >
2020-10-01 17:44:40 -07:00
< / s p a n >
{
soapbox . getIn ( [ 'promoPanel' , 'items' ] ) . map ( ( field , i ) => (
< div className = 'row' key = { i } >
2020-10-14 07:22:42 -07:00
< IconPicker
2020-10-01 17:44:40 -07:00
label = { intl . formatMessage ( messages . promoItemIcon ) }
value = { field . get ( 'icon' ) }
2020-10-16 09:55:35 -07:00
onChange = { this . handlePromoItemChange ( i , 'icon' , field , val => val . id ) }
2020-10-01 17:44:40 -07:00
/ >
< TextInput
label = { intl . formatMessage ( messages . promoItemLabel ) }
placeholder = { intl . formatMessage ( messages . promoItemLabel ) }
value = { field . get ( 'text' ) }
onChange = { this . handlePromoItemChange ( i , 'text' , field ) }
/ >
< TextInput
label = { intl . formatMessage ( messages . promoItemURL ) }
placeholder = { intl . formatMessage ( messages . promoItemURL ) }
value = { field . get ( 'url' ) }
onChange = { this . handlePromoItemChange ( i , 'url' , field ) }
/ >
< Icon id = 'times-circle' onClick = { this . handleDeleteItem ( [ 'promoPanel' , 'items' , i ] ) } / >
2020-08-10 17:29:38 -07:00
< / d i v >
2020-10-01 17:44:40 -07:00
) )
}
2020-10-01 19:48:24 -07:00
< div className = 'actions add-row' >
2020-10-01 17:44:40 -07:00
< div name = 'button' type = 'button' role = 'presentation' className = 'btn button button-secondary' onClick = { this . handleAddItem ( [ 'promoPanel' , 'items' ] , templates . promoPanelItem ) } >
< Icon id = 'plus-circle' / >
< FormattedMessage id = 'soapbox_config.fields.promo_panel.add' defaultMessage = 'Add new Promo panel item' / >
2020-07-24 17:30:33 -07:00
< / d i v >
2020-07-21 06:30:51 -07:00
< / d i v >
2020-10-01 17:44:40 -07:00
< / d i v >
2020-10-01 18:53:11 -07:00
< / F i e l d s G r o u p >
< FieldsGroup >
2020-10-01 17:44:40 -07:00
< div className = 'input with_block_label' >
< label > < FormattedMessage id = 'soapbox_config.fields.home_footer_fields_label' defaultMessage = 'Home footer items' / > < / l a b e l >
< span className = 'hint' >
< FormattedMessage id = 'soapbox_config.hints.home_footer_fields' defaultMessage = 'You can have custom defined links displayed on the footer of your static pages' / >
< / s p a n >
{
soapbox . getIn ( [ 'navlinks' , 'homeFooter' ] ) . map ( ( field , i ) => (
< div className = 'row' key = { i } >
< TextInput
label = { intl . formatMessage ( messages . homeFooterItemLabel ) }
placeholder = { intl . formatMessage ( messages . homeFooterItemLabel ) }
value = { field . get ( 'title' ) }
onChange = { this . handleHomeFooterItemChange ( i , 'title' , field ) }
/ >
< TextInput
label = { intl . formatMessage ( messages . homeFooterItemURL ) }
placeholder = { intl . formatMessage ( messages . homeFooterItemURL ) }
value = { field . get ( 'url' ) }
onChange = { this . handleHomeFooterItemChange ( i , 'url' , field ) }
/ >
< Icon id = 'times-circle' onClick = { this . handleDeleteItem ( [ 'navlinks' , 'homeFooter' , i ] ) } / >
2020-08-10 17:29:38 -07:00
< / d i v >
2020-10-01 17:44:40 -07:00
) )
}
2020-10-01 19:48:24 -07:00
< div className = 'actions add-row' >
2020-10-01 17:44:40 -07:00
< div name = 'button' type = 'button' role = 'presentation' className = 'btn button button-secondary' onClick = { this . handleAddItem ( [ 'navlinks' , 'homeFooter' ] , templates . footerItem ) } >
< Icon id = 'plus-circle' / >
< FormattedMessage id = 'soapbox_config.fields.home_footer.add' defaultMessage = 'Add new Home Footer Item' / >
2020-07-24 17:30:33 -07:00
< / d i v >
< / d i v >
< / d i v >
2020-08-12 15:24:14 -07:00
< / F i e l d s G r o u p >
2021-06-10 11:48:43 -07:00
< FieldsGroup >
< div className = 'input with_block_label' >
< label > < FormattedMessage id = 'soapbox_config.fields.crypto_addresses_label' defaultMessage = 'Cryptocurrency addresses' / > < / l a b e l >
< span className = 'hint' >
< FormattedMessage id = 'soapbox_config.hints.crypto_addresses' defaultMessage = 'Add cryptocurrency addresses so users of your site can donate to you. Order matters, and you must use lowercase ticker values.' / >
< / s p a n >
{
soapbox . get ( 'cryptoAddresses' ) . map ( ( address , i ) => (
< div className = 'row' key = { i } >
< TextInput
label = { intl . formatMessage ( messages . cryptoAdressItemTicker ) }
placeholder = { intl . formatMessage ( messages . cryptoAdressItemTicker ) }
value = { address . get ( 'ticker' ) }
onChange = { this . handleCryptoAdressItemChange ( i , 'ticker' , address ) }
/ >
< TextInput
label = { intl . formatMessage ( messages . cryptoAdressItemAddress ) }
placeholder = { intl . formatMessage ( messages . cryptoAdressItemAddress ) }
value = { address . get ( 'address' ) }
onChange = { this . handleCryptoAdressItemChange ( i , 'address' , address ) }
/ >
< TextInput
label = { intl . formatMessage ( messages . cryptoAdressItemNote ) }
placeholder = { intl . formatMessage ( messages . cryptoAdressItemNote ) }
value = { address . get ( 'note' ) }
onChange = { this . handleCryptoAdressItemChange ( i , 'note' , address ) }
/ >
< Icon id = 'times-circle' onClick = { this . handleDeleteItem ( [ 'cryptoAddresses' , i ] ) } / >
< / d i v >
) )
}
< div className = 'actions add-row' >
< div name = 'button' type = 'button' role = 'presentation' className = 'btn button button-secondary' onClick = { this . handleAddItem ( [ 'cryptoAddresses' ] , templates . cryptoAddress ) } >
< Icon id = 'plus-circle' / >
< FormattedMessage id = 'soapbox_config.fields.crypto_address.add' defaultMessage = 'Add new crypto address' / >
< / d i v >
< / d i v >
< / d i v >
< / F i e l d s G r o u p >
< FieldsGroup >
< SimpleInput
type = 'number'
min = { 0 }
pattern = '[0-9]+'
name = 'cryptoDonatePanelLimit'
label = { intl . formatMessage ( messages . cryptoDonatePanelLimitLabel ) }
placeholder = { intl . formatMessage ( messages . cryptoDonatePanelLimitLabel ) }
value = { soapbox . getIn ( [ 'cryptoDonatePanel' , 'limit' ] ) }
onChange = { this . handleChange ( [ 'cryptoDonatePanel' , 'limit' ] , ( e ) => Number ( e . target . value ) ) }
/ >
< / F i e l d s G r o u p >
2020-09-30 12:09:00 -07:00
< Accordion
headline = { intl . formatMessage ( messages . rawJSONLabel ) }
2020-10-01 13:54:29 -07:00
expanded = { this . state . jsonEditorExpanded }
onToggle = { this . toggleJSONEditor }
2020-12-31 18:18:57 -08:00
>
< div className = { this . state . jsonValid ? 'code-editor' : 'code-editor code-editor--invalid' } >
< SimpleTextarea
hint = { intl . formatMessage ( messages . rawJSONHint ) }
value = { this . state . rawJSON }
onChange = { this . handleEditJSON }
rows = { 12 }
/ >
< / d i v >
< / A c c o r d i o n >
2020-07-21 06:30:51 -07:00
< / f i e l d s e t >
< div className = 'actions' >
< button name = 'button' type = 'submit' className = 'btn button button-primary' >
2020-08-23 13:21:19 -07:00
< FormattedMessage id = 'soapbox_config.save' defaultMessage = 'Save' / >
2020-07-21 06:30:51 -07:00
< / b u t t o n >
< / d i v >
2020-07-20 16:18:54 -07:00
< / S i m p l e F o r m >
< / C o l u m n >
) ;
}
}
2020-09-23 20:46:45 -07:00
class ColorPicker extends React . PureComponent {
static propTypes = {
style : PropTypes . object ,
value : PropTypes . string . isRequired ,
onChange : PropTypes . func . isRequired ,
onClose : PropTypes . func ,
}
handleDocumentClick = e => {
if ( this . node && ! this . node . contains ( e . target ) ) {
this . props . onClose ( ) ;
}
}
componentDidMount ( ) {
document . addEventListener ( 'click' , this . handleDocumentClick , false ) ;
document . addEventListener ( 'touchend' , this . handleDocumentClick , listenerOptions ) ;
}
componentWillUnmount ( ) {
document . removeEventListener ( 'click' , this . handleDocumentClick , false ) ;
document . removeEventListener ( 'touchend' , this . handleDocumentClick , listenerOptions ) ;
}
setRef = c => {
this . node = c ;
}
render ( ) {
const { style , value , onChange } = this . props ;
2021-08-03 10:10:42 -07:00
const margin _left _picker = isMobile ( window . innerWidth ) ? '20px' : '12px' ;
2020-09-23 20:46:45 -07:00
return (
< div id = 'SketchPickerContainer' ref = { this . setRef } style = { { ... style , marginLeft : margin _left _picker , position : 'absolute' , zIndex : 1000 } } >
< SketchPicker color = { value } disableAlpha onChange = { onChange } / >
< / d i v >
) ;
}
}
class ColorWithPicker extends ImmutablePureComponent {
static propTypes = {
buttonId : PropTypes . string . isRequired ,
label : FormPropTypes . label ,
value : PropTypes . string . isRequired ,
onChange : PropTypes . func . isRequired ,
}
onToggle = ( e ) => {
if ( ! e . key || e . key === 'Enter' ) {
if ( this . state . active ) {
this . onHidePicker ( ) ;
} else {
this . onShowPicker ( e ) ;
}
}
}
state = {
active : false ,
placement : null ,
}
onHidePicker = ( ) => {
this . setState ( { active : false } ) ;
}
onShowPicker = ( { target } ) => {
this . setState ( { active : true } ) ;
this . setState ( { placement : isMobile ( window . innerWidth ) ? 'bottom' : 'right' } ) ;
}
render ( ) {
const { buttonId , label , value , onChange } = this . props ;
const { active , placement } = this . state ;
return (
< div className = 'label_input__color' >
< label > { label } < / l a b e l >
< div id = { buttonId } className = 'color-swatch' role = 'presentation' style = { { background : value } } title = { value } value = { value } onClick = { this . onToggle } / >
< Overlay show = { active } placement = { placement } target = { this } >
< ColorPicker value = { value } onChange = { onChange } onClose = { this . onHidePicker } / >
< / O v e r l a y >
< / d i v >
) ;
}
}
2020-10-14 07:46:45 -07:00
export class IconPicker extends ImmutablePureComponent {
static propTypes = {
icons : PropTypes . object ,
label : FormPropTypes . label ,
value : PropTypes . string ,
onChange : PropTypes . func . isRequired ,
}
render ( ) {
2020-10-16 09:55:35 -07:00
const { onChange , value , label } = this . props ;
2020-10-14 07:46:45 -07:00
return (
2020-10-16 09:55:35 -07:00
< div className = 'input with_label font_icon_picker' >
< div className = 'label_input__font_icon_picker' >
2020-10-14 07:46:45 -07:00
{ label && ( < label > { label } < / l a b e l > ) }
2020-10-16 09:55:35 -07:00
< div className = 'label_input_wrapper' >
< IconPickerDropdown value = { value } onPickEmoji = { onChange } / >
2020-10-14 07:46:45 -07:00
< / d i v >
< / d i v >
< / d i v >
) ;
}
}