Commit 1a33759e authored by mgabdev's avatar mgabdev

Progress

parent bfda5978
......@@ -3,6 +3,7 @@ import { CancelToken, isCancel } from 'axios';
import { throttle } from 'lodash';
import moment from 'moment';
import { search as emojiSearch } from '../components/emoji/emoji_mart_search_light';
import { urlRegex } from '../features/compose/util/url_regex'
import { tagHistory } from '../settings';
import { useEmoji } from './emojis';
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_RICH_TEXT_EDITOR_CONTROLS_VISIBILITY = 'COMPOSE_RICH_TEXT_EDITOR_CONTROLS_VISIBILITY'
const messages = defineMessages({
uploadErrorLimit: { id: 'upload_error.limit', defaultMessage: 'File upload limit exceeded.' },
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) {
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']);
if ((!status || !status.length) && media.size === 0) {
return;
}
// : hack :
//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(closeModal());
......@@ -191,6 +202,7 @@ export function submitCompose(routerHistory, group) {
api(getState)[method](endpoint, {
status,
statusMarkdown,
scheduled_at,
in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null),
quote_of_id: getState().getIn(['compose', 'quote_of_id'], null),
......@@ -593,4 +605,10 @@ export function changeScheduledAt(date) {
type: COMPOSE_SCHEDULED_AT_CHANGE,
date,
};
};
\ No newline at end of file
};
export function changeRichTextEditorControlsVisibility() {
return {
type: COMPOSE_RICH_TEXT_EDITOR_CONTROLS_VISIBILITY,
}
}
\ No newline at end of file
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
\ No newline at end of file
const GabLogo = ({
className = _s.fillColorBrand,
width = '50px',
height = '30px',
viewBox = '0 0 50 30'
}) => (
<svg
className={className}
version='1.1'
xmlns='http://www.w3.org/2000/svg'
x='0px'
......@@ -14,14 +16,14 @@ const GabLogo = ({
xmlSpace='preserve'
>
<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-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'/>
<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
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
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'/>
......
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
\ No newline at end of file
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
\ No newline at end of file
......@@ -253,10 +253,21 @@ export default class AutosuggestTextbox extends ImmutablePureComponent {
<div className={[_s.default, _s.flexGrow1].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}
inputRef={this.setTextbox}
disabled={disabled}
......@@ -270,8 +281,7 @@ export default class AutosuggestTextbox extends ImmutablePureComponent {
onBlur={this.onBlur}
onPaste={this.onPaste}
aria-autocomplete='list'
/>
/> */ }
{ /*
<Textarea
......
import {
Editor,
EditorState,
CompositeDecorator,
RichUtils
} 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 {
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 = {
editorState: EditorState.createEmpty(),
editorState: EditorState.createEmpty(compositeDecorator),
}
onChange = (editorState) => {
this.setState({ editorState })
}
onBoldClick() {
this.onChange(RichUtils.toggleInlineStyle(this.state.editorState, 'BOLD'));
onToggleInlineStyle = (style) => {
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() {
const {
inputRef,
disabled,
placeholder,
autoFocus,
// value,
onChange,
onKeyDown,
onKeyUp,
onFocus,
onBlur,
onPaste
} = this.props
const { editorState } = this.state
return (
<div>
<div className={[_s.default].join(' ')}>
{/*<button onClick={this.onBoldClick.bind(this)}>Bold</button>*/}
<Editor
editorState={this.state.editorState}
onChange={this.onChange}
/>
<div className={[_s.default, _s.backgroundColorPrimary, _s.borderBottom1PX, _s.borderColorSecondary, _s.py5, _s.px15, _s.alignItemsCenter, _s.flexRow].join(' ')}>
{
RTE_ITEMS.map((item, i) => (
<StyleButton
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>
)
}
}
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
/>
)
}
}
\ No newline at end of file
......@@ -105,6 +105,11 @@ class DisplayName extends ImmutablePureComponent {
!!large ? '19px' :
!!small ? '14px' : '16px'
const domain = account.get('acct').split('@')[1];
const isRemoteUser = !!domain
console.log("domain, isRemoteUser:", domain, isRemoteUser)
// : todo :
return (
<span {...containerOptions} ref={this.setRef}>
......@@ -117,20 +122,20 @@ class DisplayName extends ImmutablePureComponent {
</bdi>
{
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') &&
<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') &&
<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') &&
<Icon id='verified' width='16px' height='16px' className={_s.default} title='Gab Investor' />
*/ }
<Icon id='investor' width='16px' height='16px' className={_s.default} />
}
</div>
{
!noUsername &&
......
......@@ -10,15 +10,18 @@ import CircleIcon from '../assets/circle_icon'
import CloseIcon from '../assets/close_icon'
import CommentIcon from '../assets/comment_icon'
import DissenterIcon from '../assets/dissenter_icon'
import DonorIcon from '../assets/donor_icon'
import EllipsisIcon from '../assets/ellipsis_icon'
import ErrorIcon from '../assets/error_icon'
import FullscreenIcon from '../assets/fullscreen_icon'
import GabLogoIcon from '../assets/gab_logo'
import GifIcon from '../assets/gif_icon'
import GlobeIcon from '../assets/globe_icon'
import GroupIcon from '../assets/group_icon'
import GroupAddIcon from '../assets/group_add_icon'
import HappyIcon from '../assets/happy_icon'
import HomeIcon from '../assets/home_icon'
import InvestorIcon from '../assets/investor_icon'
import LikeIcon from '../assets/like_icon'
import LinkIcon from '../assets/link_icon'
import ListIcon from '../assets/list_icon'
......@@ -33,6 +36,7 @@ import PauseIcon from '../assets/pause_icon'
import PinIcon from '../assets/pin_icon'
import PlayIcon from '../assets/play_icon'
import PollIcon from '../assets/poll_icon'
import ProIcon from '../assets/pro_icon'
import RepostIcon from '../assets/repost_icon'
import RichTextIcon from '../assets/rich_text_icon'
import SearchIcon from '../assets/search_icon'
......@@ -56,15 +60,18 @@ const ICONS = {
'close': CloseIcon,
'comment': CommentIcon,
'dissenter': DissenterIcon,
'donor': DonorIcon,
'ellipsis': EllipsisIcon,