From 7fc627e192538cb5b3fb3421882f5a76357edc91 Mon Sep 17 00:00:00 2001
From: KokaKiwi <kokakiwi@kokakiwi.net>
Date: Sun, 17 Feb 2019 23:53:51 +0100
Subject: [PATCH] Add status content type dropdown to compose box.

---
 .../flavours/glitch/actions/compose.js        |  9 ++++
 .../glitch/features/composer/index.js         | 11 ++++
 .../glitch/features/composer/options/index.js | 51 +++++++++++++++++++
 .../flavours/glitch/reducers/compose.js       |  6 +++
 4 files changed, 77 insertions(+)

diff --git a/app/javascript/flavours/glitch/actions/compose.js b/app/javascript/flavours/glitch/actions/compose.js
index 5c8b54d2a..e50dbba79 100644
--- a/app/javascript/flavours/glitch/actions/compose.js
+++ b/app/javascript/flavours/glitch/actions/compose.js
@@ -42,6 +42,7 @@ export const COMPOSE_SPOILERNESS_CHANGE = 'COMPOSE_SPOILERNESS_CHANGE';
 export const COMPOSE_SPOILER_TEXT_CHANGE = 'COMPOSE_SPOILER_TEXT_CHANGE';
 export const COMPOSE_VISIBILITY_CHANGE  = 'COMPOSE_VISIBILITY_CHANGE';
 export const COMPOSE_LISTABILITY_CHANGE = 'COMPOSE_LISTABILITY_CHANGE';
+export const COMPOSE_CONTENT_TYPE_CHANGE = 'COMPOSE_CONTENT_TYPE_CHANGE';
 
 export const COMPOSE_EMOJI_INSERT = 'COMPOSE_EMOJI_INSERT';
 
@@ -125,6 +126,7 @@ export function submitCompose(routerHistory) {
     }
     api(getState).post('/api/v1/statuses', {
       status,
+      content_type: getState().getIn(['compose', 'content_type']),
       in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null),
       media_ids: media.map(item => item.get('id')),
       sensitive: getState().getIn(['compose', 'sensitive']) || (spoilerText.length > 0 && media.size !== 0),
@@ -469,6 +471,13 @@ export function changeComposeVisibility(value) {
   };
 };
 
