From 989c3f40022bc65d69915be597acda3c4d58de60 Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Fri, 6 Jan 2017 22:09:55 +0100
Subject: Add tab bar alternative to desktop UI, upgrade react & react-redux

---
 app/assets/javascripts/components/features/ui/index.jsx | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

(limited to 'app/assets/javascripts/components/features/ui/index.jsx')

diff --git a/app/assets/javascripts/components/features/ui/index.jsx b/app/assets/javascripts/components/features/ui/index.jsx
index 76e3dd940..db793f945 100644
--- a/app/assets/javascripts/components/features/ui/index.jsx
+++ b/app/assets/javascripts/components/features/ui/index.jsx
@@ -14,6 +14,11 @@ import { connect } from 'react-redux';
 
 const UI = React.createClass({
 
+  propTypes: {
+    dispatch: React.PropTypes.func.isRequired,
+    children: React.PropTypes.node
+  },
+
   getInitialState () {
     return {
       width: window.innerWidth
@@ -41,7 +46,7 @@ const UI = React.createClass({
   handleDrop (e) {
     e.preventDefault();
 
-    if (e.dataTransfer) {
+    if (e.dataTransfer && e.dataTransfer.files.length === 1) {
       this.props.dispatch(uploadCompose(e.dataTransfer.files));
     }
   },
@@ -72,7 +77,7 @@ const UI = React.createClass({
     } else {
       mountedColumns = (
         <ColumnsArea>
-          <Compose />
+          <Compose withHeader={true} />
           <HomeTimeline trackScroll={false} />
           <Notifications trackScroll={false} />
           {this.props.children}
-- 
cgit 


From d64c454cfe0db2e0f8205e37be4b0161309c5c2c Mon Sep 17 00:00:00 2001
From: blackle <isabelle@blackle-mori.com>
Date: Sun, 8 Jan 2017 05:04:01 -0500
Subject: Switch to compose view when tapping 'mention' in dropdown on mobile

---
 .../javascripts/components/components/status_action_bar.jsx       | 2 +-
 app/assets/javascripts/components/containers/status_container.jsx | 6 +++++-
 app/assets/javascripts/components/features/account/index.jsx      | 8 ++++++++
 app/assets/javascripts/components/features/status/index.jsx       | 4 ++++
 app/assets/javascripts/components/features/ui/index.jsx           | 5 ++---
 app/assets/javascripts/components/is_mobile.jsx                   | 5 +++++
 6 files changed, 25 insertions(+), 5 deletions(-)
 create mode 100644 app/assets/javascripts/components/is_mobile.jsx

(limited to 'app/assets/javascripts/components/features/ui/index.jsx')

diff --git a/app/assets/javascripts/components/components/status_action_bar.jsx b/app/assets/javascripts/components/components/status_action_bar.jsx
index afaf82561..c037bc573 100644
--- a/app/assets/javascripts/components/components/status_action_bar.jsx
+++ b/app/assets/javascripts/components/components/status_action_bar.jsx
@@ -49,7 +49,7 @@ const StatusActionBar = React.createClass({
   },
 
   handleMentionClick () {
-    this.props.onMention(this.props.status.get('account'));
+    this.props.onMention(this.props.status.get('account'), this.context.router);
   },
 
   handleBlockClick () {
diff --git a/app/assets/javascripts/components/containers/status_container.jsx b/app/assets/javascripts/components/containers/status_container.jsx
index 6a882eab4..ad2be03d1 100644
--- a/app/assets/javascripts/components/containers/status_container.jsx
+++ b/app/assets/javascripts/components/containers/status_container.jsx
@@ -15,6 +15,7 @@ import { blockAccount } from '../actions/accounts';
 import { deleteStatus } from '../actions/statuses';
 import { openMedia } from '../actions/modal';
 import { createSelector } from 'reselect'
+import { isMobile } from '../is_mobile'
 
 const mapStateToProps = (state, props) => ({
   statusBase: state.getIn(['statuses', props.id]),
@@ -86,8 +87,11 @@ const mapDispatchToProps = (dispatch) => ({
     dispatch(deleteStatus(status.get('id')));
   },
 
-  onMention (account) {
+  onMention (account, router) {
     dispatch(mentionCompose(account));
+    if (isMobile(window.innerWidth)) {
+      router.push('/statuses/new');
+    }
   },
 
   onOpenMedia (url) {
diff --git a/app/assets/javascripts/components/features/account/index.jsx b/app/assets/javascripts/components/features/account/index.jsx
index c2cc58bb2..2a9eba28a 100644
--- a/app/assets/javascripts/components/features/account/index.jsx
+++ b/app/assets/javascripts/components/features/account/index.jsx
@@ -20,6 +20,7 @@ import LoadingIndicator      from '../../components/loading_indicator';
 import ActionBar             from './components/action_bar';
 import Column                from '../ui/components/column';
 import ColumnBackButton      from '../../components/column_back_button';
+import { isMobile } from '../../is_mobile'
 
 const makeMapStateToProps = () => {
   const getAccount = makeGetAccount();
@@ -34,6 +35,10 @@ const makeMapStateToProps = () => {
 
 const Account = React.createClass({
 
+  contextTypes: {
+    router: React.PropTypes.object
+  },
+
   propTypes: {
     params: React.PropTypes.object.isRequired,
     dispatch: React.PropTypes.func.isRequired,
@@ -71,6 +76,9 @@ const Account = React.createClass({
 
   handleMention () {
     this.props.dispatch(mentionCompose(this.props.account));
+    if (isMobile(window.innerWidth)) {
+      this.context.router.push('/statuses/new');
+    }
   },
 
   render () {
diff --git a/app/assets/javascripts/components/features/status/index.jsx b/app/assets/javascripts/components/features/status/index.jsx
index 0a1528fe9..27a252759 100644
--- a/app/assets/javascripts/components/features/status/index.jsx
+++ b/app/assets/javascripts/components/features/status/index.jsx
@@ -23,6 +23,7 @@ import { ScrollContainer }   from 'react-router-scroll';
 import ColumnBackButton      from '../../components/column_back_button';
 import StatusContainer       from '../../containers/status_container';
 import { openMedia }         from '../../actions/modal';
+import { isMobile } from '../../is_mobile'
 
 const makeMapStateToProps = () => {
   const getStatus = makeGetStatus();
@@ -80,6 +81,9 @@ const Status = React.createClass({
 
   handleMentionClick (account) {
     this.props.dispatch(mentionCompose(account));
+    if (isMobile(window.innerWidth)) {
+      this.context.router.push('/statuses/new');
+    }
   },
 
   handleOpenMedia (url) {
diff --git a/app/assets/javascripts/components/features/ui/index.jsx b/app/assets/javascripts/components/features/ui/index.jsx
index db793f945..ee2e29d6f 100644
--- a/app/assets/javascripts/components/features/ui/index.jsx
+++ b/app/assets/javascripts/components/features/ui/index.jsx
@@ -11,6 +11,7 @@ import Notifications from '../notifications';
 import { debounce } from 'react-decoration';
 import { uploadCompose } from '../../actions/compose';
 import { connect } from 'react-redux';
+import { isMobile } from '../../is_mobile'
 
 const UI = React.createClass({
 
@@ -64,11 +65,9 @@ const UI = React.createClass({
   },
 
   render () {
-    const layoutBreakpoint = 1024;
-
     let mountedColumns;
 
-    if (this.state.width <= layoutBreakpoint) {
+    if (isMobile(this.state.width)) {
       mountedColumns = (
         <ColumnsArea>
           {this.props.children}
diff --git a/app/assets/javascripts/components/is_mobile.jsx b/app/assets/javascripts/components/is_mobile.jsx
new file mode 100644
index 000000000..eaa6221e4
--- /dev/null
+++ b/app/assets/javascripts/components/is_mobile.jsx
@@ -0,0 +1,5 @@
+const LAYOUT_BREAKPOINT = 1024;
+
+export function isMobile(width) {
+  return width <= LAYOUT_BREAKPOINT;
+};
-- 
cgit 


From 46be4631ae046c45edc3cc8e01c8fc4144ff6444 Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Thu, 19 Jan 2017 10:54:18 +0100
Subject: Fix #222 - Update followers count when following/unfollowing

Also, since the root component connects to the stream that updates home/notification columns,
there is pretty much no case for refreshing those columns beyond initial load. So, move the
loading of those columns into the root component, to prevent unneccessary reloads when switching tabs
on mobile or resizing desktop window between mobile/desktop layouts
---
 .../javascripts/components/features/home_timeline/index.jsx    |  9 +--------
 .../javascripts/components/features/notifications/index.jsx    | 10 +---------
 app/assets/javascripts/components/features/ui/index.jsx        |  9 +++++++--
 app/assets/javascripts/components/reducers/accounts.jsx        |  8 +++++++-
 4 files changed, 16 insertions(+), 20 deletions(-)

(limited to 'app/assets/javascripts/components/features/ui/index.jsx')

diff --git a/app/assets/javascripts/components/features/home_timeline/index.jsx b/app/assets/javascripts/components/features/home_timeline/index.jsx
index 8703d0b70..5d2263f15 100644
--- a/app/assets/javascripts/components/features/home_timeline/index.jsx
+++ b/app/assets/javascripts/components/features/home_timeline/index.jsx
@@ -1,8 +1,6 @@
-import { connect } from 'react-redux';
 import PureRenderMixin from 'react-addons-pure-render-mixin';
 import StatusListContainer from '../ui/containers/status_list_container';
 import Column from '../ui/components/column';
-import { refreshTimeline } from '../../actions/timelines';
 import { defineMessages, injectIntl } from 'react-intl';
 import ColumnSettingsContainer from './containers/column_settings_container';
 
@@ -13,16 +11,11 @@ const messages = defineMessages({
 const HomeTimeline = React.createClass({
 
   propTypes: {
-    dispatch: React.PropTypes.func.isRequired,
     intl: React.PropTypes.object.isRequired
   },
 
   mixins: [PureRenderMixin],
 
-  componentWillMount () {
-    this.props.dispatch(refreshTimeline('home'));
-  },
-
   render () {
     const { intl } = this.props;
 
@@ -36,4 +29,4 @@ const HomeTimeline = React.createClass({
 
 });
 
-export default connect()(injectIntl(HomeTimeline));
+export default injectIntl(HomeTimeline);
diff --git a/app/assets/javascripts/components/features/notifications/index.jsx b/app/assets/javascripts/components/features/notifications/index.jsx
index 29be491eb..d243f178f 100644
--- a/app/assets/javascripts/components/features/notifications/index.jsx
+++ b/app/assets/javascripts/components/features/notifications/index.jsx
@@ -2,10 +2,7 @@ import { connect } from 'react-redux';
 import PureRenderMixin from 'react-addons-pure-render-mixin';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import Column from '../ui/components/column';
-import {
-  refreshNotifications,
-  expandNotifications
-} from '../../actions/notifications';
+import { expandNotifications } from '../../actions/notifications';
 import NotificationContainer from './containers/notification_container';
 import { ScrollContainer } from 'react-router-scroll';
 import { defineMessages, injectIntl } from 'react-intl';
@@ -43,11 +40,6 @@ const Notifications = React.createClass({
 
   mixins: [PureRenderMixin],
 
-  componentWillMount () {
-    const { dispatch } = this.props;
-    dispatch(refreshNotifications());
-  },
-
   handleScroll (e) {
     const { scrollTop, scrollHeight, clientHeight } = e.target;
 
diff --git a/app/assets/javascripts/components/features/ui/index.jsx b/app/assets/javascripts/components/features/ui/index.jsx
index ee2e29d6f..003d061ad 100644
--- a/app/assets/javascripts/components/features/ui/index.jsx
+++ b/app/assets/javascripts/components/features/ui/index.jsx
@@ -8,10 +8,12 @@ import Compose from '../compose';
 import TabsBar from './components/tabs_bar';
 import ModalContainer from './containers/modal_container';
 import Notifications from '../notifications';
+import { connect } from 'react-redux';
+import { isMobile } from '../../is_mobile';
 import { debounce } from 'react-decoration';
 import { uploadCompose } from '../../actions/compose';
-import { connect } from 'react-redux';
-import { isMobile } from '../../is_mobile'
+import { refreshTimeline } from '../../actions/timelines';
+import { refreshNotifications } from '../../actions/notifications';
 
 const UI = React.createClass({
 
@@ -56,6 +58,9 @@ const UI = React.createClass({
     window.addEventListener('resize', this.handleResize, { passive: true });
     window.addEventListener('dragover', this.handleDragOver);
     window.addEventListener('drop', this.handleDrop);
+
+    this.props.dispatch(refreshTimeline('home'));
+    this.props.dispatch(refreshNotifications());
   },
 
   componentWillUnmount () {
diff --git a/app/assets/javascripts/components/reducers/accounts.jsx b/app/assets/javascripts/components/reducers/accounts.jsx
index 73dee9078..409dfd663 100644
--- a/app/assets/javascripts/components/reducers/accounts.jsx
+++ b/app/assets/javascripts/components/reducers/accounts.jsx
@@ -6,7 +6,9 @@ import {
   FOLLOWING_EXPAND_SUCCESS,
   ACCOUNT_TIMELINE_FETCH_SUCCESS,
   ACCOUNT_TIMELINE_EXPAND_SUCCESS,
-  FOLLOW_REQUESTS_FETCH_SUCCESS
+  FOLLOW_REQUESTS_FETCH_SUCCESS,
+  ACCOUNT_FOLLOW_SUCCESS,
+  ACCOUNT_UNFOLLOW_SUCCESS
 } from '../actions/accounts';
 import { COMPOSE_SUGGESTIONS_READY } from '../actions/compose';
 import {
@@ -105,6 +107,10 @@ export default function accounts(state = initialState, action) {
   case TIMELINE_UPDATE:
   case STATUS_FETCH_SUCCESS:
     return normalizeAccountFromStatus(state, action.status);
+  case ACCOUNT_FOLLOW_SUCCESS:
+    return state.updateIn([action.relationship.id, 'followers_count'], num => num + 1);
+  case ACCOUNT_UNFOLLOW_SUCCESS:
+    return state.updateIn([action.relationship.id, 'followers_count'], num => Math.max(0, num - 1));
   default:
     return state;
   }
-- 
cgit 

