about summary refs log tree commit diff
path: root/app/javascript/flavours
diff options
context:
space:
mode:
authorReverite <github@reverite.sh>2019-11-13 20:03:31 -0500
committerReverite <github@reverite.sh>2019-11-13 20:03:31 -0500
commit61a70e595d9ec7a6937d31b36d33063520cf1385 (patch)
tree6d7697e892f9324a5d6f00ed933606fea25fd871 /app/javascript/flavours
parent48635bf45f6402c8f82d4cb66fa42e3e5e8f30c2 (diff)
parent707c4918b21d19dd53b64120dbc7263f45fc5ecd (diff)
Merge branch 'glitch' into production
Diffstat (limited to 'app/javascript/flavours')
-rw-r--r--app/javascript/flavours/glitch/actions/compose.js8
-rw-r--r--app/javascript/flavours/glitch/components/modal_root.js1
-rw-r--r--app/javascript/flavours/glitch/components/poll.js3
-rw-r--r--app/javascript/flavours/glitch/components/status_prepend.js22
-rw-r--r--app/javascript/flavours/glitch/features/compose/containers/options_container.js3
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/focal_point_modal.js10
-rw-r--r--app/javascript/flavours/glitch/features/ui/containers/status_list_container.js7
-rw-r--r--app/javascript/flavours/glitch/features/ui/index.js4
-rw-r--r--app/javascript/flavours/glitch/packs/public.js10
-rw-r--r--app/javascript/flavours/glitch/packs/settings.js10
-rw-r--r--app/javascript/flavours/glitch/reducers/compose.js6
-rw-r--r--app/javascript/flavours/glitch/styles/components/composer.scss1
-rw-r--r--app/javascript/flavours/glitch/util/load_keyboard_extensions.js16
13 files changed, 78 insertions, 23 deletions
diff --git a/app/javascript/flavours/glitch/actions/compose.js b/app/javascript/flavours/glitch/actions/compose.js
index 69cf65b5a..7182ed0fa 100644
--- a/app/javascript/flavours/glitch/actions/compose.js
+++ b/app/javascript/flavours/glitch/actions/compose.js
@@ -232,10 +232,11 @@ export function uploadCompose(files) {
   return function (dispatch, getState) {
     const uploadLimit = 4;
     const media  = getState().getIn(['compose', 'media_attachments']);
+    const pending  = getState().getIn(['compose', 'pending_media_attachments']);
     const progress = new Array(files.length).fill(0);
     let total = Array.from(files).reduce((a, v) => a + v.size, 0);
 
-    if (files.length + media.size > uploadLimit) {
+    if (files.length + media.size + pending > uploadLimit) {
       dispatch(showAlert(undefined, messages.uploadErrorLimit));
       return;
     }
@@ -262,7 +263,7 @@ export function uploadCompose(files) {
             dispatch(uploadComposeProgress(progress.reduce((a, v) => a + v, 0), total));
           },
         }).then(({ data }) => dispatch(uploadComposeSuccess(data, f)));
-      }).catch(error => dispatch(uploadComposeFail(error)));
+      }).catch(error => dispatch(uploadComposeFail(error, true)));
     };
   };
 };
@@ -293,10 +294,11 @@ export function changeUploadComposeSuccess(media) {
   };
 };
 