+export function changeComposeContentType(value) {
+  return {
+    type: COMPOSE_CONTENT_TYPE_CHANGE,
+    value,
+  };
+};
+
 export function insertEmojiCompose(position, emoji) {
   return {
     type: COMPOSE_EMOJI_INSERT,
diff --git a/app/javascript/flavours/glitch/features/composer/index.js b/app/javascript/flavours/glitch/features/composer/index.js
index ec0e405a4..d81ef385c 100644
--- a/app/javascript/flavours/glitch/features/composer/index.js
+++ b/app/javascript/flavours/glitch/features/composer/index.js
@@ -15,6 +15,7 @@ import {
   changeComposeSpoilerText,
   changeComposeSpoilerness,
   changeComposeVisibility,
+  changeComposeContentType,
   changeUploadCompose,
   clearComposeSuggestions,
   fetchComposeSuggestions,
@@ -88,6 +89,7 @@ function mapStateToProps (state) {
     media: state.getIn(['compose', 'media_attachments']),
     preselectDate: state.getIn(['compose', 'preselectDate']),
     privacy: state.getIn(['compose', 'privacy']),
+    contentType: state.getIn(['compose', 'content_type']),
     progress: state.getIn(['compose', 'progress']),
     inReplyTo: inReplyTo ? state.getIn(['statuses', inReplyTo]) : null,
     replyAccount: inReplyTo ? state.getIn(['statuses', inReplyTo, 'account']) : null,
@@ -116,6 +118,9 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
   onChangeAdvancedOption(option, value) {
     dispatch(changeComposeAdvancedOption(option, value));
   },
+  onChangeContentType(value) {
+    dispatch(changeComposeContentType(value));
+  },
   onChangeDescription(id, description) {
     dispatch(changeUploadCompose(id, { description }));
   },
@@ -388,6 +393,7 @@ class Composer extends React.Component {
       advancedOptions,
       amUnlocked,
       anyMedia,
+      contentType,
       intl,
       isSubmitting,
       isChangingUpload,
@@ -396,6 +402,7 @@ class Composer extends React.Component {
       media,
       onCancelReply,
       onChangeAdvancedOption,
+      onChangeContentType,
       onChangeDescription,
       onChangeSensitivity,
       onChangeSpoilerness,
@@ -478,6 +485,7 @@ class Composer extends React.Component {
         <ComposerOptions
           acceptContentTypes={acceptContentTypes}
           advancedOptions={advancedOptions}
+          contentType={contentType}
           disabled={isSubmitting}
           full={media ? media.size >= 4 || media.some(
             item => item.get('type') === 'video'
@@ -485,6 +493,7 @@ class Composer extends React.Component {
           hasMedia={media && !!media.size}
           intl={intl}
           onChangeAdvancedOption={onChangeAdvancedOption}
+          onChangeContentType={onChangeContentType}
           onChangeSensitivity={onChangeSensitivity}
           onChangeVisibility={onChangeVisibility}
           onDoodleOpen={onOpenDoodleModal}
@@ -529,6 +538,7 @@ Composer.propTypes = {
   media: ImmutablePropTypes.list,
   preselectDate: PropTypes.instanceOf(Date),
   privacy: PropTypes.string,
+  contentType: PropTypes.string,
   progress: PropTypes.number,
   inReplyTo: ImmutablePropTypes.map,
   resetFileKey: PropTypes.number,
@@ -548,6 +558,7 @@ Composer.propTypes = {
   //  Dispatch props.
   onCancelReply: PropTypes.func,
   onChangeAdvancedOption: PropTypes.func,
+  onChangeContentType: PropTypes.func,
   onChangeDescription: PropTypes.func,
   onChangeSensitivity: PropTypes.func,
   onChangeSpoilerText: PropTypes.func,
diff --git a/app/javascript/flavours/glitch/features/composer/options/index.js b/app/javascript/flavours/glitch/features/composer/options/index.js
index 9fe3abc03..8a11b12dc 100644
--- a/app/javascript/flavours/glitch/features/composer/options/index.js
+++ b/app/javascript/flavours/glitch/features/composer/options/index.js
@@ -34,6 +34,10 @@ const messages = defineMessages({
     defaultMessage: 'Adjust status privacy',
     id: 'privacy.change',
   },
+  content_type: {
+    defaultMessage: 'Content type',
+    id: 'content-type.change',
+  },
   direct_long: {
     defaultMessage: 'Post to mentioned users only',
     id: 'privacy.direct.long',
@@ -46,6 +50,10 @@ const messages = defineMessages({
     defaultMessage: 'Draw something',
     id: 'compose.attach.doodle',
   },
+  html: {
+    defaultMessage: 'HTML',
+    id: 'compose.content-type.html',
+  },
   local_only_long: {
     defaultMessage: 'Do not post to other instances',
     id: 'advanced_options.local-only.long',
@@ -54,6 +62,14 @@ const messages = defineMessages({
     defaultMessage: 'Local-only',
     id: 'advanced_options.local-only.short',
   },
+  markdown: {
+    defaultMessage: 'Markdown',
+    id: 'compose.content-type.markdown',
+  },
+  plain: {
+    defaultMessage: 'Plain text',
+    id: 'compose.content-type.plain',
+  },
   private_long: {
     defaultMessage: 'Post to followers only',
     id: 'privacy.private.long',
@@ -159,6 +175,7 @@ export default class ComposerOptions extends React.PureComponent {
     const {
       acceptContentTypes,
       advancedOptions,
+      contentType,
       disabled,
       full,
       hasMedia,
@@ -166,6 +183,7 @@ export default class ComposerOptions extends React.PureComponent {
       onChangeAdvancedOption,
       onChangeSensitivity,
       onChangeVisibility,
+      onChangeContentType,
       onModalClose,
       onModalOpen,
       onToggleSpoiler,
@@ -204,6 +222,24 @@ export default class ComposerOptions extends React.PureComponent {
       },
     };
 
+    const contentTypeItems = {
+      plain: {
+        icon: 'file',
+        name: 'text/plain',
+        text: <FormattedMessage {...messages.plain} />,
+      },
+      html: {
+        icon: 'file-text',
+        name: 'text/html',
+        text: <FormattedMessage {...messages.html} />,
+      },
+      markdown: {
+        icon: 'file-text',
+        name: 'text/markdown',
+        text: <FormattedMessage {...messages.markdown} />,
+      },
+    };
+
     //  The result.
     return (
       <div className='composer--options'>
@@ -285,6 +321,19 @@ export default class ComposerOptions extends React.PureComponent {
           title={intl.formatMessage(messages.change_privacy)}
           value={privacy}
         />
+        <Dropdown
+          icon="code"
+          items={[
+            contentTypeItems.plain,
+            contentTypeItems.html,
+            contentTypeItems.markdown,
+          ]}
+          onChange={onChangeContentType}
+          onModalClose={onModalClose}
+          onModalOpen={onModalOpen}
+          title={intl.formatMessage(messages.content_type)}
+          value={contentType}
+        />
         {onToggleSpoiler && (
           <TextIconButton
             active={spoiler}
@@ -327,6 +376,7 @@ export default class ComposerOptions extends React.PureComponent {
 ComposerOptions.propTypes = {
   acceptContentTypes: PropTypes.string,
   advancedOptions: ImmutablePropTypes.map,
+  contentType: PropTypes.string,
   disabled: PropTypes.bool,
   full: PropTypes.bool,
   hasMedia: PropTypes.bool,
@@ -334,6 +384,7 @@ ComposerOptions.propTypes = {
   onChangeAdvancedOption: PropTypes.func,
   onChangeSensitivity: PropTypes.func,
   onChangeVisibility: PropTypes.func,
+  onChangeContentType: PropTypes.func,
   onDoodleOpen: PropTypes.func,
   onModalClose: PropTypes.func,
   onModalOpen: PropTypes.func,
diff --git a/app/javascript/flavours/glitch/reducers/compose.js b/app/javascript/flavours/glitch/reducers/compose.js
index 7281cbd61..cfa6ac47a 100644
--- a/app/javascript/flavours/glitch/reducers/compose.js
+++ b/app/javascript/flavours/glitch/reducers/compose.js
@@ -25,6 +25,7 @@ import {
   COMPOSE_SPOILERNESS_CHANGE,
   COMPOSE_SPOILER_TEXT_CHANGE,
   COMPOSE_VISIBILITY_CHANGE,
+  COMPOSE_CONTENT_TYPE_CHANGE,
   COMPOSE_EMOJI_INSERT,
   COMPOSE_UPLOAD_CHANGE_REQUEST,
   COMPOSE_UPLOAD_CHANGE_SUCCESS,
@@ -60,6 +61,7 @@ const initialState = ImmutableMap({
   spoiler: false,
   spoiler_text: '',
   privacy: null,
+  content_type: 'text/plain',
   text: '',
   focusDate: null,
   caretPosition: null,
@@ -294,6 +296,10 @@ export default function compose(state = initialState, action) {
     return state
       .set('privacy', action.value)
       .set('idempotencyKey', uuid());
+  case COMPOSE_CONTENT_TYPE_CHANGE:
+    return state
+      .set('content_type', action.value)
+      .set('idempotencyKey', uuid());
   case COMPOSE_CHANGE:
     return state
       .set('text', action.text)
-- 
GitLab