This commit is contained in:
mgabdev 2020-03-31 12:04:50 -04:00
parent bfda5978d1
commit 1a33759e19
37 changed files with 651 additions and 458 deletions

View File

@ -3,6 +3,7 @@ import { CancelToken, isCancel } from 'axios';
import { throttle } from 'lodash'; import { throttle } from 'lodash';
import moment from 'moment'; import moment from 'moment';
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/compose/util/url_regex'
import { tagHistory } from '../settings'; import { tagHistory } from '../settings';
import { useEmoji } from './emojis'; import { useEmoji } from './emojis';
import resizeImage from '../utils/resize_image'; import resizeImage from '../utils/resize_image';
@ -62,6 +63,8 @@ export const COMPOSE_POLL_SETTINGS_CHANGE = 'COMPOSE_POLL_SETTINGS_CHANGE';
export const COMPOSE_SCHEDULED_AT_CHANGE = 'COMPOSE_SCHEDULED_AT_CHANGE'; export const COMPOSE_SCHEDULED_AT_CHANGE = 'COMPOSE_SCHEDULED_AT_CHANGE';
export const COMPOSE_RICH_TEXT_EDITOR_CONTROLS_VISIBILITY = 'COMPOSE_RICH_TEXT_EDITOR_CONTROLS_VISIBILITY'
const messages = defineMessages({ const messages = defineMessages({
uploadErrorLimit: { id: 'upload_error.limit', defaultMessage: 'File upload limit exceeded.' }, uploadErrorLimit: { id: 'upload_error.limit', defaultMessage: 'File upload limit exceeded.' },
uploadErrorPoll: { id: 'upload_error.poll', defaultMessage: 'File upload not allowed with polls.' }, uploadErrorPoll: { id: 'upload_error.poll', defaultMessage: 'File upload not allowed with polls.' },
@ -170,12 +173,20 @@ export function submitCompose(routerHistory, group) {
return function (dispatch, getState) { return function (dispatch, getState) {
if (!me) return; if (!me) return;
const status = getState().getIn(['compose', 'text'], ''); let status = getState().getIn(['compose', 'text'], '');
let statusMarkdown = getState().getIn(['compose', 'text_markdown'], '');
const media = getState().getIn(['compose', 'media_attachments']); const media = getState().getIn(['compose', 'media_attachments']);
if ((!status || !status.length) && media.size === 0) { // : hack :
return; //Prepend http:// to urls in status that don't have protocol
} status = status.replace(urlRegex, (match) =>{
const hasProtocol = match.startsWith('https://') || match.startsWith('http://')
return hasProtocol ? match : `http://${match}`
})
statusMarkdown = statusMarkdown.replace(urlRegex, (match) =>{
const hasProtocol = match.startsWith('https://') || match.startsWith('http://')
return hasProtocol ? match : `http://${match}`
})
dispatch(submitComposeRequest()); dispatch(submitComposeRequest());
dispatch(closeModal()); dispatch(closeModal());
@ -191,6 +202,7 @@ export function submitCompose(routerHistory, group) {
api(getState)[method](endpoint, { api(getState)[method](endpoint, {
status, status,
statusMarkdown,
scheduled_at, scheduled_at,
in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null), in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null),
quote_of_id: getState().getIn(['compose', 'quote_of_id'], null), quote_of_id: getState().getIn(['compose', 'quote_of_id'], null),
@ -594,3 +606,9 @@ export function changeScheduledAt(date) {
date, date,
}; };
}; };
export function changeRichTextEditorControlsVisibility() {
return {
type: COMPOSE_RICH_TEXT_EDITOR_CONTROLS_VISIBILITY,
}
}

View File

@ -0,0 +1,26 @@
const DonorIcon = ({
className = '',
width = '24px',
height = '24px',
viewBox = '0 0 24 24',
title = 'Gab.com Donor',
}) => (
<svg
className={className}
version='1.1'
xmlns='http://www.w3.org/2000/svg'
x='0px'
y='0px'
width={width}
height={height}
viewBox={viewBox}
xmlSpace='preserve'
aria-label={title}
>
<g>
<path fill='purple' d='M22.5 12.5c0-1.58-.875-2.95-2.148-3.6.154-.435.238-.905.238-1.4 0-2.21-1.71-3.998-3.818-3.998-.47 0-.92.084-1.336.25C14.818 2.415 13.51 1.5 12 1.5s-2.816.917-3.437 2.25c-.415-.165-.866-.25-1.336-.25-2.11 0-3.818 1.79-3.818 4 0 .494.083.964.237 1.4-1.272.65-2.147 2.018-2.147 3.6 0 1.495.782 2.798 1.942 3.486-.02.17-.032.34-.032.514 0 2.21 1.708 4 3.818 4 .47 0 .92-.086 1.335-.25.62 1.334 1.926 2.25 3.437 2.25 1.512 0 2.818-.916 3.437-2.25.415.163.865.248 1.336.248 2.11 0 3.818-1.79 3.818-4 0-.174-.012-.344-.033-.513 1.158-.687 1.943-1.99 1.943-3.484zm-6.616-3.334l-4.334 6.5c-.145.217-.382.334-.625.334-.143 0-.288-.04-.416-.126l-.115-.094-2.415-2.415c-.293-.293-.293-.768 0-1.06s.768-.294 1.06 0l1.77 1.767 3.825-5.74c.23-.345.696-.436 1.04-.207.346.23.44.696.21 1.04z' />
</g>
</svg>
)
export default DonorIcon

View File

@ -1,9 +1,11 @@
const GabLogo = ({ const GabLogo = ({
className = _s.fillColorBrand,
width = '50px', width = '50px',
height = '30px', height = '30px',
viewBox = '0 0 50 30' viewBox = '0 0 50 30'
}) => ( }) => (
<svg <svg
className={className}
version='1.1' version='1.1'
xmlns='http://www.w3.org/2000/svg' xmlns='http://www.w3.org/2000/svg'
x='0px' x='0px'
@ -14,14 +16,14 @@ const GabLogo = ({
xmlSpace='preserve' xmlSpace='preserve'
> >
<g> <g>
<path className={_s.fillColorBrand} d='M13.8,7.6h-2.4v0.7V9l-0.4-0.3C10.2,7.8,9,7.2,7.7,7.2c-0.2,0-0.4,0-0.4,0c-0.1,0-0.3,0-0.5,0 <path className={_s.inheritFill} d='M13.8,7.6h-2.4v0.7V9l-0.4-0.3C10.2,7.8,9,7.2,7.7,7.2c-0.2,0-0.4,0-0.4,0c-0.1,0-0.3,0-0.5,0
c-5.6,0.3-8.7,7.2-5.4,12.1c2.3,3.4,7.1,4.1,9.7,1.5l0.3-0.3l0,0.7c0,1-0.1,1.5-0.4,2.2c-1,2.4-4.1,3-6.8,1.3 c-5.6,0.3-8.7,7.2-5.4,12.1c2.3,3.4,7.1,4.1,9.7,1.5l0.3-0.3l0,0.7c0,1-0.1,1.5-0.4,2.2c-1,2.4-4.1,3-6.8,1.3
c-0.2-0.1-0.4-0.2-0.4-0.2c-0.1,0.1-1.9,3.5-1.9,3.6c0,0.1,0.5,0.4,0.8,0.6c2.2,1.4,5.6,1.7,8.3,0.8c2.7-0.9,4.5-3.2,5-6.4 c-0.2-0.1-0.4-0.2-0.4-0.2c-0.1,0.1-1.9,3.5-1.9,3.6c0,0.1,0.5,0.4,0.8,0.6c2.2,1.4,5.6,1.7,8.3,0.8c2.7-0.9,4.5-3.2,5-6.4
c0.2-1.1,0.2-0.8,0.2-8.4l0-7.1H13.8z M9.7,17.6c-2.2,1.2-4.9-0.4-4.9-2.9C4.8,12.6,7,11,9,11.6C11.8,12.4,12.3,16.1,9.7,17.6z'/> c0.2-1.1,0.2-0.8,0.2-8.4l0-7.1H13.8z M9.7,17.6c-2.2,1.2-4.9-0.4-4.9-2.9C4.8,12.6,7,11,9,11.6C11.8,12.4,12.3,16.1,9.7,17.6z'/>
<path className={_s.fillColorBrand} d='M45.6,7.7c-2.4-1-5-0.6-6.7,1L38.6,9V4.5V0h-2.4h-2.4v11v11h2.4h2.4v-0.7v-0.7l0.3,0.3 <path className={_s.inheritFill} d='M45.6,7.7c-2.4-1-5-0.6-6.7,1L38.6,9V4.5V0h-2.4h-2.4v11v11h2.4h2.4v-0.7v-0.7l0.3,0.3
c2.4,2.4,6.9,1.9,9.3-0.9C51.5,15.9,50.1,9.6,45.6,7.7z M43.7,17.5c-2.1,1.4-5.1-0.2-5.1-2.8c0-2.1,1.9-3.7,3.9-3.3 c2.4,2.4,6.9,1.9,9.3-0.9C51.5,15.9,50.1,9.6,45.6,7.7z M43.7,17.5c-2.1,1.4-5.1-0.2-5.1-2.8c0-2.1,1.9-3.7,3.9-3.3
C45.4,12.1,46.2,15.8,43.7,17.5z'/> C45.4,12.1,46.2,15.8,43.7,17.5z'/>
<path className={_s.fillColorBrand} d='M31.5,12.5c-0.7-3.7-3.1-5.5-7.1-5.3c-1.7,0.1-4,0.7-4.8,1.4l-0.1,0.1l0.7,1.7c0.4,0.9,0.7,1.7,0.7,1.7 <path className={_s.inheritFill} d='M31.5,12.5c-0.7-3.7-3.1-5.5-7.1-5.3c-1.7,0.1-4,0.7-4.8,1.4l-0.1,0.1l0.7,1.7c0.4,0.9,0.7,1.7,0.7,1.7
c0,0,0.1,0,0.2-0.1c2.7-1.4,5.4-0.9,5.6,1.1l0,0.2l-0.4-0.1c-3-0.8-6.3-0.1-7.7,1.6c-1.8,2.2-0.9,5.8,1.7,7.1 c0,0,0.1,0,0.2-0.1c2.7-1.4,5.4-0.9,5.6,1.1l0,0.2l-0.4-0.1c-3-0.8-6.3-0.1-7.7,1.6c-1.8,2.2-0.9,5.8,1.7,7.1
c2.1,1,4.6,0.6,6.1-0.8l0.2-0.2v0.6v0.6h2.4h2.4v-4C31.7,13.7,31.7,13.3,31.5,12.5z M26.9,16.4c-0.1,0.7-0.5,1.5-1,2 c2.1,1,4.6,0.6,6.1-0.8l0.2-0.2v0.6v0.6h2.4h2.4v-4C31.7,13.7,31.7,13.3,31.5,12.5z M26.9,16.4c-0.1,0.7-0.5,1.5-1,2
c-1.2,1.1-3,0.7-3.2-0.7c-0.2-1,0.6-1.7,2-1.8c0.1,0,0.2,0,0.2,0c0,0,0.2,0,0.4,0c0.5,0,1,0.1,1.4,0.2l0.3,0.1L26.9,16.4z'/> c-1.2,1.1-3,0.7-3.2-0.7c-0.2-1,0.6-1.7,2-1.8c0.1,0,0.2,0,0.2,0c0,0,0.2,0,0.4,0c0.5,0,1,0.1,1.4,0.2l0.3,0.1L26.9,16.4z'/>

View File

@ -0,0 +1,26 @@
const InvestorIcon = ({
className = '',
width = '24px',
height = '24px',
viewBox = '0 0 24 24',
title = 'Gab.com Investor',
}) => (
<svg
className={className}
version='1.1'
xmlns='http://www.w3.org/2000/svg'
x='0px'
y='0px'
width={width}
height={height}
viewBox={viewBox}
xmlSpace='preserve'
aria-label={title}
>
<g>
<path fill='red' d='M22.5 12.5c0-1.58-.875-2.95-2.148-3.6.154-.435.238-.905.238-1.4 0-2.21-1.71-3.998-3.818-3.998-.47 0-.92.084-1.336.25C14.818 2.415 13.51 1.5 12 1.5s-2.816.917-3.437 2.25c-.415-.165-.866-.25-1.336-.25-2.11 0-3.818 1.79-3.818 4 0 .494.083.964.237 1.4-1.272.65-2.147 2.018-2.147 3.6 0 1.495.782 2.798 1.942 3.486-.02.17-.032.34-.032.514 0 2.21 1.708 4 3.818 4 .47 0 .92-.086 1.335-.25.62 1.334 1.926 2.25 3.437 2.25 1.512 0 2.818-.916 3.437-2.25.415.163.865.248 1.336.248 2.11 0 3.818-1.79 3.818-4 0-.174-.012-.344-.033-.513 1.158-.687 1.943-1.99 1.943-3.484zm-6.616-3.334l-4.334 6.5c-.145.217-.382.334-.625.334-.143 0-.288-.04-.416-.126l-.115-.094-2.415-2.415c-.293-.293-.293-.768 0-1.06s.768-.294 1.06 0l1.77 1.767 3.825-5.74c.23-.345.696-.436 1.04-.207.346.23.44.696.21 1.04z' />
</g>
</svg>
)
export default InvestorIcon

View File

@ -0,0 +1,26 @@
const ProIcon = ({
className = '',
width = '24px',
height = '24px',
viewBox = '0 0 24 24',
title = 'GabPRO Account',
}) => (
<svg
className={className}
version='1.1'
xmlns='http://www.w3.org/2000/svg'
x='0px'
y='0px'
width={width}
height={height}
viewBox={viewBox}
xmlSpace='preserve'
aria-label={title}
>
<g>
<path fill='green' d='M22.5 12.5c0-1.58-.875-2.95-2.148-3.6.154-.435.238-.905.238-1.4 0-2.21-1.71-3.998-3.818-3.998-.47 0-.92.084-1.336.25C14.818 2.415 13.51 1.5 12 1.5s-2.816.917-3.437 2.25c-.415-.165-.866-.25-1.336-.25-2.11 0-3.818 1.79-3.818 4 0 .494.083.964.237 1.4-1.272.65-2.147 2.018-2.147 3.6 0 1.495.782 2.798 1.942 3.486-.02.17-.032.34-.032.514 0 2.21 1.708 4 3.818 4 .47 0 .92-.086 1.335-.25.62 1.334 1.926 2.25 3.437 2.25 1.512 0 2.818-.916 3.437-2.25.415.163.865.248 1.336.248 2.11 0 3.818-1.79 3.818-4 0-.174-.012-.344-.033-.513 1.158-.687 1.943-1.99 1.943-3.484zm-6.616-3.334l-4.334 6.5c-.145.217-.382.334-.625.334-.143 0-.288-.04-.416-.126l-.115-.094-2.415-2.415c-.293-.293-.293-.768 0-1.06s.768-.294 1.06 0l1.77 1.767 3.825-5.74c.23-.345.696-.436 1.04-.207.346.23.44.696.21 1.04z' />
</g>
</svg>
)
export default ProIcon

View File

@ -253,10 +253,21 @@ export default class AutosuggestTextbox extends ImmutablePureComponent {
<div className={[_s.default, _s.flexGrow1].join(' ')}> <div className={[_s.default, _s.flexGrow1].join(' ')}>
<div className={[_s.default, _s.ml5].join(' ')}> <div className={[_s.default, _s.ml5].join(' ')}>
{/*<Composer <Composer
/>*/} inputRef={this.setTextbox}
disabled={disabled}
placeholder={placeholder}
autoFocus={autoFocus}
value={value}
onChange={this.onChange}
onKeyDown={this.onKeyDown}
onKeyUp={onKeyUp}
onFocus={this.onFocus}
onBlur={this.onBlur}
onPaste={this.onPaste}
/>
<Textarea { /* <Textarea
className={_s.default} className={_s.default}
inputRef={this.setTextbox} inputRef={this.setTextbox}
disabled={disabled} disabled={disabled}
@ -270,8 +281,7 @@ export default class AutosuggestTextbox extends ImmutablePureComponent {
onBlur={this.onBlur} onBlur={this.onBlur}
onPaste={this.onPaste} onPaste={this.onPaste}
aria-autocomplete='list' aria-autocomplete='list'
/> /> */ }
{ /* { /*
<Textarea <Textarea

View File

@ -1,36 +1,315 @@
import { import {
Editor, Editor,
EditorState, EditorState,
CompositeDecorator,
RichUtils RichUtils
} from 'draft-js' } from 'draft-js'
import { urlRegex } from '../features/compose/util/url_regex'
import classNames from 'classnames/bind'
import Button from './button'
const cx = classNames.bind(_s)
const getBlockStyle = (block) => {
switch (block.getType()) {
case 'blockquote':
return 'RichEditor-blockquote'
default:
return null
}
}
function handleStrategy(contentBlock, callback, contentState) {
findWithRegex(HANDLE_REGEX, contentBlock, callback)
}
function hashtagStrategy (contentBlock, callback, contentState) {
findWithRegex(HASHTAG_REGEX, contentBlock, callback)
}
function urlStrategy (contentBlock, callback, contentState) {
findWithRegex(urlRegex, contentBlock, callback)
}
const findWithRegex = (regex, contentBlock, callback) => {
const text = contentBlock.getText()
let matchArr, start
while ((matchArr = regex.exec(text)) !== null) {
start = matchArr.index
callback(start, start + matchArr[0].length)
}
}
const HighlightedSpan = (props) => {
console.log("HighlightedSpan:", props)
return (
<span
className={_s.colorBrand}
data-offset-key={props.offsetKey}
>
{props.children}
</span>
)
}
const RTE_ITEMS = [
{
label: 'Bold',
style: 'BOLD',
type: 'style',
icon: 'circle',
},
{
label: 'Italic',
style: 'ITALIC',
type: 'style',
icon: 'circle',
},
{
label: 'Underline',
style: 'UNDERLINE',
type: 'style',
icon: 'circle',
},
{
label: 'Monospace',
style: 'CODE',
type: 'style',
icon: 'circle',
},
{
label: 'H1',
style: 'header-one',
type: 'block',
icon: 'circle',
},
{
label: 'Blockquote',
style: 'blockquote',
type: 'block',
icon: 'circle',
},
{
label: 'UL',
style: 'unordered-list-item',
type: 'block',
icon: 'circle',
},
{
label: 'OL',
style: 'ordered-list-item',
type: 'block',
icon: 'circle',
},
{
label: 'Code Block',
style: 'code-block',
type: 'block',
icon: 'circle',
},
]
const compositeDecorator = new CompositeDecorator([
{
strategy: handleStrategy,
component: HighlightedSpan,
},
{
strategy: hashtagStrategy,
component: HighlightedSpan,
},
{
strategy: urlStrategy,
component: HighlightedSpan,
}
])
const HANDLE_REGEX = /\@[\w]+/g;
const HASHTAG_REGEX = /\#[\w\u0590-\u05ff]+/g;
export default class Composer extends PureComponent { export default class Composer extends PureComponent {
static propTypes = {
inputRef: PropTypes.func,
disabled: PropTypes.bool,
placeholder: PropTypes.string,
autoFocus: PropTypes.bool,
value: PropTypes.string,
onChange: PropTypes.func,
onKeyDown: PropTypes.func,
onKeyUp: PropTypes.func,
onFocus: PropTypes.func,
onBlur: PropTypes.func,
onPaste: PropTypes.func,
}
state = { state = {
editorState: EditorState.createEmpty(), editorState: EditorState.createEmpty(compositeDecorator),
} }
onChange = (editorState) => { onChange = (editorState) => {
this.setState({ editorState }) this.setState({ editorState })
} }
onBoldClick() { onToggleInlineStyle = (style) => {
this.onChange(RichUtils.toggleInlineStyle(this.state.editorState, 'BOLD')); this.onChange(RichUtils.toggleInlineStyle(this.state.editorState, style))
}
focus = () => {
this.textbox.editor.focus()
}
handleKeyCommand = (command) => {
const { editorState } = this.state
const newState = RichUtils.handleKeyCommand(editorState, command)
if (newState) {
this.onChange(newState)
return true
}
return false
}
onTab = (e) => {
const maxDepth = 4
this.onChange(RichUtils.onTab(e, this.state.editorState, maxDepth))
}
toggleBlockType = (blockType) => {
this.onChange(
RichUtils.toggleBlockType(
this.state.editorState,
blockType
)
)
}
toggleInlineStyle = (inlineStyle) => {
this.onChange(
RichUtils.toggleInlineStyle(
this.state.editorState,
inlineStyle
)
)
}
setRef = (n) => {
this.textbox = n
} }
render() { render() {
const {
inputRef,
disabled,
placeholder,
autoFocus,
// value,
onChange,
onKeyDown,
onKeyUp,
onFocus,
onBlur,
onPaste
} = this.props
const { editorState } = this.state
return ( return (
<div> <div className={[_s.default].join(' ')}>
{/*<button onClick={this.onBoldClick.bind(this)}>Bold</button>*/} <div className={[_s.default, _s.backgroundColorPrimary, _s.borderBottom1PX, _s.borderColorSecondary, _s.py5, _s.px15, _s.alignItemsCenter, _s.flexRow].join(' ')}>
{
<Editor RTE_ITEMS.map((item, i) => (
editorState={this.state.editorState} <StyleButton
onChange={this.onChange} key={`rte-button-${i}`}
editorState={editorState}
{...item}
/> />
))
}
</div>
<div
onClick={this.focus}
className={[_s.text, _s.fontSize16PX].join(' ')}
>
<Editor
blockStyleFn={getBlockStyle}
// customStyleMap={styleMap}
editorState={editorState}
handleKeyCommand={this.handleKeyCommand}
onChange={this.onChange}
onTab={this.onTab}
placeholder={placeholder}
ref={this.setRef}
/>
</div>
</div> </div>
) )
} }
} }
class StyleButton extends PureComponent {
static propTypes = {
onToggle: PropTypes.func,
label: PropTypes.string,
style: PropTypes.string,
icon: PropTypes.string,
type: PropTypes.string,
}
handleOnToggle = (e) => {
const { onToggle, style } = this.props
e.preventDefault()
onToggle(style)
}
render() {
const {
label,
style,
type,
icon,
editorState
} = this.props
const selection = editorState.getSelection()
const currentStyle = editorState.getCurrentInlineStyle()
const blockType = editorState.getCurrentContent().getBlockForKey(selection.getStartKey()).getType()
let active
// active={type.style === blockType}
// active={currentStyle.has(type.style)}
const btnClasses = cx({
px10: 1,
mr5: 1,
backgroundSubtle2Dark_onHover: 1,
backgroundColorBrandLight: active,
// py10: !small,
// py5: small,
// px5: small,
})
const iconClasses = cx({
fillColorSecondary: !active,
fillColorWhite: active,
})
return (
<Button
className={btnClasses}
backgroundColor='none'
onClick={this.handleOnToggle}
title={label}
icon={'rich-text'}
iconClassName={iconClasses}
iconWidth='10px'
iconHeight='10px'
radiusSmall
/>
)
}
}

View File

@ -105,6 +105,11 @@ class DisplayName extends ImmutablePureComponent {
!!large ? '19px' : !!large ? '19px' :
!!small ? '14px' : '16px' !!small ? '14px' : '16px'
const domain = account.get('acct').split('@')[1];
const isRemoteUser = !!domain
console.log("domain, isRemoteUser:", domain, isRemoteUser)
// : todo : // : todo :
return ( return (
<span {...containerOptions} ref={this.setRef}> <span {...containerOptions} ref={this.setRef}>
@ -117,20 +122,20 @@ class DisplayName extends ImmutablePureComponent {
</bdi> </bdi>
{ {
account.get('is_verified') && account.get('is_verified') &&
<Icon id='verified' width={iconSize} height={iconSize} className={_s.default} title='Verified Account' /> <Icon id='verified' width={iconSize} height={iconSize} className={_s.default} />
} }
{ /* {
account.get('is_pro') && account.get('is_pro') &&
<Icon id='verified' width='16px' height='16px' className={_s.default} title='Gab PRO' /> <Icon id='pro' width='16px' height='16px' className={_s.default} />
*/ } }
{ /* {
account.get('is_donor') && account.get('is_donor') &&
<Icon id='verified' width='16px' height='16px' className={_s.default} title='Gab Donor' /> <Icon id='donor' width='16px' height='16px' className={_s.default} />
*/ } }
{ /* {
account.get('is_investor') && account.get('is_investor') &&
<Icon id='verified' width='16px' height='16px' className={_s.default} title='Gab Investor' /> <Icon id='investor' width='16px' height='16px' className={_s.default} />
*/ } }
</div> </div>
{ {
!noUsername && !noUsername &&

View File

@ -10,15 +10,18 @@ import CircleIcon from '../assets/circle_icon'
import CloseIcon from '../assets/close_icon' import CloseIcon from '../assets/close_icon'
import CommentIcon from '../assets/comment_icon' import CommentIcon from '../assets/comment_icon'
import DissenterIcon from '../assets/dissenter_icon' import DissenterIcon from '../assets/dissenter_icon'
import DonorIcon from '../assets/donor_icon'
import EllipsisIcon from '../assets/ellipsis_icon' import EllipsisIcon from '../assets/ellipsis_icon'
import ErrorIcon from '../assets/error_icon' import ErrorIcon from '../assets/error_icon'
import FullscreenIcon from '../assets/fullscreen_icon' import FullscreenIcon from '../assets/fullscreen_icon'
import GabLogoIcon from '../assets/gab_logo'
import GifIcon from '../assets/gif_icon' import GifIcon from '../assets/gif_icon'
import GlobeIcon from '../assets/globe_icon' import GlobeIcon from '../assets/globe_icon'
import GroupIcon from '../assets/group_icon' import GroupIcon from '../assets/group_icon'
import GroupAddIcon from '../assets/group_add_icon' import GroupAddIcon from '../assets/group_add_icon'
import HappyIcon from '../assets/happy_icon' import HappyIcon from '../assets/happy_icon'
import HomeIcon from '../assets/home_icon' import HomeIcon from '../assets/home_icon'
import InvestorIcon from '../assets/investor_icon'
import LikeIcon from '../assets/like_icon' import LikeIcon from '../assets/like_icon'
import LinkIcon from '../assets/link_icon' import LinkIcon from '../assets/link_icon'
import ListIcon from '../assets/list_icon' import ListIcon from '../assets/list_icon'
@ -33,6 +36,7 @@ import PauseIcon from '../assets/pause_icon'
import PinIcon from '../assets/pin_icon' import PinIcon from '../assets/pin_icon'
import PlayIcon from '../assets/play_icon' import PlayIcon from '../assets/play_icon'
import PollIcon from '../assets/poll_icon' import PollIcon from '../assets/poll_icon'
import ProIcon from '../assets/pro_icon'
import RepostIcon from '../assets/repost_icon' import RepostIcon from '../assets/repost_icon'
import RichTextIcon from '../assets/rich_text_icon' import RichTextIcon from '../assets/rich_text_icon'
import SearchIcon from '../assets/search_icon' import SearchIcon from '../assets/search_icon'
@ -56,15 +60,18 @@ const ICONS = {
'close': CloseIcon, 'close': CloseIcon,
'comment': CommentIcon, 'comment': CommentIcon,
'dissenter': DissenterIcon, 'dissenter': DissenterIcon,
'donor': DonorIcon,
'ellipsis': EllipsisIcon, 'ellipsis': EllipsisIcon,
'error': ErrorIcon, 'error': ErrorIcon,
'fullscreen': FullscreenIcon, 'fullscreen': FullscreenIcon,
'gab-logo': GabLogoIcon,
'gif': GifIcon, 'gif': GifIcon,
'globe': GlobeIcon, 'globe': GlobeIcon,
'group': GroupIcon, 'group': GroupIcon,
'group-add': GroupAddIcon, 'group-add': GroupAddIcon,
'happy': HappyIcon, 'happy': HappyIcon,
'home': HomeIcon, 'home': HomeIcon,
'investor': InvestorIcon,
'like': LikeIcon, 'like': LikeIcon,
'link': LinkIcon, 'link': LinkIcon,
'list': ListIcon, 'list': ListIcon,
@ -79,6 +86,7 @@ const ICONS = {
'pin': PinIcon, 'pin': PinIcon,
'play': PlayIcon, 'play': PlayIcon,
'poll': PollIcon, 'poll': PollIcon,
'pro': ProIcon,
'repost': RepostIcon, 'repost': RepostIcon,
'rich-text': RichTextIcon, 'rich-text': RichTextIcon,
'search': SearchIcon, 'search': SearchIcon,

View File

@ -134,7 +134,7 @@ class LinkFooter extends PureComponent {
defaultMessage='Gab Social is open source software. You can contribute or report issues on our self-hosted GitLab at {gitlab}.' defaultMessage='Gab Social is open source software. You can contribute or report issues on our self-hosted GitLab at {gitlab}.'
values={{ values={{
gitlab: ( gitlab: (
<a href={source_url} className={_s.inherit} rel='noopener' target='_blank'> <a href={source_url} className={_s.inherit} rel='noopener noreferrer' target='_blank'>
{repository} {repository}
</a> </a>
) )

View File

@ -0,0 +1,27 @@
import { defineMessages, injectIntl } from 'react-intl'
import ImmutablePureComponent from 'react-immutable-pure-component'
import Button from '../button'
import Text from '../text'
import ModalLayout from './modal_layout'
const messages = defineMessages({
})
export default
@injectIntl
class LeavingGabModal extends ImmutablePureComponent {
static propTypes = {
intl: PropTypes.object.isRequired,
}
render() {
const { intl } = this.props
return (
<ModalLayout>
</ModalLayout>
)
}
}

View File

@ -96,12 +96,12 @@ class ProfileInfoPanel extends ImmutablePureComponent {
{ /* : todo : */} { /* : todo : */}
<dd className='verified'> <dd className='verified'>
<a href={proof.get('proof_url')} target='_blank' rel='noopener'> <a href={proof.get('proof_url')} target='_blank' rel='noopener noreferrer'>
<span title={intl.formatMessage(messages.linkVerifiedOn, { date: intl.formatDate(proof.get('updated_at'), dateFormatOptions) })}> <span title={intl.formatMessage(messages.linkVerifiedOn, { date: intl.formatDate(proof.get('updated_at'), dateFormatOptions) })}>
<Icon id='check' className='verified__mark' /> <Icon id='check' className='verified__mark' />
</span> </span>
</a> </a>
<a href={proof.get('profile_url')} target='_blank' rel='noopener'> <a href={proof.get('profile_url')} target='_blank' rel='noopener noreferrer'>
<span <span
className={_s.dangerousContent} className={_s.dangerousContent}
dangerouslySetInnerHTML={{ __html: ' ' + proof.get('provider_username') }} dangerouslySetInnerHTML={{ __html: ' ' + proof.get('provider_username') }}

View File

@ -0,0 +1,63 @@
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { shortNumberFormat } from '../../utils/numbers'
import PopoverLayout from './popover_layout'
import Avatar from '../avatar'
import Button from '../button'
import DisplayName from '../display_name'
import Text from '../text'
export default class RemoteUserInfoPopover extends ImmutablePureComponent {
static propTypes = {
account: ImmutablePropTypes.map.isRequired,
}
render() {
const { account } = this.props
const content = !account ? null : { __html: account.get('note_emojified') }
return (
<PopoverLayout>
<div className={[_s.default, _s.px15, _s.py15].join(' ')} style={{width: '260px'}}>
<div className={[_s.default, _s.flexRow].join(' ')}>
<div className={[_s.default, _s.flexGrow1].join(' ')}>
<Avatar account={account} size={42} />
<DisplayName account={account} multiline noHover />
</div>
<div className={[_s.default, _s.marginLeftAuto].join(' ')}>
<Button
color='white'
>
<Text size='medium' weight='bold' color='inherit'>
Following
</Text>
</Button>
</div>
</div>
<div className={[_s.default, _s.my10].join(' ')}>
<div className={_s.dangerousContent} dangerouslySetInnerHTML={content} />
</div>
<div className={[_s.default, _s.flexRow].join(' ')}>
<div>
<Text size='small'>
{shortNumberFormat(account.get('following_count'))}
Following
</Text>
</div>
<div>
<Text size='small'>
{shortNumberFormat(account.get('followers_count'))}
Followers
</Text>
</div>
</div>
</div>
</PopoverLayout>
)
}
}

View File

@ -68,7 +68,7 @@ class StatusOptionsPopover extends ImmutablePureComponent {
menu.push({ menu.push({
icon: 'circle', icon: 'circle',
hideArrow: true, hideArrow: true,
title: formatMessage(messages.open), title: intl.formatMessage(messages.open),
onClick: this.handleOpen onClick: this.handleOpen
}); });
@ -76,13 +76,13 @@ class StatusOptionsPopover extends ImmutablePureComponent {
menu.push({ menu.push({
icon: 'circle', icon: 'circle',
hideArrow: true, hideArrow: true,
title: formatMessage(messages.copy), title: intl.formatMessage(messages.copy),
onClick: this.handleCopy, onClick: this.handleCopy,
}) })
menu.push({ menu.push({
icon: 'circle', icon: 'circle',
hideArrow: true, hideArrow: true,
title: formatMessage(messages.embed), title: intl.formatMessage(messages.embed),
onClick: this.handleEmbed, onClick: this.handleEmbed,
}) })
} }
@ -93,7 +93,7 @@ class StatusOptionsPopover extends ImmutablePureComponent {
menu.push({ menu.push({
icon: 'circle', icon: 'circle',
hideArrow: true, hideArrow: true,
title: formatMessage(mutingConversation ? messages.unmuteConversation : messages.muteConversation), title: intl.formatMessage(mutingConversation ? messages.unmuteConversation : messages.muteConversation),
onClick: this.handleConversationMuteClick, onClick: this.handleConversationMuteClick,
}) })
} }
@ -103,33 +103,33 @@ class StatusOptionsPopover extends ImmutablePureComponent {
menu.push({ menu.push({
icon: 'circle', icon: 'circle',
hideArrow: true, hideArrow: true,
title: formatMessage(status.get('pinned') ? messages.unpin : messages.pin), title: intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin),
onClick: this.handlePinClick, onClick: this.handlePinClick,
}) })
} else { } else {
if (status.get('visibility') === 'private') { if (status.get('visibility') === 'private') {
menu.push({ menu.push({
title: formatMessage(status.get('reblogged') ? messages.cancel_repost_private : messages.repost_private), title: intl.formatMessage(status.get('reblogged') ? messages.cancel_repost_private : messages.repost_private),
onClick: this.handleRepostClick onClick: this.handleRepostClick
}) })
} }
} }
menu.push({ text: formatMessage(messages.delete), action: this.handleDeleteClick }); menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick });
menu.push({ text: formatMessage(messages.edit), action: this.handleEditClick }); menu.push({ text: intl.formatMessage(messages.edit), action: this.handleEditClick });
} else { } else {
menu.push({ text: formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick }); menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick });
menu.push({ text: formatMessage(messages.mute, { name: status.getIn(['account', 'username']) }), action: this.handleMuteClick }); menu.push({ text: intl.formatMessage(messages.mute, { name: status.getIn(['account', 'username']) }), action: this.handleMuteClick });
menu.push({ text: formatMessage(messages.block, { name: status.getIn(['account', 'username']) }), action: this.handleBlockClick }); menu.push({ text: intl.formatMessage(messages.block, { name: status.getIn(['account', 'username']) }), action: this.handleBlockClick });
menu.push({ text: formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport }); menu.push({ text: intl.formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport });
if (isStaff) { if (isStaff) {
menu.push({ text: formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }), href: `/admin/accounts/${status.getIn(['account', 'id'])}` }); menu.push({ text: intl.formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }), href: `/admin/accounts/${status.getIn(['account', 'id'])}` });
menu.push({ text: formatMessage(messages.admin_status), href: `/admin/accounts/${status.getIn(['account', 'id'])}/statuses/${status.get('id')}` }); menu.push({ text: intl.formatMessage(messages.admin_status), href: `/admin/accounts/${status.getIn(['account', 'id'])}/statuses/${status.get('id')}` });
} }
if (withGroupAdmin) { if (withGroupAdmin) {
menu.push({ text: formatMessage(messages.group_remove_account), action: this.handleGroupRemoveAccount }); menu.push({ text: intl.formatMessage(messages.group_remove_account), action: this.handleGroupRemoveAccount });
menu.push({ text: formatMessage(messages.group_remove_post), action: this.handleGroupRemovePost }); menu.push({ text: intl.formatMessage(messages.group_remove_post), action: this.handleGroupRemovePost });
} }
} }

View File

@ -142,7 +142,7 @@ class Sidebar extends ImmutablePureComponent {
{ {
title: 'Home', title: 'Home',
icon: 'home', icon: 'home',
to: '/', to: '/home',
count: homeItemsQueueCount, count: homeItemsQueueCount,
}, },
{ {

View File

@ -4,7 +4,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component' import ImmutablePureComponent from 'react-immutable-pure-component'
import { me } from '../initial_state' import { me } from '../initial_state'
import { makeGetAccount } from '../selectors' import { makeGetAccount } from '../selectors'
import GabLogo from '../assets/gab_logo' import Icon from './icon'
import SidebarSectionItem from './sidebar_section_item' import SidebarSectionItem from './sidebar_section_item'
import Text from './text' import Text from './text'
@ -27,14 +27,20 @@ class SidebarHeader extends ImmutablePureComponent {
render() { render() {
const { account } = this.props const { account } = this.props
const isPro = account.get('is_pro')
const gabLogoClasses = isPro ? _s.fillColorGabPro : _s.fillColorBrand
return ( return (
<Fragment> <Fragment>
<h1 className={[_s.default].join(' ')}> <h1 className={[_s.default].join(' ')}>
<NavLink to='/' aria-label='Gab' className={[_s.default, _s.flexRow, _s.noSelect, _s.noUnderline, _s.height50PX, _s.cursorPointer, _s.px10].join(' ')}> <NavLink to='/' aria-label='Gab' className={[_s.default, _s.flexRow, _s.noSelect, _s.noUnderline, _s.height50PX, _s.cursorPointer, _s.px10].join(' ')}>
<GabLogo /> <Icon id='gab-logo' className={gabLogoClasses} />
<Text weight='bold' color='brand' size='extraSmall' className={[_s.pb5].join(' ')}> {
BETA isPro &&
<Text weight='bold' color='pro' size='extraSmall' className={[_s.pb5].join(' ')}>
PRO
</Text> </Text>
}
</NavLink> </NavLink>
</h1> </h1>

View File

@ -5,6 +5,10 @@ import Icon from './icon'
const cx = classNames.bind(_s) const cx = classNames.bind(_s)
export default class SidebarSectionItem extends PureComponent { export default class SidebarSectionItem extends PureComponent {
static contextTypes = {
router: PropTypes.object,
}
static propTypes = { static propTypes = {
to: PropTypes.string, to: PropTypes.string,
href: PropTypes.string, href: PropTypes.string,
@ -46,7 +50,8 @@ export default class SidebarSectionItem extends PureComponent {
const { hovering } = this.state const { hovering } = this.state
const iconSize = '16px' const iconSize = '16px'
const shouldShowActive = hovering || active const currentPathname = this.context.router.route.location.pathname
const shouldShowActive = hovering || active || currentPathname === to || currentPathname === href
const isNotifications = to === '/notifications' const isNotifications = to === '/notifications'
const containerClasses = cx({ const containerClasses = cx({
@ -58,10 +63,12 @@ export default class SidebarSectionItem extends PureComponent {
px10: 1, px10: 1,
alignItemsCenter: 1, alignItemsCenter: 1,
radiusSmall: 1, radiusSmall: 1,
// border1PX: shouldShowActive, mt2: 1,
// borderColorSecondary: shouldShowActive, border1PX: 1,
backgroundSubtle2: shouldShowActive, borderColorTransparent: !shouldShowActive,
backgroundTransparent: 1, borderColorSecondary: shouldShowActive,
backgroundTransparent: !shouldShowActive,
backgroundColorPrimary: shouldShowActive,
}) })
const textClasses = cx({ const textClasses = cx({
@ -70,13 +77,13 @@ export default class SidebarSectionItem extends PureComponent {
fontSize15PX: 1, fontSize15PX: 1,
text: 1, text: 1,
textOverflowEllipsis: 1, textOverflowEllipsis: 1,
colorSecondary: !hovering && !active && !me && !shouldShowActive,
colorPrimary: shouldShowActive || me, colorPrimary: shouldShowActive || me,
colorSecondary: !hovering && !active && !me,
}) })
const iconClasses = cx({ const iconClasses = cx({
fillColorSecondary: !hovering && !active && !shouldShowActive,
fillColorBlack: shouldShowActive, fillColorBlack: shouldShowActive,
fillColorSecondary: !hovering && !active,
}) })
const countClasses = cx({ const countClasses = cx({

View File

@ -44,7 +44,7 @@ export default class StatusActionBarItem extends PureComponent {
}) })
return ( return (
<div className={[_s.default, _s.flexGrow1, _s.px10].join(' ')}> <div className={[_s.default, _s.flexGrow1, _s.px5].join(' ')}>
<button <button
ref={buttonRef} ref={buttonRef}
className={btnClasses} className={btnClasses}

View File

@ -144,7 +144,7 @@ export default class Card extends ImmutablePureComponent {
className={[_s.default, _s.displayFlex, _s.text, _s.noUnderline, _s.overflowWrapBreakWord, _s.colorPrimary, _s.fontSize15PX, _s.fontWeightMedium].join(' ')} className={[_s.default, _s.displayFlex, _s.text, _s.noUnderline, _s.overflowWrapBreakWord, _s.colorPrimary, _s.fontSize15PX, _s.fontWeightMedium].join(' ')}
href={card.get('url')} href={card.get('url')}
title={card.get('title')} title={card.get('title')}
rel='noopener' rel='noopener noreferrer'
target='_blank' target='_blank'
> >
{card.get('title')} {card.get('title')}
@ -228,7 +228,7 @@ export default class Card extends ImmutablePureComponent {
<a <a
href={card.get('url')} href={card.get('url')}
className={[_s.default, _s.cursorPointer, _s.flexRow, _s.overflowHidden, _s.noUnderline, _s.width100PC, _s.backgroundSubtle_onHover, _s.borderColorSecondary2, _s.border1PX, _s.radiusSmall].join(' ')} className={[_s.default, _s.cursorPointer, _s.flexRow, _s.overflowHidden, _s.noUnderline, _s.width100PC, _s.backgroundSubtle_onHover, _s.borderColorSecondary2, _s.border1PX, _s.radiusSmall].join(' ')}
rel='noopener' rel='noopener noreferrer'
ref={this.setRef} ref={this.setRef}
> >
{embed} {embed}

View File

@ -49,14 +49,15 @@ class StatusContent extends ImmutablePureComponent {
const links = node.querySelectorAll('a') const links = node.querySelectorAll('a')
for (var i = 0; i < links.length; ++i) { for (let i = 0; i < links.length; ++i) {
let link = links[i] const link = links[i]
if (link.classList.contains('status-link')) { if (link.classList.contains('linked')) {
continue continue
} }
link.classList.add('status-link') link.classList.add('linked')
link.classList.add(_s.text, _s.colorBrand, _s.cursorPointer, _s.inherit)
let mention = this.props.status.get('mentions').find(item => link.href === `${item.get('url')}`) const mention = this.props.status.get('mentions').find(item => link.href === `${item.get('url')}`)
if (mention) { if (mention) {
link.addEventListener('click', this.onMentionClick.bind(this, mention), false) link.addEventListener('click', this.onMentionClick.bind(this, mention), false)
@ -66,6 +67,20 @@ class StatusContent extends ImmutablePureComponent {
} else { } else {
link.setAttribute('title', link.href) link.setAttribute('title', link.href)
} }
const descendents = link.getElementsByTagName('*')
for (let j = 0; j < descendents.length; j++) {
const descendent = descendents[j];
if (descendent.classList.contains('invisible')) {
descendent.classList.remove('invisible')
descendent.classList.add(_s.fontSize0, _s.text, _s.inherit)
}
if (descendent.classList.contains('ellipsis')) {
descendent.classList.remove('ellipsis')
descendent.classList.add(_s.noSelect, _s.text, _s.inherit)
}
}
} }
if ( if (
@ -89,8 +104,8 @@ class StatusContent extends ImmutablePureComponent {
onMentionClick = (mention, e) => { onMentionClick = (mention, e) => {
if (this.context.router && e.button === 0 && !(e.ctrlKey || e.metaKey)) { if (this.context.router && e.button === 0 && !(e.ctrlKey || e.metaKey)) {
e.preventDefault() // e.preventDefault()
this.context.router.history.push(`/${mention.get('acct')}`) // this.context.router.history.push(`/${mention.get('acct')}`)
} }
} }

View File

@ -9,6 +9,7 @@ const COLORS = {
error: 'error', error: 'error',
white: 'white', white: 'white',
inherit: 'inherit', inherit: 'inherit',
pro: 'pro',
} }
const SIZES = { const SIZES = {
@ -73,6 +74,7 @@ export default class Text extends PureComponent {
colorSecondary: color === COLORS.secondary, colorSecondary: color === COLORS.secondary,
colorBrand: color === COLORS.brand, colorBrand: color === COLORS.brand,
colorWhite: color === COLORS.white, colorWhite: color === COLORS.white,
colorGabPro: color === COLORS.pro,
inherit: color === COLORS.inherit, inherit: color === COLORS.inherit,
fontSize19PX: size === SIZES.large, fontSize19PX: size === SIZES.large,

View File

@ -1,14 +1,12 @@
import ImmutablePureComponent from 'react-immutable-pure-component' import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes' import ImmutablePropTypes from 'react-immutable-proptypes'
import { injectIntl, defineMessages } from 'react-intl' import { injectIntl, defineMessages } from 'react-intl'
import classNames from 'classnames/bind'
import { me } from '../initial_state' import { me } from '../initial_state'
import ComposeFormContainer from '../features/compose/containers/compose_form_container' import ComposeFormContainer from '../features/compose/containers/compose_form_container'
import Avatar from './avatar'
import Block from './block' import Block from './block'
import Heading from './heading' import Heading from './heading'
const cx = classNames.bind(_s)
const messages = defineMessages({ const messages = defineMessages({
createPost: { id: 'column_header.create_post', defaultMessage: 'Create Post' }, createPost: { id: 'column_header.create_post', defaultMessage: 'Create Post' },
}) })
@ -57,12 +55,15 @@ class TimelineComposeBlock extends ImmutablePureComponent {
return ( return (
<section className={[_s.default, _s.mb15].join(' ')}> <section className={[_s.default, _s.mb15].join(' ')}>
<Block> <Block>
<div className={[_s.default, _s.backgroundSubtle, _s.borderBottom1PX, _s.borderColorSecondary, _s.px15, _s.py2].join(' ')}> <div className={[_s.default, _s.backgroundSubtle, _s.borderBottom1PX, _s.borderColorSecondary, _s.px15, _s.py2, _s.alignItemsCenter, _s.flexRow].join(' ')}>
<div className={_s.mr10}>
<Avatar account={account} size='20' />
</div>
<Heading size='h5'> <Heading size='h5'>
{intl.formatMessage(messages.createPost)} {intl.formatMessage(messages.createPost)}
</Heading> </Heading>
</div> </div>
<div className={[_s.default, _s.flexRow, _s.px15, _s.py15].join(' ')}> <div className={[_s.default, _s.flexRow, _s.px15, _s.pt15, _s.pb10].join(' ')}>
<ComposeFormContainer {...rest} /> <ComposeFormContainer {...rest} />
</div> </div>
</Block> </Block>

View File

@ -210,7 +210,7 @@ class Video extends PureComponent {
const y = 1 - ((e.clientY - rect.top) / this.volHeight) const y = 1 - ((e.clientY - rect.top) / this.volHeight)
if (!isNaN(y)) { if (!isNaN(y)) {
var slideamt = y const slideamt = y
if (y > 1) { if (y > 1) {
slideamt = 1 slideamt = 1
} else if (y < 0) { } else if (y < 0) {

View File

@ -5,7 +5,6 @@ import ImmutablePropTypes from 'react-immutable-proptypes'
import classNames from 'classnames/bind' import classNames from 'classnames/bind'
import CharacterCounter from '../../../../components/character_counter' import CharacterCounter from '../../../../components/character_counter'
import UploadForm from '../upload_form' import UploadForm from '../upload_form'
import ReplyIndicatorContainer from '../../containers/reply_indicator_container'
import AutosuggestTextbox from '../../../../components/autosuggest_textbox' import AutosuggestTextbox from '../../../../components/autosuggest_textbox'
import PollButton from '../../components/poll_button' import PollButton from '../../components/poll_button'
import UploadButton from '../media_upload_button' import UploadButton from '../media_upload_button'
@ -255,12 +254,6 @@ class ComposeForm extends ImmutablePureComponent {
mt10: !shouldCondense, mt10: !shouldCondense,
}) })
const avatarContainerClasses = cx({
default: 1,
mr10: 1,
mt5: shouldCondense,
})
const contentWarningClasses = cx({ const contentWarningClasses = cx({
default: 1, default: 1,
pt5: 1, pt5: 1,
@ -271,13 +264,14 @@ class ComposeForm extends ImmutablePureComponent {
displayNone: !spoiler displayNone: !spoiler
}) })
const avatarSize = shouldCondense ? 28 : 46
return ( return (
<div className={[_s.default, _s.flexRow, _s.width100PC].join(' ')}> <div className={[_s.default, _s.flexRow, _s.width100PC].join(' ')}>
<div className={avatarContainerClasses}> {
<Avatar account={account} size={avatarSize} /> shouldCondense &&
<div className={[_s.default, _s.mr10, _s.mt5]}>
<Avatar account={account} size='28' />
</div> </div>
}
<div <div
className={containerClasses} className={containerClasses}
@ -285,8 +279,6 @@ class ComposeForm extends ImmutablePureComponent {
onClick={this.handleClick} onClick={this.handleClick}
> >
{ /* !shouldCondense && <ReplyIndicatorContainer /> */}
<div className={contentWarningClasses}> <div className={contentWarningClasses}>
<AutosuggestTextbox <AutosuggestTextbox
placeholder={intl.formatMessage(messages.spoiler_placeholder)} placeholder={intl.formatMessage(messages.spoiler_placeholder)}
@ -301,7 +293,6 @@ class ComposeForm extends ImmutablePureComponent {
onSuggestionSelected={this.onSpoilerSuggestionSelected} onSuggestionSelected={this.onSpoilerSuggestionSelected}
searchTokens={[':']} searchTokens={[':']}
id='cw-spoiler-input' id='cw-spoiler-input'
className='spoiler-input__input'
/> />
</div> </div>

View File

@ -1 +0,0 @@
export { default } from './reply_indicator'

View File

@ -1,64 +0,0 @@
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { defineMessages, injectIntl } from 'react-intl';
import { NavLink } from 'react-router-dom';
import Avatar from '../../../../components/avatar';
import Button from '../../../../components/button';
import DisplayName from '../../../../components/display_name';
import { isRtl } from '../../../../utils/rtl';
const messages = defineMessages({
cancel: { id: 'reply_indicator.cancel', defaultMessage: 'Cancel' },
});
export default
@injectIntl
class ReplyIndicator extends ImmutablePureComponent {
static contextTypes = {
router: PropTypes.object,
};
static propTypes = {
status: ImmutablePropTypes.map,
onCancel: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
};
handleClick = () => {
this.props.onCancel();
}
render () {
const { status, intl } = this.props;
if (!status) {
return null;
}
const content = { __html: status.get('contentHtml') };
const style = {
direction: isRtl(status.get('search_index')) ? 'rtl' : 'ltr',
};
return (
<div className='reply-indicator'>
<div className='reply-indicator__header'>
<div className='reply-indicator__cancel'>
<Button title={intl.formatMessage(messages.cancel)} icon='times' onClick={this.handleClick} inverted />
</div>
<NavLink to={`/${status.getIn(['account', 'acct'])}`} className='reply-indicator__display-name'>
<div className='reply-indicator__display-avatar'>
<Avatar account={status.get('account')} size={24} />
</div>
<DisplayName account={status.get('account')} />
</NavLink>
</div>
<div className='reply-indicator-content' style={style} dangerouslySetInnerHTML={content} />
</div>
);
}
}

View File

@ -1,137 +0,0 @@
.reply-indicator {
border-radius: 4px;
margin-bottom: 10px;
background: $gab-background-base-light;
padding: 10px;
min-height: 23px;
overflow-y: auto;
flex: 0 2 auto;
max-height: 500px;
@media screen and (min-width: 320px) and (max-width: 375px) {
max-height: 220px;
}
@media screen and (max-width: 320px) {
max-height: 130px;
}
&__header {
margin-bottom: 5px;
overflow: hidden;
}
&__cancel {
float: right;
line-height: 24px;
}
&__display-name {
color: $inverted-text-color;
display: block;
max-width: 100%;
line-height: 24px;
overflow: hidden;
padding-right: 25px;
text-decoration: none;
&:hover strong {
text-decoration: underline;
}
}
&__display-avatar {
float: left;
margin-right: 5px;
}
}
.reply-indicator-content {
position: relative;
padding-top: 2px;
color: $primary-text-color;
@include text-sizing(15px, 400, 20px);
@include text-overflow(normal, break-word);
&:focus {
outline: 0;
}
&.status__content--with-spoiler {
white-space: normal;
.status__content__text {
white-space: pre-wrap;
}
}
.emojione {
margin: -3px 0 0;
@include size(20px);
}
p {
margin-bottom: 20px;
white-space: pre-wrap;
&:last-child {
margin-bottom: 2px;
}
}
a {
color: $gab-brand-default;
text-decoration: none;
&:hover {
text-decoration: underline;
.fa {
color: lighten($dark-text-color, 7%);
}
}
&.mention {
&:hover {
text-decoration: none;
span {
text-decoration: underline;
}
}
}
.fa {
color: $dark-text-color;
}
}
.status__content__spoiler-link {
background: $action-button-color;
&:hover {
background: lighten($action-button-color, 7%);
text-decoration: none;
}
&::-moz-focus-inner {
border: 0;
}
&::-moz-focus-inner,
&:focus,
&:active {
outline: 0 !important;
}
}
.status__content__text {
display: none;
&.status__content__text--visible {
display: block;
}
}
}

View File

@ -1,23 +0,0 @@
import { cancelReplyCompose } from '../../../actions/compose';
import { makeGetStatus } from '../../../selectors';
import ReplyIndicator from '../components/reply_indicator';
const makeMapStateToProps = () => {
const getStatus = makeGetStatus();
const mapStateToProps = state => ({
status: getStatus(state, { id: state.getIn(['compose', 'in_reply_to']) }),
});
return mapStateToProps;
};
const mapDispatchToProps = dispatch => ({
onCancel () {
dispatch(cancelReplyCompose());
},
});
export default connect(makeMapStateToProps, mapDispatchToProps)(ReplyIndicator);

View File

@ -16,7 +16,7 @@ const regexSupplant = function(regex, flags) {
regex = regex.source; regex = regex.source;
} }
return new RegExp(regex.replace(/#\{(\w+)\}/g, function(match, name) { return new RegExp(regex.replace(/#\{(\w+)\}/g, function(match, name) {
var newRegex = regexen[name] || ''; let newRegex = regexen[name] || '';
if (typeof newRegex !== 'string') { if (typeof newRegex !== 'string') {
newRegex = newRegex.source; newRegex = newRegex.source;
} }
@ -185,7 +185,7 @@ export const urlRegex = (function() {
regexen.validUrlQueryEndingChars = /[a-z0-9_&=#\/]/i; regexen.validUrlQueryEndingChars = /[a-z0-9_&=#\/]/i;
regexen.validUrl = regexSupplant( regexen.validUrl = regexSupplant(
'(' + // $1 URL '(' + // $1 URL
'(https?:\\/\\/)' + // $2 Protocol '(https?:\\/\\/)?' + // $2 Protocol
'(#{validDomain})' + // $3 Domain(s) '(#{validDomain})' + // $3 Domain(s)
'(?::(#{validPortNumber}))?' + // $4 Port number (optional) '(?::(#{validPortNumber}))?' + // $4 Port number (optional)
'(\\/#{validUrlPath}*)?' + // $5 URL Path '(\\/#{validUrlPath}*)?' + // $5 URL Path

View File

@ -36,6 +36,7 @@ import {
COMPOSE_POLL_OPTION_REMOVE, COMPOSE_POLL_OPTION_REMOVE,
COMPOSE_POLL_SETTINGS_CHANGE, COMPOSE_POLL_SETTINGS_CHANGE,
COMPOSE_SCHEDULED_AT_CHANGE, COMPOSE_SCHEDULED_AT_CHANGE,
COMPOSE_RICH_TEXT_EDITOR_CONTROLS_VISIBILITY
} from '../actions/compose'; } from '../actions/compose';
import { TIMELINE_DELETE } from '../actions/timelines'; import { TIMELINE_DELETE } from '../actions/timelines';
import { STORE_HYDRATE } from '../actions/store'; import { STORE_HYDRATE } from '../actions/store';
@ -73,6 +74,7 @@ const initialState = ImmutableMap({
idempotencyKey: null, idempotencyKey: null,
tagHistory: ImmutableList(), tagHistory: ImmutableList(),
scheduled_at: null, scheduled_at: null,
rte_controls_visible: false,
}); });
const initialPoll = ImmutableMap({ const initialPoll = ImmutableMap({
@ -386,6 +388,8 @@ export default function compose(state = initialState, action) {
return state.update('poll', poll => poll.set('expires_in', action.expiresIn).set('multiple', action.isMultiple)); return state.update('poll', poll => poll.set('expires_in', action.expiresIn).set('multiple', action.isMultiple));
case COMPOSE_SCHEDULED_AT_CHANGE: case COMPOSE_SCHEDULED_AT_CHANGE:
return state.set('scheduled_at', action.date); return state.set('scheduled_at', action.date);
case COMPOSE_RICH_TEXT_EDITOR_CONTROLS_VISIBILITY:
return ''
default: default:
return state; return state;
} }

View File

@ -368,6 +368,10 @@ body {
color: #21cf7a color: #21cf7a
} }
.colorGabPro {
color: #ffd700;
}
.fillColorBlack { .fillColorBlack {
fill: #000; fill: #000;
} }
@ -388,6 +392,10 @@ body {
fill: #666; fill: #666;
} }
.fillColorGabPro {
fill: #ffd700;
}
.bottom0 { .bottom0 {
bottom: 0; bottom: 0;
} }
@ -716,6 +724,10 @@ body {
font-size: 12px; font-size: 12px;
} }
.fontSize0 {
font-size: 0;
}
.fontWeightNormal { .fontWeightNormal {
font-weight: 400; font-weight: 400;
} }
@ -858,6 +870,10 @@ body {
padding-top: 25%; padding-top: 25%;
} }
.pt15 {
padding-top: 15px;
}
.pt10 { .pt10 {
padding-top: 10px; padding-top: 10px;
} }

View File

@ -43,6 +43,8 @@ class Formatter
def reformat(html) def reformat(html)
sanitize(html, Sanitize::Config::GABSOCIAL_STRICT) sanitize(html, Sanitize::Config::GABSOCIAL_STRICT)
rescue ArgumentError
''
end end
def plaintext(status) def plaintext(status)
@ -246,7 +248,7 @@ class Formatter
def link_to_url(entity, options = {}) def link_to_url(entity, options = {})
url = Addressable::URI.parse(entity[:url]) url = Addressable::URI.parse(entity[:url])
html_attrs = { target: '_blank', rel: 'nofollow noopener' } html_attrs = { target: '_blank', rel: 'nofollow noopener noreferrer' }
html_attrs[:rel] = "me #{html_attrs[:rel]}" if options[:me] html_attrs[:rel] = "me #{html_attrs[:rel]}" if options[:me]
@ -284,14 +286,14 @@ class Formatter
suffix = url[prefix.length + 30..-1] suffix = url[prefix.length + 30..-1]
cutoff = url[prefix.length..-1].length > 30 cutoff = url[prefix.length..-1].length > 30
"<span class=\"invisible\">#{encode(prefix)}</span><span class=\"#{cutoff ? 'ellipsis' : ''}\">#{encode(text)}</span><span class=\"invisible\">#{encode(suffix)}</span>" "<span aria-hidden=\"true\" class=\"invisible\">#{encode(prefix)}</span>#{encode(text)}<span aria-hidden=\"true\" class=\"invisible\">#{encode(suffix)}</span>" + (cutoff ? "<span aria-hidden=\"true\" class=\"ellipsis\">…</span>" : "")
end end
def hashtag_html(tag) def hashtag_html(tag)
"<a href=\"#{encode(tag_url(tag.downcase))}\" class=\"mention hashtag\" rel=\"tag\">#<span>#{encode(tag)}</span></a>" "<a data-focusable=\"true\" role=\"link\" href=\"#{encode(tag_url(tag.downcase))}\" class=\"mention hashtag\" rel=\"tag\">##{encode(tag)}</a>"
end end
def mention_html(account) def mention_html(account)
"<span class=\"h-card\"><a href=\"#{encode(TagManager.instance.url_for(account))}\" class=\"u-url mention\">@<span>#{encode(account.username)}</span></a></span>" "<a data-focusable=\"true\" role=\"link\" href=\"#{encode(TagManager.instance.url_for(account))}\" class=\"u-url mention\">@#{encode(account.acct)}</a>"
end end
end end

View File

@ -29,7 +29,7 @@ class Sanitize
add_attributes: { add_attributes: {
'a' => { 'a' => {
'rel' => 'nofollow noopener', 'rel' => 'nofollow noopener noreferrer',
'target' => '_blank', 'target' => '_blank',
}, },
}, },

View File

@ -123,7 +123,7 @@
<published>2016-10-10T00:41:31Z</published> <published>2016-10-10T00:41:31Z</published>
<updated>2016-10-10T00:41:31Z</updated> <updated>2016-10-10T00:41:31Z</updated>
<title>Social media needs MOAR cats! http://kickass.zone/media/3</title> <title>Social media needs MOAR cats! http://kickass.zone/media/3</title>
<content type="html">&lt;p&gt;Social media needs MOAR cats! &lt;a rel="nofollow noopener" href="http://kickass.zone/media/3"&gt;http://kickass.zone/media/3&lt;/a&gt;&lt;/p&gt;</content> <content type="html">&lt;p&gt;Social media needs MOAR cats! &lt;a rel="nofollow noopener noreferrer" href="http://kickass.zone/media/3"&gt;http://kickass.zone/media/3&lt;/a&gt;&lt;/p&gt;</content>
<activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb> <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
<link rel="self" type="application/atom+xml" href="http://kickass.zone/users/localhost/updates/9.atom"/> <link rel="self" type="application/atom+xml" href="http://kickass.zone/users/localhost/updates/9.atom"/>
<link rel="alternate" type="text/html" href="http://kickass.zone/users/localhost/updates/9"/> <link rel="alternate" type="text/html" href="http://kickass.zone/users/localhost/updates/9"/>
@ -135,7 +135,7 @@
<published>2016-10-10T00:38:39Z</published> <published>2016-10-10T00:38:39Z</published>
<updated>2016-10-10T00:38:39Z</updated> <updated>2016-10-10T00:38:39Z</updated>
<title>http://kickass.zone/media/2</title> <title>http://kickass.zone/media/2</title>
<content type="html">&lt;p&gt;&lt;a rel="nofollow noopener" href="http://kickass.zone/media/2"&gt;http://kickass.zone/media/2&lt;/a&gt;&lt;/p&gt;</content> <content type="html">&lt;p&gt;&lt;a rel="nofollow noopener noreferrer" href="http://kickass.zone/media/2"&gt;http://kickass.zone/media/2&lt;/a&gt;&lt;/p&gt;</content>
<activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb> <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
<link rel="self" type="application/atom+xml" href="http://kickass.zone/users/localhost/updates/8.atom"/> <link rel="self" type="application/atom+xml" href="http://kickass.zone/users/localhost/updates/8.atom"/>
<link rel="alternate" type="text/html" href="http://kickass.zone/users/localhost/updates/8"/> <link rel="alternate" type="text/html" href="http://kickass.zone/users/localhost/updates/8"/>

View File

@ -87,7 +87,7 @@ RSpec.describe FetchLinkCardService, type: :service do
end end
context 'in a remote status' do context 'in a remote status' do
let(:status) { Fabricate(:status, account: Fabricate(:account, domain: 'example.com'), text: 'Habt ihr ein paar gute Links zu #<span class="tag"><a href="https://quitter.se/tag/wannacry" target="_blank" rel="tag noopener" title="https://quitter.se/tag/wannacry">Wannacry</a></span> herumfliegen? Ich will mal unter <br> <a href="https://github.com/qbi/WannaCry" target="_blank" rel="noopener" title="https://github.com/qbi/WannaCry">https://github.com/qbi/WannaCry</a> was sammeln. !<a href="http://sn.jonkman.ca/group/416/id" target="_blank" rel="noopener" title="http://sn.jonkman.ca/group/416/id">security</a>&nbsp;') } let(:status) { Fabricate(:status, account: Fabricate(:account, domain: 'example.com'), text: 'Habt ihr ein paar gute Links zu #<span class="tag"><a href="https://quitter.se/tag/wannacry" target="_blank" rel="tag noopener noreferrer" title="https://quitter.se/tag/wannacry">Wannacry</a></span> herumfliegen? Ich will mal unter <br> <a href="https://github.com/qbi/WannaCry" target="_blank" rel="noopener noreferrer" title="https://github.com/qbi/WannaCry">https://github.com/qbi/WannaCry</a> was sammeln. !<a href="http://sn.jonkman.ca/group/416/id" target="_blank" rel="noopener noreferrer" title="http://sn.jonkman.ca/group/416/id">security</a>&nbsp;') }
it 'parses out URLs' do it 'parses out URLs' do
expect(a_request(:head, 'https://github.com/qbi/WannaCry')).to have_been_made.at_least_once expect(a_request(:head, 'https://github.com/qbi/WannaCry')).to have_been_made.at_least_once

View File

@ -28,12 +28,12 @@ RSpec.describe VerifyLinkService, type: :service do
end end
end end
context 'when a link contains an <a rel="noopener"> back' do context 'when a link contains an <a rel="noopener noreferrer"> back' do
let(:html) do let(:html) do
<<-HTML <<-HTML
<!doctype html> <!doctype html>
<body> <body>
<a href="#{ActivityPub::TagManager.instance.url_for(account)}" rel="noopener me" target="_blank">Follow me on Gab Social</a> <a href="#{ActivityPub::TagManager.instance.url_for(account)}" rel="noopener noreferrer me" target="_blank">Follow me on Gab Social</a>
</body> </body>
HTML HTML
end end

134
yarn.lock
View File

@ -3020,7 +3020,7 @@ debug@=3.1.0:
dependencies: dependencies:
ms "2.0.0" ms "2.0.0"
debug@^3.0.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6: debug@^3.0.0, debug@^3.1.1, debug@^3.2.5:
version "3.2.6" version "3.2.6"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
@ -3061,11 +3061,6 @@ deep-extend@^0.5.1:
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.5.1.tgz#b894a9dd90d3023fbf1c55a394fb858eb2066f1f" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.5.1.tgz#b894a9dd90d3023fbf1c55a394fb858eb2066f1f"
integrity sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w== integrity sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==
deep-extend@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
deep-is@~0.1.3: deep-is@~0.1.3:
version "0.1.3" version "0.1.3"
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
@ -3154,11 +3149,6 @@ detect-file@^1.0.0:
resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7"
integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc= integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=
detect-libc@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=
detect-newline@^2.1.0: detect-newline@^2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2"
@ -4231,13 +4221,6 @@ fs-extra@^8.0.1:
jsonfile "^4.0.0" jsonfile "^4.0.0"
universalify "^0.1.0" universalify "^0.1.0"
fs-minipass@^1.2.5:
version "1.2.7"
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7"
integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==
dependencies:
minipass "^2.6.0"
fs-write-stream-atomic@^1.0.8: fs-write-stream-atomic@^1.0.8:
version "1.0.10" version "1.0.10"
resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9"
@ -4781,7 +4764,7 @@ https-browserify@^1.0.0:
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=
iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13: iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@~0.4.13:
version "0.4.24" version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
@ -4810,13 +4793,6 @@ iferr@^0.1.5:
resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501"
integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE=
ignore-walk@^3.0.1:
version "3.0.3"
resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37"
integrity sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==
dependencies:
minimatch "^3.0.4"
ignore@^4.0.6: ignore@^4.0.6:
version "4.0.6" version "4.0.6"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
@ -4928,7 +4904,7 @@ inherits@2.0.3:
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
ini@^1.3.4, ini@^1.3.5, ini@~1.3.0: ini@^1.3.4, ini@^1.3.5:
version "1.3.5" version "1.3.5"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
@ -6447,21 +6423,6 @@ minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5:
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0:
version "2.9.0"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6"
integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==
dependencies:
safe-buffer "^5.1.2"
yallist "^3.0.0"
minizlib@^1.2.1:
version "1.3.3"
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d"
integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==
dependencies:
minipass "^2.9.0"
mississippi@^3.0.0: mississippi@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022" resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022"
@ -6598,15 +6559,6 @@ nearley@^2.7.10:
randexp "0.4.6" randexp "0.4.6"
semver "^5.4.1" semver "^5.4.1"
needle@^2.2.1:
version "2.3.3"
resolved "https://registry.yarnpkg.com/needle/-/needle-2.3.3.tgz#a041ad1d04a871b0ebb666f40baaf1fb47867117"
integrity sha512-EkY0GeSq87rWp1hoq/sH/wnTWgFVhYlnIkbJ0YJFfRgEFlz2RraCjBpFQ+vrEgEdp0ThfyHADmkChEhcb7PKyw==
dependencies:
debug "^3.2.6"
iconv-lite "^0.4.4"
sax "^1.2.4"
negotiator@0.6.2: negotiator@0.6.2:
version "0.6.2" version "0.6.2"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
@ -6708,22 +6660,6 @@ node-notifier@^5.4.2:
shellwords "^0.1.1" shellwords "^0.1.1"
which "^1.3.0" which "^1.3.0"
node-pre-gyp@*:
version "0.14.0"
resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.14.0.tgz#9a0596533b877289bcad4e143982ca3d904ddc83"
integrity sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA==
dependencies:
detect-libc "^1.0.2"
mkdirp "^0.5.1"
needle "^2.2.1"
nopt "^4.0.1"
npm-packlist "^1.1.6"
npmlog "^4.0.2"
rc "^1.2.7"
rimraf "^2.6.1"
semver "^5.3.0"
tar "^4.4.2"
node-releases@^1.1.52: node-releases@^1.1.52:
version "1.1.52" version "1.1.52"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.52.tgz#bcffee3e0a758e92e44ecfaecd0a47554b0bcba9" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.52.tgz#bcffee3e0a758e92e44ecfaecd0a47554b0bcba9"
@ -6761,14 +6697,6 @@ node-sass@^4.13.1:
dependencies: dependencies:
abbrev "1" abbrev "1"
nopt@^4.0.1:
version "4.0.3"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48"
integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==
dependencies:
abbrev "1"
osenv "^0.1.4"
normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: normalize-package-data@^2.3.2, normalize-package-data@^2.3.4:
version "2.5.0" version "2.5.0"
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
@ -6811,27 +6739,6 @@ normalize-url@^3.0.0:
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559"
integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==
npm-bundled@^1.0.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.1.tgz#1edd570865a94cdb1bc8220775e29466c9fb234b"
integrity sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==
dependencies:
npm-normalize-package-bin "^1.0.1"
npm-normalize-package-bin@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2"
integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==
npm-packlist@^1.1.6:
version "1.4.8"
resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e"
integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==
dependencies:
ignore-walk "^3.0.1"
npm-bundled "^1.0.1"
npm-normalize-package-bin "^1.0.1"
npm-run-path@^2.0.0: npm-run-path@^2.0.0:
version "2.0.2" version "2.0.2"
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
@ -6839,7 +6746,7 @@ npm-run-path@^2.0.0:
dependencies: dependencies:
path-key "^2.0.0" path-key "^2.0.0"
"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.0.2, npmlog@^4.1.2: "npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.1.2:
version "4.1.2" version "4.1.2"
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==
@ -7081,7 +6988,7 @@ os-tmpdir@^1.0.0, os-tmpdir@~1.0.2:
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
osenv@0, osenv@^0.1.4: osenv@0:
version "0.1.5" version "0.1.5"
resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410"
integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==
@ -8152,16 +8059,6 @@ raw-body@2.4.0:
iconv-lite "0.4.24" iconv-lite "0.4.24"
unpipe "1.0.0" unpipe "1.0.0"
rc@^1.2.7:
version "1.2.8"
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==
dependencies:
deep-extend "^0.6.0"
ini "~1.3.0"
minimist "^1.2.0"
strip-json-comments "~2.0.1"
react-contenteditable@^3.3.3: react-contenteditable@^3.3.3:
version "3.3.3" version "3.3.3"
resolved "https://registry.yarnpkg.com/react-contenteditable/-/react-contenteditable-3.3.3.tgz#73ea271a0e84d54516e6075c318d4372e5b95c70" resolved "https://registry.yarnpkg.com/react-contenteditable/-/react-contenteditable-3.3.3.tgz#73ea271a0e84d54516e6075c318d4372e5b95c70"
@ -8866,7 +8763,7 @@ rgba-regex@^1.0.0:
resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3"
integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=
rimraf@2, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.3: rimraf@2, rimraf@^2.5.4, rimraf@^2.6.3:
version "2.7.1" version "2.7.1"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
@ -9071,7 +8968,7 @@ selfsigned@^1.10.7:
dependencies: dependencies:
node-forge "0.9.0" node-forge "0.9.0"
"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: "semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1:
version "5.7.1" version "5.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
@ -9651,7 +9548,7 @@ strip-indent@^1.0.1:
dependencies: dependencies:
get-stdin "^4.0.1" get-stdin "^4.0.1"
strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: strip-json-comments@^2.0.1:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
@ -9766,19 +9663,6 @@ tar@^2.0.0:
fstream "^1.0.12" fstream "^1.0.12"
inherits "2" inherits "2"
tar@^4.4.2:
version "4.4.13"
resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525"
integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==
dependencies:
chownr "^1.1.1"
fs-minipass "^1.2.5"
minipass "^2.8.6"
minizlib "^1.2.1"
mkdirp "^0.5.0"
safe-buffer "^5.1.2"
yallist "^3.0.3"
tcomb@^2.5.0: tcomb@^2.5.0:
version "2.7.0" version "2.7.0"
resolved "https://registry.yarnpkg.com/tcomb/-/tcomb-2.7.0.tgz#10d62958041669a5d53567b9a4ee8cde22b1c2b0" resolved "https://registry.yarnpkg.com/tcomb/-/tcomb-2.7.0.tgz#10d62958041669a5d53567b9a4ee8cde22b1c2b0"
@ -10629,7 +10513,7 @@ yallist@^2.1.2:
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=
yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3: yallist@^3.0.2:
version "3.1.1" version "3.1.1"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==