-export function changeUploadComposeFail(error) {
+export function changeUploadComposeFail(error, decrement = false) {
   return {
     type: COMPOSE_UPLOAD_CHANGE_FAIL,
     error: error,
+    decrement: decrement,
     skipLoading: true,
   };
 };
diff --git a/app/javascript/flavours/glitch/components/modal_root.js b/app/javascript/flavours/glitch/components/modal_root.js
index fd0af9f6e..e73ef8d12 100644
--- a/app/javascript/flavours/glitch/components/modal_root.js
+++ b/app/javascript/flavours/glitch/components/modal_root.js
@@ -1,5 +1,6 @@
 import React from 'react';
 import PropTypes from 'prop-types';
+import 'wicg-inert';
 import createHistory from 'history/createBrowserHistory';
 
 export default class ModalRoot extends React.PureComponent {
diff --git a/app/javascript/flavours/glitch/components/poll.js b/app/javascript/flavours/glitch/components/poll.js
index a7f9a97da..2d2a7cbe0 100644
--- a/app/javascript/flavours/glitch/components/poll.js
+++ b/app/javascript/flavours/glitch/components/poll.js
@@ -39,7 +39,8 @@ class Poll extends ImmutablePureComponent {
 
   static getDerivedStateFromProps (props, state) {
     const { poll, intl } = props;
-    const expired = poll.get('expired') || (new Date(poll.get('expires_at'))).getTime() < intl.now();
+    const expires_at = poll.get('expires_at');
+    const expired = poll.get('expired') || expires_at !== null && (new Date(expires_at)).getTime() < intl.now();
     return (expired === state.expired) ? null : { expired };
   }
 
diff --git a/app/javascript/flavours/glitch/components/status_prepend.js b/app/javascript/flavours/glitch/components/status_prepend.js
index 344c4d423..637c4f23a 100644
--- a/app/javascript/flavours/glitch/components/status_prepend.js
+++ b/app/javascript/flavours/glitch/components/status_prepend.js
@@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { FormattedMessage } from 'react-intl';
 import Icon from 'flavours/glitch/components/icon';
+import { me } from 'flavours/glitch/util/initial_state';
 
 export default class StatusPrepend extends React.PureComponent {
 
@@ -64,12 +65,21 @@ export default class StatusPrepend extends React.PureComponent {
         />
       );
     case 'poll':
-      return (
-        <FormattedMessage
-          id='notification.poll'
-          defaultMessage='A poll you have voted in has ended'
-        />
-      );
+      if (me === account.get('id')) {
+        return (
+          <FormattedMessage
+            id='notification.own_poll'
+            defaultMessage='Your poll has ended'
+          />
+        );
+      } else {
+        return (
+          <FormattedMessage
+            id='notification.poll'
+            defaultMessage='A poll you have voted in has ended'
+          />
+        );
+      }
     }
     return null;
   }
diff --git a/app/javascript/flavours/glitch/features/compose/containers/options_container.js b/app/javascript/flavours/glitch/features/compose/containers/options_container.js
index df842f3bf..c792aa582 100644
--- a/app/javascript/flavours/glitch/features/compose/containers/options_container.js
+++ b/app/javascript/flavours/glitch/features/compose/containers/options_container.js
@@ -12,11 +12,12 @@ function mapStateToProps (state) {
   const spoilersAlwaysOn = state.getIn(['local_settings', 'always_show_spoilers_field']);
   const poll = state.getIn(['compose', 'poll']);
   const media = state.getIn(['compose', 'media_attachments']);
+  const pending_media = state.getIn(['compose', 'pending_media_attachments']);
   return {
     acceptContentTypes: state.getIn(['media_attachments', 'accept_content_types']).toArray().join(','),
     resetFileKey: state.getIn(['compose', 'resetFileKey']),
     hasPoll: !!poll,
-    allowMedia: !poll && (media ? media.size < 4 && !media.some(item => ['video', 'audio'].includes(item.get('type'))) : true),
+    allowMedia: !poll && (media ? media.size + pending_media < 4 && !media.some(item => ['video', 'audio'].includes(item.get('type'))) : pending_media < 4),
     hasMedia: media && !!media.size,
     allowPoll: !(media && !!media.size),
     showContentTypeChoice: state.getIn(['local_settings', 'show_content_type_choice']),
diff --git a/app/javascript/flavours/glitch/features/ui/components/focal_point_modal.js b/app/javascript/flavours/glitch/features/ui/components/focal_point_modal.js
index f5ecf77b9..70e86905f 100644
--- a/app/javascript/flavours/glitch/features/ui/components/focal_point_modal.js
+++ b/app/javascript/flavours/glitch/features/ui/components/focal_point_modal.js
@@ -184,6 +184,15 @@ class FocalPointModal extends ImmutablePureComponent {
     this.setState({ description: e.target.value, dirty: true });
   }
 
+  handleKeyDown = (e) => {
+    if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
+      e.preventDefault();
+      e.stopPropagation();
+      this.setState({ description: e.target.value, dirty: true });
+      this.handleSubmit();
+    }
+  }
+
   handleSubmit = () => {
     this.props.onSave(this.state.description, this.state.focusX, this.state.focusY);
     this.props.onClose();
@@ -254,6 +263,7 @@ class FocalPointModal extends ImmutablePureComponent {
                 className='setting-text light'
                 value={detecting ? '…' : description}
                 onChange={this.handleChange}
+                onKeyDown={this.handleKeyDown}
                 disabled={detecting}
                 autoFocus
               />
diff --git a/app/javascript/flavours/glitch/features/ui/containers/status_list_container.js b/app/javascript/flavours/glitch/features/ui/containers/status_list_container.js
index 4ca853563..c01d0e5bc 100644
--- a/app/javascript/flavours/glitch/features/ui/containers/status_list_container.js
+++ b/app/javascript/flavours/glitch/features/ui/containers/status_list_container.js
@@ -19,9 +19,9 @@ const getRegex = createSelector([
   return regex;
 });
 
-const makeGetStatusIds = () => createSelector([
+const makeGetStatusIds = (pending = false) => createSelector([
   (state, { type }) => state.getIn(['settings', type], ImmutableMap()),
-  (state, { type }) => state.getIn(['timelines', type, 'items'], ImmutableList()),
+  (state, { type }) => state.getIn(['timelines', type, pending ? 'pendingItems' : 'items'], ImmutableList()),
   (state)           => state.get('statuses'),
   getRegex,
 ], (columnSettings, statusIds, statuses, regex) => {
@@ -56,13 +56,14 @@ const makeGetStatusIds = () => createSelector([
 
 const makeMapStateToProps = () => {
   const getStatusIds = makeGetStatusIds();
+  const getPendingStatusIds = makeGetStatusIds(true);
 
   const mapStateToProps = (state, { timelineId }) => ({
     statusIds: getStatusIds(state, { type: timelineId }),
     isLoading: state.getIn(['timelines', timelineId, 'isLoading'], true),
     isPartial: state.getIn(['timelines', timelineId, 'isPartial'], false),
     hasMore:   state.getIn(['timelines', timelineId, 'hasMore']),
-    numPending: state.getIn(['timelines', timelineId, 'pendingItems'], ImmutableList()).size,
+    numPending: getPendingStatusIds(state, { type: timelineId }).size,
   });
 
   return mapStateToProps;
diff --git a/app/javascript/flavours/glitch/features/ui/index.js b/app/javascript/flavours/glitch/features/ui/index.js
index e5925a484..646def8f2 100644
--- a/app/javascript/flavours/glitch/features/ui/index.js
+++ b/app/javascript/flavours/glitch/features/ui/index.js
@@ -174,7 +174,9 @@ class SwitchingColumnsArea extends React.PureComponent {
   }
 
   setRef = c => {
-    this.node = c.getWrappedInstance();
+    if (c) {
+      this.node = c.getWrappedInstance();
+    }
   }
 
   render () {
diff --git a/app/javascript/flavours/glitch/packs/public.js b/app/javascript/flavours/glitch/packs/public.js
index 767fb023c..973d6ee46 100644
--- a/app/javascript/flavours/glitch/packs/public.js
+++ b/app/javascript/flavours/glitch/packs/public.js
@@ -1,5 +1,6 @@
 import loadPolyfills from 'flavours/glitch/util/load_polyfills';
 import ready from 'flavours/glitch/util/ready';
+import loadKeyboardExtensions from 'flavours/glitch/util/load_keyboard_extensions';
 
 function main() {
   const IntlMessageFormat = require('intl-messageformat').default;
@@ -118,6 +119,9 @@ function main() {
   });
 }
 
-loadPolyfills().then(main).catch(error => {
-  console.error(error);
-});
+loadPolyfills()
+  .then(main)
+  .then(loadKeyboardExtensions)
+  .catch(error => {
+    console.error(error);
+  });
diff --git a/app/javascript/flavours/glitch/packs/settings.js b/app/javascript/flavours/glitch/packs/settings.js
index b32f38226..edf1b82e0 100644
--- a/app/javascript/flavours/glitch/packs/settings.js
+++ b/app/javascript/flavours/glitch/packs/settings.js
@@ -1,5 +1,6 @@
 import loadPolyfills from 'flavours/glitch/util/load_polyfills';
 import ready from 'flavours/glitch/util/ready';
+import loadKeyboardExtensions from 'flavours/glitch/util/load_keyboard_extensions';
 
 function main() {
   const { delegate } = require('rails-ujs');
@@ -15,6 +16,9 @@ function main() {
   });
 }
 
-loadPolyfills().then(main).catch(error => {
-  console.error(error);
-});
+loadPolyfills()
+  .then(main)
+  .then(loadKeyboardExtensions)
+  .catch(error => {
+    console.error(error);
+  });
diff --git a/app/javascript/flavours/glitch/reducers/compose.js b/app/javascript/flavours/glitch/reducers/compose.js
index 17ce5de3c..ac826de2b 100644
--- a/app/javascript/flavours/glitch/reducers/compose.js
+++ b/app/javascript/flavours/glitch/reducers/compose.js
@@ -78,6 +78,7 @@ const initialState = ImmutableMap({
   is_changing_upload: false,
   progress: 0,
   media_attachments: ImmutableList(),
+  pending_media_attachments: 0,
   poll: null,
   suggestion_token: null,
   suggestions: ImmutableList(),
@@ -201,6 +202,7 @@ function appendMedia(state, media, file) {
     map.set('is_uploading', false);
     map.set('resetFileKey', Math.floor((Math.random() * 0x10000)));
     map.set('idempotencyKey', uuid());
+    map.update('pending_media_attachments', n => n - 1);
 
     if (prevSize === 0 && (state.get('default_sensitive') || state.get('spoiler'))) {
       map.set('sensitive', true);
@@ -423,11 +425,11 @@ export default function compose(state = initialState, action) {
   case COMPOSE_UPLOAD_CHANGE_FAIL:
     return state.set('is_changing_upload', false);
   case COMPOSE_UPLOAD_REQUEST:
-    return state.set('is_uploading', true);
+    return state.set('is_uploading', true).update('pending_media_attachments', n => n + 1);
   case COMPOSE_UPLOAD_SUCCESS:
     return appendMedia(state, fromJS(action.media), action.file);
   case COMPOSE_UPLOAD_FAIL:
-    return state.set('is_uploading', false);
+    return state.set('is_uploading', false).update('pending_media_attachments', n => action.decrement ? n - 1 : n);
   case COMPOSE_UPLOAD_UNDO:
     return removeMedia(state, action.media_id);
   case COMPOSE_UPLOAD_PROGRESS:
diff --git a/app/javascript/flavours/glitch/styles/components/composer.scss b/app/javascript/flavours/glitch/styles/components/composer.scss
index 92a29a933..51287f62e 100644
--- a/app/javascript/flavours/glitch/styles/components/composer.scss
+++ b/app/javascript/flavours/glitch/styles/components/composer.scss
@@ -249,6 +249,7 @@
 .compose-form__autosuggest-wrapper,
 .autosuggest-input {
   position: relative;
+  width: 100%;
 
   label {
     .autosuggest-textarea__textarea {
diff --git a/app/javascript/flavours/glitch/util/load_keyboard_extensions.js b/app/javascript/flavours/glitch/util/load_keyboard_extensions.js
new file mode 100644
index 000000000..2dd0e45fa
--- /dev/null
+++ b/app/javascript/flavours/glitch/util/load_keyboard_extensions.js
@@ -0,0 +1,16 @@
+// On KaiOS, we may not be able to use a mouse cursor or navigate using Tab-based focus, so we install
+// special left/right focus navigation keyboard listeners, at least on public pages (i.e. so folks
+// can at least log in using KaiOS devices).
+
+function importArrowKeyNavigation() {
+  return import(/* webpackChunkName: "arrow-key-navigation" */ 'arrow-key-navigation');
+}
+
+export default function loadKeyboardExtensions() {
+  if (/KAIOS/.test(navigator.userAgent)) {
+    return importArrowKeyNavigation().then(arrowKeyNav => {
+      arrowKeyNav.register();
+    });
+  }
+  return Promise.resolve();
+}