about summary refs log tree commit diff
path: root/app/javascript/flavours
diff options
context:
space:
mode:
authorFire Demon <firedemon@creature.cafe>2020-07-19 18:50:24 -0500
committerFire Demon <firedemon@creature.cafe>2020-08-30 05:43:59 -0500
commit21438b54bdaf3c557ec9ebbc482a2c418d8c64f8 (patch)
treee577d047af196823227e675dea52b2fc2fa842c6 /app/javascript/flavours
parent8c8ad0ac0ed0d3e67f3e521068b59edd4054f1e9 (diff)
[Feature] Add manual publishing option
Diffstat (limited to 'app/javascript/flavours')
-rw-r--r--app/javascript/flavours/glitch/actions/statuses.js43
-rw-r--r--app/javascript/flavours/glitch/components/status.js2
-rw-r--r--app/javascript/flavours/glitch/components/status_action_bar.js10
-rw-r--r--app/javascript/flavours/glitch/components/status_content.js17
-rw-r--r--app/javascript/flavours/glitch/containers/status_container.js12
-rw-r--r--app/javascript/flavours/glitch/features/status/components/action_bar.js10
-rw-r--r--app/javascript/flavours/glitch/features/status/components/detailed_status.js2
-rw-r--r--app/javascript/flavours/glitch/features/status/containers/detailed_status_container.js11
-rw-r--r--app/javascript/flavours/glitch/features/status/index.js15
-rw-r--r--app/javascript/flavours/glitch/reducers/statuses.js3
-rw-r--r--app/javascript/flavours/glitch/styles/monsterfork/components/status.scss23
11 files changed, 140 insertions, 8 deletions
diff --git a/app/javascript/flavours/glitch/actions/statuses.js b/app/javascript/flavours/glitch/actions/statuses.js
index 72e8f14d8..9b33ed09f 100644
--- a/app/javascript/flavours/glitch/actions/statuses.js
+++ b/app/javascript/flavours/glitch/actions/statuses.js
@@ -12,6 +12,10 @@ export const STATUS_DELETE_REQUEST = 'STATUS_DELETE_REQUEST';
 export const STATUS_DELETE_SUCCESS = 'STATUS_DELETE_SUCCESS';
 export const STATUS_DELETE_FAIL    = 'STATUS_DELETE_FAIL';
 
+export const STATUS_PUBLISH_REQUEST = 'STATUS_PUBLISH_REQUEST';
+export const STATUS_PUBLISH_SUCCESS = 'STATUS_PUBLISH_SUCCESS';
+export const STATUS_PUBLISH_FAIL    = 'STATUS_PUBLISH_FAIL';
+
 export const CONTEXT_FETCH_REQUEST = 'CONTEXT_FETCH_REQUEST';
 export const CONTEXT_FETCH_SUCCESS = 'CONTEXT_FETCH_SUCCESS';
 export const CONTEXT_FETCH_FAIL    = 'CONTEXT_FETCH_FAIL';
@@ -34,9 +38,9 @@ export function fetchStatusRequest(id, skipLoading) {
   };
 };
 
-export function fetchStatus(id) {
+export function fetchStatus(id, skipLoading = null) {
   return (dispatch, getState) => {
-    const skipLoading = getState().getIn(['statuses', id], null) !== null;
+    skipLoading = skipLoading === null ? getState().getIn(['statuses', id], null) !== null : skipLoading;
 
     dispatch(fetchContext(id));
 
@@ -73,6 +77,41 @@ export function editStatus(status, routerHistory) {
   };
 };
 
+export function publishStatus(id) {
+  return (dispatch, getState) => {
+    dispatch(publishStatusRequest(id));
+
+    api(getState).post(`/api/v1/statuses/${id}/publish`).then(() => {
+      dispatch(publishStatusSuccess(id));
+      dispatch(fetchStatus(id, false));
+    }).catch(error => {
+      dispatch(publishStatusFail(id, error));
+    });
+  };
+};
+
+export function publishStatusRequest(id) {
+  return {
+    type: STATUS_PUBLISH_REQUEST,
+    id: id,
+  };
+};
+
+export function publishStatusSuccess(id) {
+  return {
+    type: STATUS_PUBLISH_SUCCESS,
+    id: id,
+  };
+};
+
+export function publishStatusFail(id, error) {
+  return {
+    type: STATUS_PUBLISH_FAIL,
+    id: id,
+    error: error,
+  };
+};
+
 export function fetchStatusSuccess(skipLoading) {
   return {
     type: STATUS_FETCH_SUCCESS,
diff --git a/app/javascript/flavours/glitch/components/status.js b/app/javascript/flavours/glitch/components/status.js
index 3a6029b96..4626d1cd8 100644
--- a/app/javascript/flavours/glitch/components/status.js
+++ b/app/javascript/flavours/glitch/components/status.js
@@ -74,6 +74,7 @@ class Status extends ImmutablePureComponent {
     onBookmark: PropTypes.func,
     onDelete: PropTypes.func,
     onEdit: PropTypes.func,
+    onPublish: PropTypes.func,
     onDirect: PropTypes.func,
     onMention: PropTypes.func,
     onPin: PropTypes.func,
@@ -695,6 +696,7 @@ class Status extends ImmutablePureComponent {
 
     const computedClass = classNames('status', `status-${status.get('visibility')}`, {
       collapsed: isCollapsed,
+      unpublished: status.get('published') === false,
       'has-background': isCollapsed && background,
       'status__wrapper-reply': !!status.get('in_reply_to_id'),
       read: unread === false,
diff --git a/app/javascript/flavours/glitch/components/status_action_bar.js b/app/javascript/flavours/glitch/components/status_action_bar.js
index 6902103c5..e941fb994 100644
--- a/app/javascript/flavours/glitch/components/status_action_bar.js
+++ b/app/javascript/flavours/glitch/components/status_action_bar.js
@@ -13,6 +13,7 @@ const messages = defineMessages({
   delete: { id: 'status.delete', defaultMessage: 'Delete' },
   redraft: { id: 'status.redraft', defaultMessage: 'Delete & re-draft' },
   edit: { id: 'status.edit', defaultMessage: 'Edit' },
+  publish: { id: 'status.publish', defaultMessage: 'Publish' },
   direct: { id: 'status.direct', defaultMessage: 'Direct message @{name}' },
   mention: { id: 'status.mention', defaultMessage: 'Mention @{name}' },
   mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' },
@@ -63,6 +64,7 @@ class StatusActionBar extends ImmutablePureComponent {
     onReblog: PropTypes.func,
     onDelete: PropTypes.func,
     onEdit: PropTypes.func,
+    onPublish: PropTypes.func,
     onDirect: PropTypes.func,
     onMention: PropTypes.func,
     onMute: PropTypes.func,
@@ -139,6 +141,10 @@ class StatusActionBar extends ImmutablePureComponent {
     this.props.onEdit(this.props.status, this.context.router.history);
   }
 
+  handlePublishClick = () => {
+    this.props.onPublish(this.props.status);
+  }
+
   handlePinClick = () => {
     this.props.onPin(this.props.status);
   }
@@ -238,6 +244,10 @@ class StatusActionBar extends ImmutablePureComponent {
       menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick });
       menu.push({ text: intl.formatMessage(messages.redraft), action: this.handleRedraftClick });
       menu.push({ text: intl.formatMessage(messages.edit), action: this.handleEditClick });
+
+      if (status.get('published') === false) {
+        menu.push({ text: intl.formatMessage(messages.publish), action: this.handlePublishClick });
+      }
     } else {
       menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick });
       menu.push({ text: intl.formatMessage(messages.direct, { name: status.getIn(['account', 'username']) }), action: this.handleDirectClick });
diff --git a/app/javascript/flavours/glitch/components/status_content.js b/app/javascript/flavours/glitch/components/status_content.js
index b353b028b..171aff097 100644
--- a/app/javascript/flavours/glitch/components/status_content.js
+++ b/app/javascript/flavours/glitch/components/status_content.js
@@ -278,6 +278,7 @@ export default class StatusContent extends React.PureComponent {
 
     const edited = (status.get('edited') === 0) ? null : (
       <div className='status__edit-notice'>
+        <Icon id='pencil-square-o' />
         <FormattedMessage
           id='status.edited'
           defaultMessage='{count, plural, one {# edit} other {# edits}} · last update: {updated_at}'
@@ -290,6 +291,17 @@ export default class StatusContent extends React.PureComponent {
       </div>
     );
 
+    const unpublished = (status.get('published') === false) && (
+      <div className='status__unpublished-notice'>
+        <Icon id='chain-broken' />
+        <FormattedMessage
+          id='status.unpublished'
+          defaultMessage='Unpublished'
+          key={`unpublished-${status.get('id')}`}
+        />
+      </div>
+    );
+
     const content = { __html: status.get('contentHtml') };
     const spoilerContent = { __html: status.get('spoilerHtml') };
     const directionStyle = { direction: 'ltr' };
@@ -345,6 +357,8 @@ export default class StatusContent extends React.PureComponent {
 
       return (
         <div className={classNames} tabIndex='0' onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp} ref={this.setRef}>
+          {unpublished}
+          {edited}
           <p
             style={{ marginBottom: hidden && status.get('mentions').isEmpty() ? '0px' : null }}
           >
@@ -355,7 +369,6 @@ export default class StatusContent extends React.PureComponent {
             </button>
           </p>
 
-          {edited}
           {mentionsPlaceholder}
 
           <div className={`status__content__spoiler ${!hidden ? 'status__content__spoiler--visible' : ''}`}>
@@ -382,6 +395,7 @@ export default class StatusContent extends React.PureComponent {
           tabIndex='0'
           ref={this.setRef}
         >
+          {unpublished}
           {edited}
           <div
             ref={this.setContentsRef}
@@ -401,6 +415,7 @@ export default class StatusContent extends React.PureComponent {
           tabIndex='0'
           ref={this.setRef}
         >
+          {unpublished}
           {edited}
           <div ref={this.setContentsRef} key={`contents-${tagLinks}`} className='status__content__text' dangerouslySetInnerHTML={content} tabIndex='0' />
           {media}
diff --git a/app/javascript/flavours/glitch/containers/status_container.js b/app/javascript/flavours/glitch/containers/status_container.js
index 9e011ac6b..bccaba92d 100644
--- a/app/javascript/flavours/glitch/containers/status_container.js
+++ b/app/javascript/flavours/glitch/containers/status_container.js
@@ -17,7 +17,7 @@ import {
   pin,
   unpin,
 } from 'flavours/glitch/actions/interactions';
-import { muteStatus, unmuteStatus, deleteStatus, editStatus } from 'flavours/glitch/actions/statuses';
+import { muteStatus, unmuteStatus, deleteStatus, editStatus, publishStatus } from 'flavours/glitch/actions/statuses';
 import { initMuteModal } from 'flavours/glitch/actions/mutes';
 import { initBlockModal } from 'flavours/glitch/actions/blocks';
 import { initReport } from 'flavours/glitch/actions/reports';
@@ -38,6 +38,8 @@ const messages = defineMessages({
   redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? You will lose all replies, boosts and favourites to it.' },
   replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
   replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
+  publishConfirm: { id: 'confirmations.publish.confirm', defaultMessage: 'Publish' },
+  publishMessage: { id: 'confirmations.publish.message', defaultMessage: 'Are you ready to publish your post?' },
   unfilterConfirm: { id: 'confirmations.unfilter.confirm', defaultMessage: 'Show' },
   author: { id: 'confirmations.unfilter.author', defaultMessage: 'Author' },
   matchingFilters: { id: 'confirmations.unfilter.filters', defaultMessage: 'Matching {count, plural, one {filter} other {filters}}' },
@@ -170,6 +172,14 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({
     dispatch(editStatus(status, history));
   },
 
+  onPublish (status) {
+    dispatch(openModal('CONFIRM', {
+      message: intl.formatMessage(messages.publishMessage),
+      confirm: intl.formatMessage(messages.publishConfirm),
+      onConfirm: () => dispatch(publishStatus(status.get('id'))),
+    }));
+  },
+
   onDirect (account, router) {
     dispatch(directCompose(account, router));
   },
diff --git a/app/javascript/flavours/glitch/features/status/components/action_bar.js b/app/javascript/flavours/glitch/features/status/components/action_bar.js
index c4f510184..e76d9634b 100644
--- a/app/javascript/flavours/glitch/features/status/components/action_bar.js
+++ b/app/javascript/flavours/glitch/features/status/components/action_bar.js
@@ -11,6 +11,7 @@ const messages = defineMessages({
   delete: { id: 'status.delete', defaultMessage: 'Delete' },
   redraft: { id: 'status.redraft', defaultMessage: 'Delete & re-draft' },
   edit: { id: 'status.edit', defaultMessage: 'Edit' },
+  publish: { id: 'status.publish', defaultMessage: 'Publish' },
   direct: { id: 'status.direct', defaultMessage: 'Direct message @{name}' },
   mention: { id: 'status.mention', defaultMessage: 'Mention @{name}' },
   reply: { id: 'status.reply', defaultMessage: 'Reply' },
@@ -52,6 +53,7 @@ class ActionBar extends React.PureComponent {
     onBlock: PropTypes.func,
     onDelete: PropTypes.func.isRequired,
     onEdit: PropTypes.func.isRequired,
+    onPublish: PropTypes.func.isRequired,
     onDirect: PropTypes.func.isRequired,
     onMention: PropTypes.func.isRequired,
     onReport: PropTypes.func,
@@ -88,6 +90,10 @@ class ActionBar extends React.PureComponent {
     this.props.onEdit(this.props.status, this.context.router.history);
   }
 
+  handlePublishClick = () => {
+    this.props.onPublish(this.props.status);
+  }
+
   handleDirectClick = () => {
     this.props.onDirect(this.props.status.get('account'), this.context.router.history);
   }
@@ -171,6 +177,10 @@ class ActionBar extends React.PureComponent {
       menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick });
       menu.push({ text: intl.formatMessage(messages.redraft), action: this.handleRedraftClick });
       menu.push({ text: intl.formatMessage(messages.edit), action: this.handleEditClick });
+
+      if (status.get('published') === false) {
+        menu.push({ text: intl.formatMessage(messages.publish), action: this.handlePublishClick });
+      }
     } else {
       menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick });
       menu.push({ text: intl.formatMessage(messages.direct, { name: status.getIn(['account', 'username']) }), action: this.handleDirectClick });
diff --git a/app/javascript/flavours/glitch/features/status/components/detailed_status.js b/app/javascript/flavours/glitch/features/status/components/detailed_status.js
index 24e614a8d..0db0316cc 100644
--- a/app/javascript/flavours/glitch/features/status/components/detailed_status.js
+++ b/app/javascript/flavours/glitch/features/status/components/detailed_status.js
@@ -259,7 +259,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
 
     return (
       <div style={outerStyle}>
-        <div ref={this.setRef} className={classNames('detailed-status', `detailed-status-${status.get('visibility')}`, { compact })} {...selectorAttribs}>
+        <div ref={this.setRef} className={classNames('detailed-status', `detailed-status-${status.get('visibility')}`, { compact, unpublished: status.get('published') === false })} {...selectorAttribs}>
           <a href={status.getIn(['account', 'url'])} onClick={this.handleAccountClick} className='detailed-status__display-name'>
             <div className='detailed-status__display-avatar'><Avatar account={status.get('account')} size={48} /></div>
             <DisplayName account={status.get('account')} localDomain={this.props.domain} />
diff --git a/app/javascript/flavours/glitch/features/status/containers/detailed_status_container.js b/app/javascript/flavours/glitch/features/status/containers/detailed_status_container.js
index eeafc0b08..124de903a 100644
--- a/app/javascript/flavours/glitch/features/status/containers/detailed_status_container.js
+++ b/app/javascript/flavours/glitch/features/status/containers/detailed_status_container.js
@@ -19,6 +19,7 @@ import {
   unmuteStatus,
   editStatus,
   deleteStatus,
+  publishStatus,
   hideStatus,
   revealStatus,
 } from 'flavours/glitch/actions/statuses';
@@ -35,6 +36,8 @@ const messages = defineMessages({
   deleteMessage: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this status?' },
   redraftConfirm: { id: 'confirmations.redraft.confirm', defaultMessage: 'Delete & redraft' },
   redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.' },
+  publishConfirm: { id: 'confirmations.publish.confirm', defaultMessage: 'Publish' },
+  publishMessage: { id: 'confirmations.publish.message', defaultMessage: 'Are you ready to publish your post?' },
   replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
   replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
 });
@@ -123,6 +126,14 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
     dispatch(editStatus(status, history));
   },
 
+  onPublish (status) {
+    dispatch(openModal('CONFIRM', {
+      message: intl.formatMessage(messages.publishMessage),
+      confirm: intl.formatMessage(messages.publishConfirm),
+      onConfirm: () => dispatch(publishStatus(status.get('id'))),
+    }));
+  },
+
   onDirect (account, router) {
     dispatch(directCompose(account, router));
   },
diff --git a/app/javascript/flavours/glitch/features/status/index.js b/app/javascript/flavours/glitch/features/status/index.js
index beea64341..3a6847e8d 100644
--- a/app/javascript/flavours/glitch/features/status/index.js
+++ b/app/javascript/flavours/glitch/features/status/index.js
@@ -26,7 +26,7 @@ import {
   directCompose,
 } from 'flavours/glitch/actions/compose';
 import { changeLocalSetting } from 'flavours/glitch/actions/local_settings';
-import { muteStatus, unmuteStatus, deleteStatus, editStatus } from 'flavours/glitch/actions/statuses';
+import { muteStatus, unmuteStatus, deleteStatus, editStatus, publishStatus } from 'flavours/glitch/actions/statuses';
 import { initMuteModal } from 'flavours/glitch/actions/mutes';
 import { initBlockModal } from 'flavours/glitch/actions/blocks';
 import { initReport } from 'flavours/glitch/actions/reports';
@@ -50,6 +50,8 @@ const messages = defineMessages({
   deleteMessage: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this status?' },
   redraftConfirm: { id: 'confirmations.redraft.confirm', defaultMessage: 'Delete & redraft' },
   redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? You will lose all replies, boosts and favourites to it.' },
+  publishConfirm: { id: 'confirmations.publish.confirm', defaultMessage: 'Publish' },
+  publishMessage: { id: 'confirmations.publish.message', defaultMessage: 'Are you ready to publish your post?' },
   revealAll: { id: 'status.show_more_all', defaultMessage: 'Show more for all' },
   hideAll: { id: 'status.show_less_all', defaultMessage: 'Show less for all' },
   detailedStatus: { id: 'status.detailed_status', defaultMessage: 'Detailed conversation view' },
@@ -308,6 +310,16 @@ class Status extends ImmutablePureComponent {
     this.props.dispatch(editStatus(status, history));
   }
 
+  handlePublishClick = (status) => {
+    const { dispatch, intl } = this.props;
+
+    dispatch(openModal('CONFIRM', {
+      message: intl.formatMessage(messages.publishMessage),
+      confirm: intl.formatMessage(messages.publishConfirm),
+      onConfirm: () => dispatch(publishStatus(status.get('id'))),
+    }));
+  }
+
   handleDirectClick = (account, router) => {
     this.props.dispatch(directCompose(account, router));
   }
@@ -593,6 +605,7 @@ class Status extends ImmutablePureComponent {
                   onBookmark={this.handleBookmarkClick}
                   onDelete={this.handleDeleteClick}
                   onEdit={this.handleEditClick}
+                  onPublish={this.handlePublishClick}
                   onDirect={this.handleDirectClick}
                   onMention={this.handleMentionClick}
                   onMute={this.handleMuteClick}
diff --git a/app/javascript/flavours/glitch/reducers/statuses.js b/app/javascript/flavours/glitch/reducers/statuses.js
index 5db766b96..20822b4cb 100644
--- a/app/javascript/flavours/glitch/reducers/statuses.js
+++ b/app/javascript/flavours/glitch/reducers/statuses.js
@@ -10,6 +10,7 @@ import {
 import {
   STATUS_MUTE_SUCCESS,
   STATUS_UNMUTE_SUCCESS,
+  STATUS_PUBLISH_SUCCESS,
 } from 'flavours/glitch/actions/statuses';
 import {
   TIMELINE_DELETE,
@@ -56,6 +57,8 @@ export default function statuses(state = initialState, action) {
     return state.setIn([action.id, 'muted'], true);
   case STATUS_UNMUTE_SUCCESS:
     return state.setIn([action.id, 'muted'], false);
+  case STATUS_PUBLISH_SUCCESS:
+    return state.setIn([action.id, 'published'], true);
   case TIMELINE_DELETE:
     return deleteStatus(state, action.id, action.references);
   default:
diff --git a/app/javascript/flavours/glitch/styles/monsterfork/components/status.scss b/app/javascript/flavours/glitch/styles/monsterfork/components/status.scss
index fb2cfabce..33601b8bf 100644
--- a/app/javascript/flavours/glitch/styles/monsterfork/components/status.scss
+++ b/app/javascript/flavours/glitch/styles/monsterfork/components/status.scss
@@ -1,9 +1,28 @@
-.status__edit-notice {
+.status__edit-notice, .status__unpublished-notice {
+  margin-bottom: 1em;
+
   & > span {
     color: $dark-text-color;
     line-height: normal;
     font-style: italic;
     font-size: 12px;
+    padding-left: 8px;
+    position: relative;
+    bottom: 0.25em;
+  }
+
+  & > i {
+    color: lighten($dark-text-color, 4%);
+  }
+}
+
+.status, .detailed-status {
+  &.unpublished {
+    background: darken($ui-base-color, 4%);
+
+    &:focus {
+      background: lighten($ui-base-color, 4%);
+    }
   }
 }
 
@@ -15,4 +34,4 @@
 
 div[data-nest-deep="true"] {
   border-left: 75px dashed darken($ui-base-color, 8%);
-}
\ No newline at end of file
+}