From 1bbcd71cd403da843e8a4a67926a4394b31a63fd Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 4 Jan 2017 15:31:25 +0100 Subject: Fix #390 - fix redirect after sign-up (to login page instead of homepage) --- app/controllers/auth/registrations_controller.rb | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'app') diff --git a/app/controllers/auth/registrations_controller.rb b/app/controllers/auth/registrations_controller.rb index 60eb9905a..6ce4984bb 100644 --- a/app/controllers/auth/registrations_controller.rb +++ b/app/controllers/auth/registrations_controller.rb @@ -23,6 +23,10 @@ class Auth::RegistrationsController < Devise::RegistrationsController new_user_session_path end + def after_inactive_sign_up_path_for(_resource) + new_user_session_path + end + def check_single_user_mode redirect_to root_path if Rails.configuration.x.single_user_mode end -- cgit From 98b83aca372fabdfc32b05c1eb72c80a79102e53 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 4 Jan 2017 15:43:28 +0100 Subject: Fix #391 - relative timestamps now contain an exact datetime in title --- .../components/components/relative_timestamp.jsx | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'app') diff --git a/app/assets/javascripts/components/components/relative_timestamp.jsx b/app/assets/javascripts/components/components/relative_timestamp.jsx index 3a5b88523..3b012b184 100644 --- a/app/assets/javascripts/components/components/relative_timestamp.jsx +++ b/app/assets/javascripts/components/components/relative_timestamp.jsx @@ -1,15 +1,18 @@ -import { - FormattedMessage, - FormattedDate, - FormattedRelative -} from 'react-intl'; - -const RelativeTimestamp = ({ timestamp }) => { - return ; +import { injectIntl, FormattedRelative } from 'react-intl'; + +const RelativeTimestamp = ({ intl, timestamp }) => { + const date = new Date(timestamp); + + return ( + + ); }; RelativeTimestamp.propTypes = { + intl: React.PropTypes.object.isRequired, timestamp: React.PropTypes.string.isRequired }; -export default RelativeTimestamp; +export default injectIntl(RelativeTimestamp); -- cgit From 3807b0b171d588ccccfc6210c823e5ce87b9b90f Mon Sep 17 00:00:00 2001 From: Jessica Stokes Date: Wed, 4 Jan 2017 17:25:48 -0800 Subject: Improve quality of life for 4-inch phones Removes extra UI margins < 360px, and allows the tab bar to scroll. Also slightly improves horizontal scrolling behaviour on desktop. --- .../components/features/ui/components/tabs_bar.jsx | 3 +-- app/assets/stylesheets/components.scss | 27 ++++++++++++++++++---- 2 files changed, 24 insertions(+), 6 deletions(-) (limited to 'app') diff --git a/app/assets/javascripts/components/features/ui/components/tabs_bar.jsx b/app/assets/javascripts/components/features/ui/components/tabs_bar.jsx index 219979522..499c9e287 100644 --- a/app/assets/javascripts/components/features/ui/components/tabs_bar.jsx +++ b/app/assets/javascripts/components/features/ui/components/tabs_bar.jsx @@ -3,9 +3,8 @@ import { FormattedMessage } from 'react-intl'; const outerStyle = { background: '#373b4a', - margin: '10px', flex: '0 0 auto', - marginBottom: '0' + overflowY: 'auto' }; const tabStyle = { diff --git a/app/assets/stylesheets/components.scss b/app/assets/stylesheets/components.scss index acfa85c6b..2996fa92e 100644 --- a/app/assets/stylesheets/components.scss +++ b/app/assets/stylesheets/components.scss @@ -331,11 +331,15 @@ } .columns-area { - margin: 10px; - margin-left: 0; flex-direction: row; } +@media screen and (min-width: 360px) { + .columns-area { + margin: 10px; + } +} + .column { width: 330px; position: relative; @@ -346,11 +350,20 @@ } .column, .drawer { - margin-left: 10px; + margin-left: 5px; + margin-right: 5px; flex: 0 0 auto; overflow: hidden; } +.column:first-child, .drawer:first-child { + margin-left: 0; +} + +.column:last-child, .drawer:last-child { + margin-right: 0; +} + @media screen and (max-width: 1024px) { .column, .drawer { width: 100%; @@ -359,7 +372,6 @@ } .columns-area { - margin: 10px; flex-direction: column; } } @@ -368,6 +380,13 @@ display: flex; } +@media screen and (min-width: 360px) { + .tabs-bar { + margin: 10px; + margin-bottom: 0; + } +} + @media screen and (min-width: 1025px) { .tabs-bar { display: none; -- cgit From 5b75f6d0f36940f40db9f1064f37b0069068b691 Mon Sep 17 00:00:00 2001 From: Jessica Stokes Date: Wed, 4 Jan 2017 17:32:11 -0800 Subject: Make tabs bar take up less room on 4-inch phones --- app/assets/javascripts/components/features/ui/components/tabs_bar.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app') diff --git a/app/assets/javascripts/components/features/ui/components/tabs_bar.jsx b/app/assets/javascripts/components/features/ui/components/tabs_bar.jsx index 499c9e287..dbfdc3f85 100644 --- a/app/assets/javascripts/components/features/ui/components/tabs_bar.jsx +++ b/app/assets/javascripts/components/features/ui/components/tabs_bar.jsx @@ -10,7 +10,7 @@ const outerStyle = { const tabStyle = { display: 'block', flex: '1 1 auto', - padding: '10px', + padding: '10px 5px', color: '#fff', textDecoration: 'none', textAlign: 'center', -- cgit From 312736cd1baaea12935e7decbb457c06299cc7d4 Mon Sep 17 00:00:00 2001 From: Jessica Stokes Date: Wed, 4 Jan 2017 17:33:05 -0800 Subject: Stop Mastodon friend from overlapping text 🐘 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/features/getting_started/index.jsx | 4 +--- app/assets/stylesheets/components.scss | 12 ++++-------- 2 files changed, 5 insertions(+), 11 deletions(-) (limited to 'app') diff --git a/app/assets/javascripts/components/features/getting_started/index.jsx b/app/assets/javascripts/components/features/getting_started/index.jsx index 157bdf8f2..77253dd73 100644 --- a/app/assets/javascripts/components/features/getting_started/index.jsx +++ b/app/assets/javascripts/components/features/getting_started/index.jsx @@ -43,13 +43,11 @@ const GettingStarted = ({ intl, me }) => { {followRequests} -
+

- -
); }; diff --git a/app/assets/stylesheets/components.scss b/app/assets/stylesheets/components.scss index 2996fa92e..c64419243 100644 --- a/app/assets/stylesheets/components.scss +++ b/app/assets/stylesheets/components.scss @@ -608,12 +608,8 @@ } } -.getting-started__illustration { - width: 330px; - height: 235px; - background: image-url('mastodon-getting-started.png') no-repeat 0 0; - position: absolute; - pointer-events: none; - bottom: 0; - left: 0; +.getting-started { + overflow-y: auto; + padding-bottom: 235px; + background: image-url('mastodon-getting-started.png') no-repeat 0 100% local; } -- cgit From aaee8c9b5d935143cf749fbea78f8cfee74ee37f Mon Sep 17 00:00:00 2001 From: Jessica Stokes Date: Wed, 4 Jan 2017 17:44:41 -0800 Subject: Disallow compose navbar from being shrunk --- .../components/features/compose/components/navigation_bar.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app') diff --git a/app/assets/javascripts/components/features/compose/components/navigation_bar.jsx b/app/assets/javascripts/components/features/compose/components/navigation_bar.jsx index df94c30d2..23d695f13 100644 --- a/app/assets/javascripts/components/features/compose/components/navigation_bar.jsx +++ b/app/assets/javascripts/components/features/compose/components/navigation_bar.jsx @@ -16,7 +16,7 @@ const NavigationBar = React.createClass({ render () { return ( -
+
-- cgit From bb033c1d37db11a871011ac8cdf6737721fbc13e Mon Sep 17 00:00:00 2001 From: Jessica Stokes Date: Wed, 4 Jan 2017 18:00:50 -0800 Subject: "Reblog" -> "boost" in more places A couple of places were using "reblog" rather than "boost" - this updates them to match the web UI --- .../components/features/notifications/components/notification.jsx | 2 +- config/locales/en.yml | 4 ++-- spec/mailers/notification_mailer_spec.rb | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'app') diff --git a/app/assets/javascripts/components/features/notifications/components/notification.jsx b/app/assets/javascripts/components/features/notifications/components/notification.jsx index 9f4cf9e4d..37715dd05 100644 --- a/app/assets/javascripts/components/features/notifications/components/notification.jsx +++ b/app/assets/javascripts/components/features/notifications/components/notification.jsx @@ -71,7 +71,7 @@ const Notification = React.createClass({
- +
diff --git a/config/locales/en.yml b/config/locales/en.yml index e166fc717..dd2ae3aac 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -67,8 +67,8 @@ en: body: 'You were mentioned by %{name} in:' subject: You were mentioned by %{name} reblog: - body: 'Your status was reblogged by %{name}:' - subject: "%{name} reblogged your status" + body: 'Your status was boosted by %{name}:' + subject: "%{name} boosted your status" pagination: next: Next prev: Prev diff --git a/spec/mailers/notification_mailer_spec.rb b/spec/mailers/notification_mailer_spec.rb index d4baca5aa..3beaebeb1 100644 --- a/spec/mailers/notification_mailer_spec.rb +++ b/spec/mailers/notification_mailer_spec.rb @@ -53,12 +53,12 @@ RSpec.describe NotificationMailer, type: :mailer do let(:mail) { NotificationMailer.reblog(own_status.account, Notification.create!(account: receiver.account, activity: reblog)) } it "renders the headers" do - expect(mail.subject).to eq("bob reblogged your status") + expect(mail.subject).to eq("bob boosted your status") expect(mail.to).to eq([receiver.email]) end it "renders the body" do - expect(mail.body.encoded).to match("Your status was reblogged by bob") + expect(mail.body.encoded).to match("Your status was boosted by bob") end end -- cgit From cbcb7e1241c4d0655ca7c6ad0840585d61e23e03 Mon Sep 17 00:00:00 2001 From: Jessica Stokes Date: Wed, 4 Jan 2017 18:10:45 -0800 Subject: Don't render the media list when there's no media This stops the empty compose view from scrolling on 4-inch devices. --- .../components/features/compose/components/upload_form.jsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'app') diff --git a/app/assets/javascripts/components/features/compose/components/upload_form.jsx b/app/assets/javascripts/components/features/compose/components/upload_form.jsx index ac548033c..8a14dda69 100644 --- a/app/assets/javascripts/components/features/compose/components/upload_form.jsx +++ b/app/assets/javascripts/components/features/compose/components/upload_form.jsx @@ -18,9 +18,13 @@ const UploadForm = React.createClass({ mixins: [PureRenderMixin], render () { - const { intl } = this.props; + const { intl, media } = this.props; - const uploads = this.props.media.map(attachment => ( + if (!media.size) { + return null; + } + + const uploads = media.map(attachment => (
@@ -29,7 +33,7 @@ const UploadForm = React.createClass({ )); return ( -
+
{uploads}
); -- cgit From 98729d50c80760e076607ecf625a95caea817aed Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 5 Jan 2017 03:14:33 +0100 Subject: Make shortcode emojis work, make getting started area scrollable --- app/assets/javascripts/components/emoji.jsx | 2 +- .../javascripts/components/features/getting_started/index.jsx | 10 ++++++---- app/assets/javascripts/extras.jsx | 2 -- app/assets/stylesheets/components.scss | 6 ++++++ 4 files changed, 13 insertions(+), 7 deletions(-) (limited to 'app') diff --git a/app/assets/javascripts/components/emoji.jsx b/app/assets/javascripts/components/emoji.jsx index a06c75953..c93c07c74 100644 --- a/app/assets/javascripts/components/emoji.jsx +++ b/app/assets/javascripts/components/emoji.jsx @@ -5,5 +5,5 @@ emojione.sprites = false; emojione.imagePathPNG = '/emoji/'; export default function emojify(text) { - return emojione.unicodeToImage(text); + return emojione.toImage(text); }; diff --git a/app/assets/javascripts/components/features/getting_started/index.jsx b/app/assets/javascripts/components/features/getting_started/index.jsx index 77253dd73..51f165f9e 100644 --- a/app/assets/javascripts/components/features/getting_started/index.jsx +++ b/app/assets/javascripts/components/features/getting_started/index.jsx @@ -43,10 +43,12 @@ const GettingStarted = ({ intl, me }) => { {followRequests}
-
-

-

-

+
+
+

+

+

+
); diff --git a/app/assets/javascripts/extras.jsx b/app/assets/javascripts/extras.jsx index b9f8e6842..c1df182de 100644 --- a/app/assets/javascripts/extras.jsx +++ b/app/assets/javascripts/extras.jsx @@ -19,8 +19,6 @@ $(() => { }); $('.webapp-btn').on('click', e => { - console.log(e); - if (e.button === 0) { e.preventDefault(); window.location.href = $(e.target).attr('href'); diff --git a/app/assets/stylesheets/components.scss b/app/assets/stylesheets/components.scss index c64419243..4f03f94e5 100644 --- a/app/assets/stylesheets/components.scss +++ b/app/assets/stylesheets/components.scss @@ -436,6 +436,10 @@ overflow-x: hidden; flex: 1 1 auto; -webkit-overflow-scrolling: touch; + + &.optionally-scrollable { + overflow-y: auto; + } } .column-back-button { @@ -609,7 +613,9 @@ } .getting-started { + box-sizing: border-box; overflow-y: auto; padding-bottom: 235px; background: image-url('mastodon-getting-started.png') no-repeat 0 100% local; + height: 100%; } -- cgit From 251b04298e826818ecb48c2fdf96a83649e14e93 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 5 Jan 2017 03:17:23 +0100 Subject: Fix undesired delivering of private toot to remote accounts that follow author --- app/services/process_mentions_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app') diff --git a/app/services/process_mentions_service.rb b/app/services/process_mentions_service.rb index ee42a5df2..72568e702 100644 --- a/app/services/process_mentions_service.rb +++ b/app/services/process_mentions_service.rb @@ -28,7 +28,7 @@ class ProcessMentionsService < BaseService status.mentions.each do |mention| mentioned_account = mention.account - next if status.private_visibility? && !mentioned_account.following?(status.account) + next if status.private_visibility? && (!mentioned_account.following?(status.account) || !mentioned_account.local?) if mentioned_account.local? NotifyService.new.call(mentioned_account, mention) -- cgit From 6c28886317bdf837e7e3f399115af91ac282735e Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 5 Jan 2017 03:28:21 +0100 Subject: Improve background jobs params and error handling --- app/workers/notification_worker.rb | 2 ++ app/workers/pubsubhubbub/confirmation_worker.rb | 2 +- app/workers/pubsubhubbub/delivery_worker.rb | 6 +++++- app/workers/thread_resolve_worker.rb | 2 ++ app/workers/unfavourite_worker.rb | 2 ++ 5 files changed, 12 insertions(+), 2 deletions(-) (limited to 'app') diff --git a/app/workers/notification_worker.rb b/app/workers/notification_worker.rb index 386e94111..e4c38d384 100644 --- a/app/workers/notification_worker.rb +++ b/app/workers/notification_worker.rb @@ -3,6 +3,8 @@ class NotificationWorker include Sidekiq::Worker + sidekiq_options retry: 5 + def perform(stream_entry_id, target_account_id) SendInteractionService.new.call(StreamEntry.find(stream_entry_id), Account.find(target_account_id)) end diff --git a/app/workers/pubsubhubbub/confirmation_worker.rb b/app/workers/pubsubhubbub/confirmation_worker.rb index 489bd8359..868fd9f97 100644 --- a/app/workers/pubsubhubbub/confirmation_worker.rb +++ b/app/workers/pubsubhubbub/confirmation_worker.rb @@ -4,7 +4,7 @@ class Pubsubhubbub::ConfirmationWorker include Sidekiq::Worker include RoutingHelper - sidekiq_options queue: 'push' + sidekiq_options queue: 'push', retry: false def perform(subscription_id, mode, secret = nil, lease_seconds = nil) subscription = Subscription.find(subscription_id) diff --git a/app/workers/pubsubhubbub/delivery_worker.rb b/app/workers/pubsubhubbub/delivery_worker.rb index 35bf7b2f0..15005bc80 100644 --- a/app/workers/pubsubhubbub/delivery_worker.rb +++ b/app/workers/pubsubhubbub/delivery_worker.rb @@ -4,7 +4,11 @@ class Pubsubhubbub::DeliveryWorker include Sidekiq::Worker include RoutingHelper - sidekiq_options queue: 'push', retry: 5 + sidekiq_options queue: 'push', retry: 3, dead: false + + sidekiq_retry_in do |count| + 5 * (count + 1) + end def perform(subscription_id, payload) subscription = Subscription.find(subscription_id) diff --git a/app/workers/thread_resolve_worker.rb b/app/workers/thread_resolve_worker.rb index 84eae73be..593edd032 100644 --- a/app/workers/thread_resolve_worker.rb +++ b/app/workers/thread_resolve_worker.rb @@ -3,6 +3,8 @@ class ThreadResolveWorker include Sidekiq::Worker + sidekiq_options retry: false + def perform(child_status_id, parent_url) child_status = Status.find(child_status_id) parent_status = FetchRemoteStatusService.new.call(parent_url) diff --git a/app/workers/unfavourite_worker.rb b/app/workers/unfavourite_worker.rb index a14c82d6f..cce07e486 100644 --- a/app/workers/unfavourite_worker.rb +++ b/app/workers/unfavourite_worker.rb @@ -5,5 +5,7 @@ class UnfavouriteWorker def perform(account_id, status_id) UnfavouriteService.new.call(Account.find(account_id), Status.find(status_id)) + rescue ActiveRecord::RecordNotFound + true end end -- cgit From 1da73ecadedf26c5f04dc2de3bb65349ffc52ab9 Mon Sep 17 00:00:00 2001 From: Jessica Stokes Date: Wed, 4 Jan 2017 18:29:43 -0800 Subject: Fix Command-enter tooting metaKey is only set correctly on keyDown, not keyUp, so this swaps to using that --- .../javascripts/components/components/autosuggest_textarea.jsx | 9 ++++++++- .../components/features/compose/components/compose_form.jsx | 4 ++-- 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'app') diff --git a/app/assets/javascripts/components/components/autosuggest_textarea.jsx b/app/assets/javascripts/components/components/autosuggest_textarea.jsx index 39ccbcaf9..57352be90 100644 --- a/app/assets/javascripts/components/components/autosuggest_textarea.jsx +++ b/app/assets/javascripts/components/components/autosuggest_textarea.jsx @@ -38,7 +38,8 @@ const AutosuggestTextarea = React.createClass({ onSuggestionsClearRequested: React.PropTypes.func.isRequired, onSuggestionsFetchRequested: React.PropTypes.func.isRequired, onChange: React.PropTypes.func.isRequired, - onKeyUp: React.PropTypes.func + onKeyUp: React.PropTypes.func, + onKeyDown: React.PropTypes.func }, getInitialState () { @@ -108,6 +109,12 @@ const AutosuggestTextarea = React.createClass({ break; } + + if (e.defaultPrevented || !this.props.onKeyDown) { + return; + } + + this.props.onKeyDown(e); }, onBlur () { diff --git a/app/assets/javascripts/components/features/compose/components/compose_form.jsx b/app/assets/javascripts/components/features/compose/components/compose_form.jsx index 55f361b0b..412c29310 100644 --- a/app/assets/javascripts/components/features/compose/components/compose_form.jsx +++ b/app/assets/javascripts/components/features/compose/components/compose_form.jsx @@ -49,7 +49,7 @@ const ComposeForm = React.createClass({ this.props.onChange(e.target.value); }, - handleKeyUp (e) { + handleKeyDown (e) { if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) { this.props.onSubmit(); } @@ -115,7 +115,7 @@ const ComposeForm = React.createClass({ value={this.props.text} onChange={this.handleChange} suggestions={this.props.suggestions} - onKeyUp={this.handleKeyUp} + onKeyDown={this.handleKeyDown} onSuggestionsFetchRequested={this.onSuggestionsFetchRequested} onSuggestionsClearRequested={this.onSuggestionsClearRequested} onSuggestionSelected={this.onSuggestionSelected} -- cgit From cc46c6b4936a3dbe1c38b97ab77a3fcfd3f22f03 Mon Sep 17 00:00:00 2001 From: Jessica Stokes Date: Wed, 4 Jan 2017 18:31:45 -0800 Subject: Friendlier unknown errors Don't ask users to check the console - if they're on mobile, they probably can't anyway ;) --- app/assets/javascripts/components/middleware/errors.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app') diff --git a/app/assets/javascripts/components/middleware/errors.jsx b/app/assets/javascripts/components/middleware/errors.jsx index 3a1473bc1..74d77f0f9 100644 --- a/app/assets/javascripts/components/middleware/errors.jsx +++ b/app/assets/javascripts/components/middleware/errors.jsx @@ -23,7 +23,7 @@ export default function errorsMiddleware() { dispatch(showAlert(title, message)); } else { console.error(action.error); - dispatch(showAlert('Oops!', 'An unexpected error occurred. Inspect the console for more details')); + dispatch(showAlert('Oops!', 'An unexpected error occurred.')); } } } -- cgit From 0c600e9db6f343cdf8068554e27590e126035229 Mon Sep 17 00:00:00 2001 From: Jessica Stokes Date: Wed, 4 Jan 2017 19:30:02 -0800 Subject: Move "getting started" to its own route --- app/assets/javascripts/components/containers/mastodon.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'app') diff --git a/app/assets/javascripts/components/containers/mastodon.jsx b/app/assets/javascripts/components/containers/mastodon.jsx index 670455376..026daeb06 100644 --- a/app/assets/javascripts/components/containers/mastodon.jsx +++ b/app/assets/javascripts/components/containers/mastodon.jsx @@ -16,6 +16,7 @@ import { useRouterHistory, Router, Route, + IndexRedirect, IndexRoute } from 'react-router'; import { useScroll } from 'react-router-scroll'; @@ -107,8 +108,9 @@ const Mastodon = React.createClass({ - + + -- cgit From 9c493b1ea28ac3d36be367c019236394b6bd341d Mon Sep 17 00:00:00 2001 From: Jessica Stokes Date: Wed, 4 Jan 2017 19:30:22 -0800 Subject: Replace "Public" in tab bar with "More" hamburger --- app/assets/javascripts/components/features/ui/components/tabs_bar.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app') diff --git a/app/assets/javascripts/components/features/ui/components/tabs_bar.jsx b/app/assets/javascripts/components/features/ui/components/tabs_bar.jsx index dbfdc3f85..aa40a488f 100644 --- a/app/assets/javascripts/components/features/ui/components/tabs_bar.jsx +++ b/app/assets/javascripts/components/features/ui/components/tabs_bar.jsx @@ -30,7 +30,7 @@ const TabsBar = () => { - +
); }; -- cgit From 05cc5636d8d56f95e1428408f6fce87ffb7c1a42 Mon Sep 17 00:00:00 2001 From: Jessica Stokes Date: Wed, 4 Jan 2017 19:30:39 -0800 Subject: Remove hamburger from "getting started" --- .../components/features/getting_started/index.jsx | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'app') diff --git a/app/assets/javascripts/components/features/getting_started/index.jsx b/app/assets/javascripts/components/features/getting_started/index.jsx index 51f165f9e..9f47e7ab4 100644 --- a/app/assets/javascripts/components/features/getting_started/index.jsx +++ b/app/assets/javascripts/components/features/getting_started/index.jsx @@ -16,17 +16,6 @@ const mapStateToProps = state => ({ me: state.getIn(['accounts', state.getIn(['meta', 'me'])]) }); -const hamburgerStyle = { - background: '#373b4a', - color: '#fff', - fontSize: '16px', - padding: '15px', - position: 'absolute', - right: '0', - top: '-48px', - cursor: 'default' -}; - const GettingStarted = ({ intl, me }) => { let followRequests = ''; @@ -37,7 +26,6 @@ const GettingStarted = ({ intl, me }) => { return (
-
{followRequests} -- cgit From 7ac55d2674b65bae78e6559a51ce97d859074bba Mon Sep 17 00:00:00 2001 From: Jessica Stokes Date: Wed, 4 Jan 2017 19:47:02 -0800 Subject: Differentiate settings links The "settings" links in the Getting Started section (or, if #399 were to happen, "more" menu) and compose sections are now different; the "compose" link is "Edit profile," while the one in the Getting Started section is now "Preferences." All languages have been updated to accommodate this, based on the existing usages of these phrases in language files in the Rails part of the app! addresses part of #384 --- .../components/features/compose/components/navigation_bar.jsx | 2 +- app/assets/javascripts/components/features/getting_started/index.jsx | 4 ++-- app/assets/javascripts/components/locales/de.jsx | 3 ++- app/assets/javascripts/components/locales/en.jsx | 3 ++- app/assets/javascripts/components/locales/es.jsx | 3 ++- app/assets/javascripts/components/locales/fr.jsx | 3 ++- app/assets/javascripts/components/locales/hu.jsx | 3 ++- app/assets/javascripts/components/locales/pt.jsx | 3 ++- app/assets/javascripts/components/locales/uk.jsx | 3 ++- 9 files changed, 17 insertions(+), 10 deletions(-) (limited to 'app') diff --git a/app/assets/javascripts/components/features/compose/components/navigation_bar.jsx b/app/assets/javascripts/components/features/compose/components/navigation_bar.jsx index 23d695f13..71b50fc3a 100644 --- a/app/assets/javascripts/components/features/compose/components/navigation_bar.jsx +++ b/app/assets/javascripts/components/features/compose/components/navigation_bar.jsx @@ -21,7 +21,7 @@ const NavigationBar = React.createClass({
{this.props.account.get('acct')} - · · + ·
); diff --git a/app/assets/javascripts/components/features/getting_started/index.jsx b/app/assets/javascripts/components/features/getting_started/index.jsx index 51f165f9e..ae850c3f9 100644 --- a/app/assets/javascripts/components/features/getting_started/index.jsx +++ b/app/assets/javascripts/components/features/getting_started/index.jsx @@ -8,7 +8,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; const messages = defineMessages({ heading: { id: 'getting_started.heading', defaultMessage: 'Getting started' }, public_timeline: { id: 'navigation_bar.public_timeline', defaultMessage: 'Public timeline' }, - settings: { id: 'navigation_bar.settings', defaultMessage: 'Settings' }, + preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' }, follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' } }); @@ -39,7 +39,7 @@ const GettingStarted = ({ intl, me }) => {
- + {followRequests}
diff --git a/app/assets/javascripts/components/locales/de.jsx b/app/assets/javascripts/components/locales/de.jsx index 17b74e15d..97df67480 100644 --- a/app/assets/javascripts/components/locales/de.jsx +++ b/app/assets/javascripts/components/locales/de.jsx @@ -36,7 +36,8 @@ const en = { "compose_form.publish": "Veröffentlichen", "compose_form.sensitive": "Medien als sensitiv markieren", "compose_form.unlisted": "Öffentlich nicht auflisten", - "navigation_bar.settings": "Einstellungen", + "navigation_bar.edit_profile": "Profil bearbeiten", + "navigation_bar.preferences": "Einstellungen", "navigation_bar.public_timeline": "Öffentlich", "navigation_bar.logout": "Abmelden", "reply_indicator.cancel": "Abbrechen", diff --git a/app/assets/javascripts/components/locales/en.jsx b/app/assets/javascripts/components/locales/en.jsx index 3d4a38919..05fb0243c 100644 --- a/app/assets/javascripts/components/locales/en.jsx +++ b/app/assets/javascripts/components/locales/en.jsx @@ -40,7 +40,8 @@ const en = { "compose_form.publish": "Toot", "compose_form.sensitive": "Mark media as sensitive", "compose_form.private": "Mark as private", - "navigation_bar.settings": "Settings", + "navigation_bar.edit_profile": "Edit profile", + "navigation_bar.preferences": "Preferences", "navigation_bar.public_timeline": "Public timeline", "navigation_bar.logout": "Logout", "reply_indicator.cancel": "Cancel", diff --git a/app/assets/javascripts/components/locales/es.jsx b/app/assets/javascripts/components/locales/es.jsx index 6bd9b18ed..b75fb57d9 100644 --- a/app/assets/javascripts/components/locales/es.jsx +++ b/app/assets/javascripts/components/locales/es.jsx @@ -37,7 +37,8 @@ const es = { "compose_form.publish": "Publicar", "compose_form.sensitive": "Marcar el contenido como sensible", "compose_form.unlisted": "Privado", - "navigation_bar.settings": "Ajustes", + "navigation_bar.edit_profile": "Editar perfil", + "navigation_bar.preferences": "Preferencias", "navigation_bar.public_timeline": "Público", "navigation_bar.logout": "Cerrar sesión", "reply_indicator.cancel": "Cancelar", diff --git a/app/assets/javascripts/components/locales/fr.jsx b/app/assets/javascripts/components/locales/fr.jsx index 968c3f8c3..183e5d5b5 100644 --- a/app/assets/javascripts/components/locales/fr.jsx +++ b/app/assets/javascripts/components/locales/fr.jsx @@ -38,7 +38,8 @@ const fr = { "compose_form.publish": "Pouet", "compose_form.sensitive": "Marquer le contenu comme délicat", "compose_form.unlisted": "Ne pas apparaître dans le fil public", - "navigation_bar.settings": "Paramètres", + "navigation_bar.edit_profile": "Modifier le profil", + "navigation_bar.preferences": "Préférences", "navigation_bar.public_timeline": "Public", "navigation_bar.logout": "Déconnexion", "reply_indicator.cancel": "Annuler", diff --git a/app/assets/javascripts/components/locales/hu.jsx b/app/assets/javascripts/components/locales/hu.jsx index 606fc830f..9a2d14d87 100644 --- a/app/assets/javascripts/components/locales/hu.jsx +++ b/app/assets/javascripts/components/locales/hu.jsx @@ -38,7 +38,8 @@ const hu = { "compose_form.publish": "Tülk!", "compose_form.sensitive": "Tartalom érzékenynek jelölése", "compose_form.unlisted": "Listázatlan mód", - "navigation_bar.settings": "Beállítások", + "navigation_bar.edit_profile": "Profil szerkesztése", + "navigation_bar.preferences": "Beállítások", "navigation_bar.public_timeline": "Nyilvános időfolyam", "navigation_bar.logout": "Kijelentkezés", "reply_indicator.cancel": "Mégsem", diff --git a/app/assets/javascripts/components/locales/pt.jsx b/app/assets/javascripts/components/locales/pt.jsx index 57cbcd31b..d68724b13 100644 --- a/app/assets/javascripts/components/locales/pt.jsx +++ b/app/assets/javascripts/components/locales/pt.jsx @@ -36,7 +36,8 @@ const pt = { "compose_form.publish": "Publicar", "compose_form.sensitive": "Marcar conteúdo como sensível", "compose_form.unlisted": "Modo não-listado", - "navigation_bar.settings": "Configurações", + "navigation_bar.edit_profile": "Editar perfil", + "navigation_bar.preferences": "Preferências", "navigation_bar.public_timeline": "Timeline Pública", "navigation_bar.logout": "Logout", "reply_indicator.cancel": "Cancelar", diff --git a/app/assets/javascripts/components/locales/uk.jsx b/app/assets/javascripts/components/locales/uk.jsx index 53535c25a..84a348c21 100644 --- a/app/assets/javascripts/components/locales/uk.jsx +++ b/app/assets/javascripts/components/locales/uk.jsx @@ -38,7 +38,8 @@ const uk = { "compose_form.publish": "Дмухнути", "compose_form.sensitive": "Непристойний зміст", "compose_form.unlisted": "Таємний режим", - "navigation_bar.settings": "Налаштування", + "navigation_bar.edit_profile": "Редагувати профіль", + "navigation_bar.preferences": "Налаштування", "navigation_bar.public_timeline": "Публічна стіна", "navigation_bar.logout": "Вийти", "reply_indicator.cancel": "Відмінити", -- cgit From c100b83b98b8a6ce24d7f0d9140f47e722b17a06 Mon Sep 17 00:00:00 2001 From: Misty De Meo Date: Wed, 4 Jan 2017 20:04:14 -0800 Subject: Automatically position cursor when writing a reply toot --- .../components/features/compose/components/compose_form.jsx | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'app') diff --git a/app/assets/javascripts/components/features/compose/components/compose_form.jsx b/app/assets/javascripts/components/features/compose/components/compose_form.jsx index 412c29310..44c44bcb0 100644 --- a/app/assets/javascripts/components/features/compose/components/compose_form.jsx +++ b/app/assets/javascripts/components/features/compose/components/compose_form.jsx @@ -86,6 +86,13 @@ const ComposeForm = React.createClass({ componentDidUpdate (prevProps) { if (prevProps.in_reply_to !== this.props.in_reply_to) { + // If replying to zero or one users, places the cursor at the end of the textbox. + // If replying to more than one user, selects any usernames past the first; + // this provides a convenient shortcut to drop everyone else from the conversation. + let selectionStart = this.props.text.search(/\s/) + 1; + let selectionEnd = this.props.text.length; + this.autosuggestTextarea.textarea.setSelectionRange(selectionStart, selectionEnd); + this.autosuggestTextarea.textarea.focus(); } }, -- cgit From 1f3c895ffb320c188b77b0036603b40a22733902 Mon Sep 17 00:00:00 2001 From: Jessica Stokes Date: Wed, 4 Jan 2017 20:24:27 -0800 Subject: Use system fonts on more platforms This allows other platforms such as Windows, macOS and iOS to use their system fonts rather than downloading a copy of Roboto. It also makes the app feel a little closer to native on those platforms! --- app/assets/javascripts/components/components/button.jsx | 2 +- .../javascripts/components/features/compose/components/search.jsx | 2 +- app/assets/stylesheets/about.scss | 2 +- app/assets/stylesheets/application.scss | 2 +- app/assets/stylesheets/components.scss | 4 ++-- app/assets/stylesheets/forms.scss | 6 +++--- public/404.html | 4 ++-- public/500.html | 4 ++-- storybook/storybook.scss | 2 +- 9 files changed, 14 insertions(+), 14 deletions(-) (limited to 'app') diff --git a/app/assets/javascripts/components/components/button.jsx b/app/assets/javascripts/components/components/button.jsx index d63129013..19c52550a 100644 --- a/app/assets/javascripts/components/components/button.jsx +++ b/app/assets/javascripts/components/components/button.jsx @@ -27,7 +27,7 @@ const Button = React.createClass({ render () { const style = { - fontFamily: 'Roboto', + fontFamily: 'inherit', display: this.props.block ? 'block' : 'inline-block', width: this.props.block ? '100%' : 'auto', position: 'relative', diff --git a/app/assets/javascripts/components/features/compose/components/search.jsx b/app/assets/javascripts/components/features/compose/components/search.jsx index b4e618820..e4672216b 100644 --- a/app/assets/javascripts/components/features/compose/components/search.jsx +++ b/app/assets/javascripts/components/features/compose/components/search.jsx @@ -38,7 +38,7 @@ const inputStyle = { border: 'none', padding: '10px', paddingRight: '30px', - fontFamily: 'Roboto', + fontFamily: 'inherit', background: '#282c37', color: '#9baec8', fontSize: '14px', diff --git a/app/assets/stylesheets/about.scss b/app/assets/stylesheets/about.scss index 3681672d8..620c86a67 100644 --- a/app/assets/stylesheets/about.scss +++ b/app/assets/stylesheets/about.scss @@ -11,7 +11,7 @@ } h1 { - font: 46px/52px 'Roboto', sans-serif; + font: 46px/52px -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; font-weight: 600; margin-bottom: 20px; color: #2b90d9; diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index e4c550b81..1e571385f 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -95,7 +95,7 @@ table { } body { - font-family: 'Roboto', sans-serif; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #282c37 image-url('background-photo.jpeg'); background-size: cover; background-attachment: fixed; diff --git a/app/assets/stylesheets/components.scss b/app/assets/stylesheets/components.scss index 4f03f94e5..c2fcd76b3 100644 --- a/app/assets/stylesheets/components.scss +++ b/app/assets/stylesheets/components.scss @@ -1,6 +1,6 @@ .button { background-color: #2b90d9; - font-family: 'Roboto'; + font-family: inherit; display: inline-block; position: relative; box-sizing: border-box; @@ -574,7 +574,7 @@ resize: none; color: #282c37; padding: 7px; - font-family: 'Roboto'; + font-family: inherit; font-size: 14px; margin: 0; resize: vertical; diff --git a/app/assets/stylesheets/forms.scss b/app/assets/stylesheets/forms.scss index e6d2e85a2..b3b71b412 100644 --- a/app/assets/stylesheets/forms.scss +++ b/app/assets/stylesheets/forms.scss @@ -26,7 +26,7 @@ code { display: flex; label { - font-family: 'Roboto'; + font-family: inherit; font-size: 16px; color: #fff; width: 100px; @@ -48,7 +48,7 @@ code { margin-bottom: 5px; label { - font-family: 'Roboto'; + font-family: inherit; font-size: 14px; color: white; display: block; @@ -83,7 +83,7 @@ code { display: block; width: 100%; outline: 0; - font-family: 'Roboto'; + font-family: inherit; &:invalid { box-shadow: none; diff --git a/public/404.html b/public/404.html index eecfd6743..fc75c78be 100644 --- a/public/404.html +++ b/public/404.html @@ -7,7 +7,7 @@ diff --git a/public/500.html b/public/500.html index 915b890f1..d085d490b 100644 --- a/public/500.html +++ b/public/500.html @@ -7,7 +7,7 @@ diff --git a/storybook/storybook.scss b/storybook/storybook.scss index b0145f9bd..31f11b5ad 100644 --- a/storybook/storybook.scss +++ b/storybook/storybook.scss @@ -2,7 +2,7 @@ @import url(https://fonts.googleapis.com/css?family=Roboto+Mono:400,500); #root { - font-family: 'Roboto', sans-serif; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #282c37; font-size: 13px; line-height: 18px; -- cgit From c318e6e42e2903af90be205760a5a2ba00e378b8 Mon Sep 17 00:00:00 2001 From: Misty De Meo Date: Wed, 4 Jan 2017 22:23:02 -0800 Subject: Display native emoji on browsers which support it --- app/assets/javascripts/components/emoji.jsx | 11 ++++++++++- package.json | 1 + yarn.lock | 8 ++++++-- 3 files changed, 17 insertions(+), 3 deletions(-) (limited to 'app') diff --git a/app/assets/javascripts/components/emoji.jsx b/app/assets/javascripts/components/emoji.jsx index c93c07c74..82b82b719 100644 --- a/app/assets/javascripts/components/emoji.jsx +++ b/app/assets/javascripts/components/emoji.jsx @@ -1,9 +1,18 @@ import emojione from 'emojione'; +import detectVersion from 'mojibaka'; emojione.imageType = 'png'; emojione.sprites = false; emojione.imagePathPNG = '/emoji/'; +let emoji_version = detectVersion(); + export default function emojify(text) { - return emojione.toImage(text); + // Browser too old to support native emoji + if (emoji_version < 6.1) { + return emojione.toImage(text); + // Convert short codes into native emoji + } else { + return emojione.shortnameToUnicode(text); + } }; diff --git a/package.json b/package.json index 05663a729..13b0484ec 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "intl": "^1.2.5", "jsdom": "^9.6.0", "mocha": "^3.1.1", + "mojibaka": "^0.0.1", "node-sass": "^4.0.0", "react": "^15.3.2", "react-addons-perf": "^15.3.2", diff --git a/yarn.lock b/yarn.lock index f71a8ae10..b79c46898 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2368,7 +2368,7 @@ glob-parent@^2.0.0: dependencies: is-glob "^2.0.0" -glob@7.0.5, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5: +glob@7.0.5, glob@^7.0.0, glob@^7.0.3: version "7.0.5" resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.5.tgz#b4202a69099bbb4d292a7c1b95b6682b67ebdc95" dependencies: @@ -2389,7 +2389,7 @@ glob@^5.0.15: once "^1.3.0" path-is-absolute "^1.0.0" -glob@~7.1.1: +glob@^7.0.5, glob@~7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" dependencies: @@ -3392,6 +3392,10 @@ module-deps@^4.0.2: through2 "^2.0.0" xtend "^4.0.0" +mojibaka@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/mojibaka/-/mojibaka-0.0.1.tgz#54b0690d9149bbdf97f13b909f2417c53b8d52e5" + ms@0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" -- cgit From 53b765f4b150d7671d697eed7333c948622045cf Mon Sep 17 00:00:00 2001 From: Misty De Meo Date: Wed, 4 Jan 2017 22:47:51 -0800 Subject: Bump emoji requirement to Unicode 9 --- app/assets/javascripts/components/emoji.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app') diff --git a/app/assets/javascripts/components/emoji.jsx b/app/assets/javascripts/components/emoji.jsx index 82b82b719..990aea5be 100644 --- a/app/assets/javascripts/components/emoji.jsx +++ b/app/assets/javascripts/components/emoji.jsx @@ -9,7 +9,7 @@ let emoji_version = detectVersion(); export default function emojify(text) { // Browser too old to support native emoji - if (emoji_version < 6.1) { + if (emoji_version < 9.0) { return emojione.toImage(text); // Convert short codes into native emoji } else { -- cgit From 9e6ceb3201a2737e676decf3df9605714632a300 Mon Sep 17 00:00:00 2001 From: Eugen Date: Thu, 5 Jan 2017 13:45:21 +0100 Subject: Revert "Display native emoji on browsers which support it" --- app/assets/javascripts/components/emoji.jsx | 11 +---------- package.json | 1 - yarn.lock | 8 ++------ 3 files changed, 3 insertions(+), 17 deletions(-) (limited to 'app') diff --git a/app/assets/javascripts/components/emoji.jsx b/app/assets/javascripts/components/emoji.jsx index 990aea5be..c93c07c74 100644 --- a/app/assets/javascripts/components/emoji.jsx +++ b/app/assets/javascripts/components/emoji.jsx @@ -1,18 +1,9 @@ import emojione from 'emojione'; -import detectVersion from 'mojibaka'; emojione.imageType = 'png'; emojione.sprites = false; emojione.imagePathPNG = '/emoji/'; -let emoji_version = detectVersion(); - export default function emojify(text) { - // Browser too old to support native emoji - if (emoji_version < 9.0) { - return emojione.toImage(text); - // Convert short codes into native emoji - } else { - return emojione.shortnameToUnicode(text); - } + return emojione.toImage(text); }; diff --git a/package.json b/package.json index 13b0484ec..05663a729 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,6 @@ "intl": "^1.2.5", "jsdom": "^9.6.0", "mocha": "^3.1.1", - "mojibaka": "^0.0.1", "node-sass": "^4.0.0", "react": "^15.3.2", "react-addons-perf": "^15.3.2", diff --git a/yarn.lock b/yarn.lock index b79c46898..f71a8ae10 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2368,7 +2368,7 @@ glob-parent@^2.0.0: dependencies: is-glob "^2.0.0" -glob@7.0.5, glob@^7.0.0, glob@^7.0.3: +glob@7.0.5, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5: version "7.0.5" resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.5.tgz#b4202a69099bbb4d292a7c1b95b6682b67ebdc95" dependencies: @@ -2389,7 +2389,7 @@ glob@^5.0.15: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.5, glob@~7.1.1: +glob@~7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" dependencies: @@ -3392,10 +3392,6 @@ module-deps@^4.0.2: through2 "^2.0.0" xtend "^4.0.0" -mojibaka@^0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/mojibaka/-/mojibaka-0.0.1.tgz#54b0690d9149bbdf97f13b909f2417c53b8d52e5" - ms@0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" -- cgit From 10e6288444567106570baf317801d99989e2df83 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 5 Jan 2017 13:59:58 +0100 Subject: Revert to Roboto for all --- app/assets/stylesheets/application.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app') diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 1e571385f..e4c550b81 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -95,7 +95,7 @@ table { } body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + font-family: 'Roboto', sans-serif; background: #282c37 image-url('background-photo.jpeg'); background-size: cover; background-attachment: fixed; -- cgit From ca7dce4a5a0234461fee554153d9328bc246ee55 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 5 Jan 2017 14:06:09 +0100 Subject: Fix selection resetting in compose form after unrelated data updates --- .../components/features/compose/components/compose_form.jsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'app') diff --git a/app/assets/javascripts/components/features/compose/components/compose_form.jsx b/app/assets/javascripts/components/features/compose/components/compose_form.jsx index 44c44bcb0..8128bb382 100644 --- a/app/assets/javascripts/components/features/compose/components/compose_form.jsx +++ b/app/assets/javascripts/components/features/compose/components/compose_form.jsx @@ -85,14 +85,14 @@ const ComposeForm = React.createClass({ }, componentDidUpdate (prevProps) { - if (prevProps.in_reply_to !== this.props.in_reply_to) { + if (!prevProps.in_reply_to || !this.props.in_reply_to || prevProps.in_reply_to.get('id') !== this.props.in_reply_to.get('id')) { // If replying to zero or one users, places the cursor at the end of the textbox. // If replying to more than one user, selects any usernames past the first; // this provides a convenient shortcut to drop everyone else from the conversation. - let selectionStart = this.props.text.search(/\s/) + 1; - let selectionEnd = this.props.text.length; - this.autosuggestTextarea.textarea.setSelectionRange(selectionStart, selectionEnd); + const selectionStart = this.props.text.search(/\s/) + 1; + const selectionEnd = this.props.text.length; + this.autosuggestTextarea.textarea.setSelectionRange(selectionStart, selectionEnd); this.autosuggestTextarea.textarea.focus(); } }, -- cgit From a1de2e332d4406eb0d5934aa22fb96958d291a5f Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 5 Jan 2017 14:18:38 +0100 Subject: Fix compose form bug --- .../javascripts/components/features/compose/components/compose_form.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app') diff --git a/app/assets/javascripts/components/features/compose/components/compose_form.jsx b/app/assets/javascripts/components/features/compose/components/compose_form.jsx index 8128bb382..5a76b177b 100644 --- a/app/assets/javascripts/components/features/compose/components/compose_form.jsx +++ b/app/assets/javascripts/components/features/compose/components/compose_form.jsx @@ -85,7 +85,7 @@ const ComposeForm = React.createClass({ }, componentDidUpdate (prevProps) { - if (!prevProps.in_reply_to || !this.props.in_reply_to || prevProps.in_reply_to.get('id') !== this.props.in_reply_to.get('id')) { + if ((!prevProps.in_reply_to && this.props.in_reply_to) || prevProps.in_reply_to.get('id') !== this.props.in_reply_to.get('id')) { // If replying to zero or one users, places the cursor at the end of the textbox. // If replying to more than one user, selects any usernames past the first; // this provides a convenient shortcut to drop everyone else from the conversation. -- cgit From 00b9ba64c92715ba86d45209215694497eecefce Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 5 Jan 2017 14:23:59 +0100 Subject: Fixed unexpected error --- .../components/features/compose/components/compose_form.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'app') diff --git a/app/assets/javascripts/components/features/compose/components/compose_form.jsx b/app/assets/javascripts/components/features/compose/components/compose_form.jsx index 5a76b177b..078bfbdc6 100644 --- a/app/assets/javascripts/components/features/compose/components/compose_form.jsx +++ b/app/assets/javascripts/components/features/compose/components/compose_form.jsx @@ -85,12 +85,12 @@ const ComposeForm = React.createClass({ }, componentDidUpdate (prevProps) { - if ((!prevProps.in_reply_to && this.props.in_reply_to) || prevProps.in_reply_to.get('id') !== this.props.in_reply_to.get('id')) { + if ((prevProps.in_reply_to === null && this.props.in_reply_to !== null) || (prevProps.in_reply_to !== null && this.props.in_reply_to !== null && prevProps.in_reply_to.get('id') !== this.props.in_reply_to.get('id'))) { // If replying to zero or one users, places the cursor at the end of the textbox. // If replying to more than one user, selects any usernames past the first; // this provides a convenient shortcut to drop everyone else from the conversation. const selectionStart = this.props.text.search(/\s/) + 1; - const selectionEnd = this.props.text.length; + const selectionEnd = this.props.text.length; this.autosuggestTextarea.textarea.setSelectionRange(selectionStart, selectionEnd); this.autosuggestTextarea.textarea.focus(); -- cgit From 10a9ebae3b2fc0addb4604798a62d6c4e066d491 Mon Sep 17 00:00:00 2001 From: Effy Elden Date: Fri, 6 Jan 2017 08:26:45 +1100 Subject: Add tag property to desktop notifications, preventing duplicates (i.e. when multiple Mastodon tabs are open) --- app/assets/javascripts/components/actions/notifications.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app') diff --git a/app/assets/javascripts/components/actions/notifications.jsx b/app/assets/javascripts/components/actions/notifications.jsx index 8bd835406..182b598aa 100644 --- a/app/assets/javascripts/components/actions/notifications.jsx +++ b/app/assets/javascripts/components/actions/notifications.jsx @@ -40,7 +40,7 @@ export function updateNotifications(notification, intlMessages, intlLocale) { const title = new IntlMessageFormat(intlMessages[`notification.${notification.type}`], intlLocale).format({ name: notification.account.display_name.length > 0 ? notification.account.display_name : notification.account.username }); const body = $('

').html(notification.status ? notification.status.content : '').text(); - new Notification(title, { body, icon: notification.account.avatar }); + new Notification(title, { body, icon: notification.account.avatar, tag: notification.id }); } }; }; -- cgit From 819bfb75c6135d7acd4d5473f47c4e10497dec2b Mon Sep 17 00:00:00 2001 From: Effy Elden Date: Fri, 6 Jan 2017 09:47:40 +1100 Subject: Add twitter:card metatag to enable Twitter Cards support --- app/views/about/index.html.haml | 1 + app/views/accounts/show.html.haml | 1 + app/views/stream_entries/show.html.haml | 2 ++ 3 files changed, 4 insertions(+) (limited to 'app') diff --git a/app/views/about/index.html.haml b/app/views/about/index.html.haml index 6dd182205..b0260dc51 100644 --- a/app/views/about/index.html.haml +++ b/app/views/about/index.html.haml @@ -9,6 +9,7 @@ %meta{ property: 'og:image', content: asset_url('mastodon_small.jpg') }/ %meta{ property: 'og:image:width', content: '400' }/ %meta{ property: 'og:image:height', content: '400' }/ + %meta{ property: 'twitter:card', content: 'summary' }/ .wrapper %h1 diff --git a/app/views/accounts/show.html.haml b/app/views/accounts/show.html.haml index 7afeb68a9..bc0f08a79 100644 --- a/app/views/accounts/show.html.haml +++ b/app/views/accounts/show.html.haml @@ -12,6 +12,7 @@ %meta{ property: 'og:image', content: full_asset_url(@account.avatar.url(:original)) }/ %meta{ property: 'og:image:width', content: '120' }/ %meta{ property: 'og:image:height', content: '120' }/ + %meta{ property: 'twitter:card', content: 'summary' }/ = render partial: 'header' diff --git a/app/views/stream_entries/show.html.haml b/app/views/stream_entries/show.html.haml index 43935da60..d106173d2 100644 --- a/app/views/stream_entries/show.html.haml +++ b/app/views/stream_entries/show.html.haml @@ -14,5 +14,7 @@ %meta{ property: 'og:image:width', content: '120' }/ %meta{ property: 'og:image:height', content: '120' }/ + %meta{ property: 'twitter:card', content: 'summary' }/ + .activity-stream.activity-stream-headless = render partial: @type, locals: { @type.to_sym => @stream_entry.activity, include_threads: true } -- cgit From 7b9f8766e88dceb9519085deada3fa673e4c015b Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 6 Jan 2017 00:21:12 +0100 Subject: Fix #416 - Generate random unique 14-byte (19 characters) shortcodes for local attachments, use them in URLs. Check status privacy before redirecting to actual file. --- app/controllers/media_controller.rb | 3 ++- app/models/media_attachment.rb | 18 ++++++++++++++++++ ...0170105224407_add_shortcode_to_media_attachments.rb | 14 ++++++++++++++ db/schema.rb | 4 +++- 4 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20170105224407_add_shortcode_to_media_attachments.rb (limited to 'app') diff --git a/app/controllers/media_controller.rb b/app/controllers/media_controller.rb index 6f1f7ec48..488c4f944 100644 --- a/app/controllers/media_controller.rb +++ b/app/controllers/media_controller.rb @@ -10,6 +10,7 @@ class MediaController < ApplicationController private def set_media_attachment - @media_attachment = MediaAttachment.where.not(status_id: nil).find(params[:id]) + @media_attachment = MediaAttachment.where.not(status_id: nil).find_by!(shortcode: params[:id]) + raise ActiveRecord::RecordNotFound unless @media_attachment.status.permitted?(current_account) end end diff --git a/app/models/media_attachment.rb b/app/models/media_attachment.rb index 2a5d23739..ecbed03e3 100644 --- a/app/models/media_attachment.rb +++ b/app/models/media_attachment.rb @@ -16,6 +16,7 @@ class MediaAttachment < ApplicationRecord validates :account, presence: true + scope :local, -> { where(remote_url: '') } default_scope { order('id asc') } def local? @@ -38,6 +39,12 @@ class MediaAttachment < ApplicationRecord image? ? 'image' : 'video' end + def to_param + shortcode + end + + before_create :set_shortcode + class << self private @@ -62,4 +69,15 @@ class MediaAttachment < ApplicationRecord end end end + + private + + def set_shortcode + return unless local? + + loop do + self.shortcode = SecureRandom.urlsafe_base64(14) + break if MediaAttachment.find_by(shortcode: shortcode).nil? + end + end end diff --git a/db/migrate/20170105224407_add_shortcode_to_media_attachments.rb b/db/migrate/20170105224407_add_shortcode_to_media_attachments.rb new file mode 100644 index 000000000..2685ae150 --- /dev/null +++ b/db/migrate/20170105224407_add_shortcode_to_media_attachments.rb @@ -0,0 +1,14 @@ +class AddShortcodeToMediaAttachments < ActiveRecord::Migration[5.0] + def up + add_column :media_attachments, :shortcode, :string, null: true, default: nil + add_index :media_attachments, :shortcode, unique: true + + # Migrate old links + MediaAttachment.local.update_all('shortcode = id') + end + + def down + remove_index :media_attachments, :shortcode + remove_column :media_attachments, :shortcode + end +end diff --git a/db/schema.rb b/db/schema.rb index b9236d42f..a535c5fdb 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20161222204147) do +ActiveRecord::Schema.define(version: 20170105224407) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -95,6 +95,8 @@ ActiveRecord::Schema.define(version: 20161222204147) do t.integer "account_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.string "shortcode" + t.index ["shortcode"], name: "index_media_attachments_on_shortcode", unique: true, using: :btree t.index ["status_id"], name: "index_media_attachments_on_status_id", using: :btree end -- cgit From 18deeb9db58391ed9ecb4d4d48ae47acb3e7fb46 Mon Sep 17 00:00:00 2001 From: Greg V Date: Fri, 6 Jan 2017 18:08:40 +0300 Subject: Add Microformats2 markup h-feed, h-card and h-entry --- app/views/accounts/_header.html.haml | 12 ++++----- app/views/accounts/show.html.haml | 22 ++++++++-------- .../stream_entries/_detailed_status.html.haml | 17 ++++++------ app/views/stream_entries/_simple_status.html.haml | 17 ++++++------ app/views/stream_entries/_status.html.haml | 30 ++++++++++++---------- app/views/tags/show.html.haml | 2 +- 6 files changed, 53 insertions(+), 47 deletions(-) (limited to 'app') diff --git a/app/views/accounts/_header.html.haml b/app/views/accounts/_header.html.haml index 1c6b5f0f6..e410ff72c 100644 --- a/app/views/accounts/_header.html.haml +++ b/app/views/accounts/_header.html.haml @@ -1,4 +1,4 @@ -.card{ style: "background-image: url(#{@account.header.url( :original)})" } +.card.h-card{ class: [local_assigns[:in_feed] && 'p-author'], style: "background-image: url(#{@account.header.url( :original)})" } - if user_signed_in? && current_account.id != @account.id && !current_account.requested?(@account) .controls - if current_account.following?(@account) @@ -9,19 +9,19 @@ .controls .remote-follow = link_to t('accounts.remote_follow'), account_remote_follow_path(@account), class: 'button' - .avatar= image_tag @account.avatar.url(:original) + .avatar= image_tag @account.avatar.url(:original), class: 'u-photo' %h1.name - = display_name(@account) + %span.p-name= display_name(@account) %small - = "@#{@account.username}" + %span.p-nickname= "@#{@account.username}" = fa_icon('lock') if @account.locked? .details .bio - .account__header__content= Formatter.instance.simplified_format(@account) + .account__header__content.p-note= Formatter.instance.simplified_format(@account) .details-counters .counter{ class: active_nav_class(account_url(@account)) } - = link_to account_url(@account) do + = link_to account_url(@account), class: 'u-url u-uid' do %span.counter-label= t('accounts.posts') %span.counter-number= number_with_delimiter @account.statuses.count .counter{ class: active_nav_class(following_account_url(@account)) } diff --git a/app/views/accounts/show.html.haml b/app/views/accounts/show.html.haml index bc0f08a79..8840b5503 100644 --- a/app/views/accounts/show.html.haml +++ b/app/views/accounts/show.html.haml @@ -14,15 +14,17 @@ %meta{ property: 'og:image:height', content: '120' }/ %meta{ property: 'twitter:card', content: 'summary' }/ -= render partial: 'header' +.h-feed + %data.p-name{ value: "#{@account.username} on #{Rails.configuration.x.local_domain}" } + = render partial: 'header', locals: {in_feed: true} -- if @statuses.empty? - .accounts-grid - = render partial: 'nothing_here' -- else - .activity-stream - = render partial: 'stream_entries/status', collection: @statuses, as: :status + - if @statuses.empty? + .accounts-grid + = render partial: 'nothing_here' + - else + .activity-stream + = render partial: 'stream_entries/status', collection: @statuses, as: :status -.pagination - - if @statuses.size == 20 - = link_to safe_join([t('pagination.next'), fa_icon('chevron-right')], ' '), account_url(@account, max_id: @statuses.last.id), class: 'next_page', rel: 'next' + .pagination + - if @statuses.size == 20 + = link_to safe_join([t('pagination.next'), fa_icon('chevron-right')], ' '), account_url(@account, max_id: @statuses.last.id), class: 'next_page', rel: 'next' diff --git a/app/views/stream_entries/_detailed_status.html.haml b/app/views/stream_entries/_detailed_status.html.haml index 94451d3bd..b0d36872c 100644 --- a/app/views/stream_entries/_detailed_status.html.haml +++ b/app/views/stream_entries/_detailed_status.html.haml @@ -1,30 +1,31 @@ .detailed-status.light - = link_to TagManager.instance.url_for(status.account), class: 'detailed-status__display-name', target: @external_links ? '_blank' : nil, rel: 'noopener' do + = link_to TagManager.instance.url_for(status.account), class: 'detailed-status__display-name p-author h-card', target: @external_links ? '_blank' : nil, rel: 'noopener' do %div %div.avatar - = image_tag status.account.avatar.url(:original), width: 48, height: 48, alt: '' + = image_tag status.account.avatar.url(:original), width: 48, height: 48, alt: '', class: 'u-photo' %span.display-name - %strong= display_name(status.account) - %span= acct(status.account) + %strong.p-name= display_name(status.account) + %span.p-nickname= acct(status.account) - .status__content= Formatter.instance.format(status) + .status__content.e-content.p-name= Formatter.instance.format(status) - unless status.media_attachments.empty? - if status.media_attachments.first.video? .video-player - if status.sensitive? = render partial: 'stream_entries/content_spoiler' - %video{ src: status.media_attachments.first.file.url(:original), loop: true } + %video{ src: status.media_attachments.first.file.url(:original), loop: true, class: 'u-video' } - else .detailed-status__attachments - if status.sensitive? = render partial: 'stream_entries/content_spoiler' - status.media_attachments.each do |media| .media-item - = link_to '', (media.remote_url.blank? ? media.file.url(:original) : media.remote_url), style: "background-image: url(#{media.file.url(:original)})", target: '_blank', rel: 'noopener' + = link_to '', (media.remote_url.blank? ? media.file.url(:original) : media.remote_url), style: "background-image: url(#{media.file.url(:original)})", target: '_blank', rel: 'noopener', class: "u-#{media.video? ? 'video' : 'photo'}" %div.detailed-status__meta - = link_to TagManager.instance.url_for(status), class: 'detailed-status__datetime', target: @external_links ? '_blank' : nil, rel: 'noopener' do + %data.dt-published{ value: status.created_at.to_time.iso8601 } + = link_to TagManager.instance.url_for(status), class: 'detailed-status__datetime u-url u-uid', target: @external_links ? '_blank' : nil, rel: 'noopener' do %span= l(status.created_at) · %span diff --git a/app/views/stream_entries/_simple_status.html.haml b/app/views/stream_entries/_simple_status.html.haml index da3bc0ccb..b08cf5dab 100644 --- a/app/views/stream_entries/_simple_status.html.haml +++ b/app/views/stream_entries/_simple_status.html.haml @@ -1,17 +1,18 @@ .status.light .status__header .status__meta - = link_to time_ago_in_words(status.created_at), TagManager.instance.url_for(status), class: 'status__relative-time', title: l(status.created_at), target: @external_links ? '_blank' : nil, rel: 'noopener' + = link_to time_ago_in_words(status.created_at), TagManager.instance.url_for(status), class: 'status__relative-time u-url u-uid', title: l(status.created_at), target: @external_links ? '_blank' : nil, rel: 'noopener' + %data.dt-published{ value: status.created_at.to_time.iso8601 } - = link_to TagManager.instance.url_for(status.account), class: 'status__display-name', target: @external_links ? '_blank' : nil, rel: 'noopener' do + = link_to TagManager.instance.url_for(status.account), class: 'status__display-name p-author h-card', target: @external_links ? '_blank' : nil, rel: 'noopener' do .status__avatar %div - = image_tag status.account.avatar(:original), width: 48, height: 48, alt: '' + = image_tag status.account.avatar(:original), width: 48, height: 48, alt: '', class: 'u-photo' %span.display-name - %strong= display_name(status.account) - %span= acct(status.account) + %strong.p-name= display_name(status.account) + %span.p-nickname= acct(status.account) - .status__content= Formatter.instance.format(status) + .status__content.e-content.p-name= Formatter.instance.format(status) - unless status.media_attachments.empty? .status__attachments @@ -19,10 +20,10 @@ = render partial: 'stream_entries/content_spoiler' - if status.media_attachments.first.video? .video-item - = link_to (status.media_attachments.first.remote_url.blank? ? status.media_attachments.first.file.url(:original) : status.media_attachments.first.remote_url), style: "background-image: url(#{status.media_attachments.first.file.url(:small)})", target: '_blank', rel: 'noopener' do + = link_to (status.media_attachments.first.remote_url.blank? ? status.media_attachments.first.file.url(:original) : status.media_attachments.first.remote_url), style: "background-image: url(#{status.media_attachments.first.file.url(:small)})", target: '_blank', rel: 'noopener', class: 'u-video' do .video-item__play = fa_icon('play') - else - status.media_attachments.each do |media| .media-item - = link_to '', (media.remote_url.blank? ? media.file.url(:original) : media.remote_url), style: "background-image: url(#{media.file.url(:original)})", target: '_blank', rel: 'noopener' + = link_to '', (media.remote_url.blank? ? media.file.url(:original) : media.remote_url), style: "background-image: url(#{media.file.url(:original)})", target: '_blank', rel: 'noopener', class: "u-#{media.video? ? 'video' : 'photo'}" diff --git a/app/views/stream_entries/_status.html.haml b/app/views/stream_entries/_status.html.haml index f70e2c890..eeb2fec00 100644 --- a/app/views/stream_entries/_status.html.haml +++ b/app/views/stream_entries/_status.html.haml @@ -3,20 +3,22 @@ - is_successor ||= false - centered ||= include_threads && !is_predecessor && !is_successor -- if status.reply? && include_threads - = render partial: 'status', collection: @ancestors, as: :status, locals: { is_predecessor: true } +%div{ class: [is_predecessor && 'u-in-reply-to h-cite', is_successor && 'u-comment h-cite', !is_predecessor && !is_successor && 'h-entry'] } + - if status.reply? && include_threads + = render partial: 'status', collection: @ancestors, as: :status, locals: { is_predecessor: true } -.entry{ class: entry_classes(status, is_predecessor, is_successor, include_threads) } - - if status.reblog? - .pre-header - %div.pre-header__icon - = fa_icon('retweet fw') - %span - = link_to TagManager.instance.url_for(status.account), class: 'status__display-name muted' do - %strong= display_name(status.account) - = t('stream_entries.reblogged') + .entry{ class: entry_classes(status, is_predecessor, is_successor, include_threads) } + - if status.reblog? + .pre-header + %div.pre-header__icon + = fa_icon('retweet fw') + %span + = link_to TagManager.instance.url_for(status.account), class: 'status__display-name muted' do + %strong= display_name(status.account) + = t('stream_entries.reblogged') - = render partial: centered ? 'stream_entries/detailed_status' : 'stream_entries/simple_status', locals: { status: proper_status(status) } + %div{ class: [status.reblog? && 'u-repost-of h-cite'] } + = render partial: centered ? 'stream_entries/detailed_status' : 'stream_entries/simple_status', locals: { status: proper_status(status) } -- if include_threads - = render partial: 'status', collection: @descendants, as: :status, locals: { is_successor: true } + - if include_threads + = render partial: 'status', collection: @descendants, as: :status, locals: { is_successor: true } diff --git a/app/views/tags/show.html.haml b/app/views/tags/show.html.haml index dd42fe22c..412ec4fa5 100644 --- a/app/views/tags/show.html.haml +++ b/app/views/tags/show.html.haml @@ -2,7 +2,7 @@ .accounts-grid = render partial: 'accounts/nothing_here' - else - .activity-stream + .activity-stream.h-feed = render partial: 'stream_entries/status', collection: @statuses, as: :status, cached: true .pagination -- cgit From 72c3a41befb6407f21068ef4cb9e34187e3515f0 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 6 Jan 2017 20:15:24 +0100 Subject: Fix h-card classes and remote follow button appearing when it shouldn't --- app/views/accounts/_header.html.haml | 4 ++-- app/views/accounts/show.html.haml | 5 +++-- app/views/stream_entries/_status.html.haml | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) (limited to 'app') diff --git a/app/views/accounts/_header.html.haml b/app/views/accounts/_header.html.haml index e410ff72c..7a5cea7ab 100644 --- a/app/views/accounts/_header.html.haml +++ b/app/views/accounts/_header.html.haml @@ -1,11 +1,11 @@ -.card.h-card{ class: [local_assigns[:in_feed] && 'p-author'], style: "background-image: url(#{@account.header.url( :original)})" } +.card.h-card.p-author{ style: "background-image: url(#{@account.header.url( :original)})" } - if user_signed_in? && current_account.id != @account.id && !current_account.requested?(@account) .controls - if current_account.following?(@account) = link_to t('accounts.unfollow'), unfollow_account_path(@account), data: { method: :post }, class: 'button' - else = link_to t('accounts.follow'), follow_account_path(@account), data: { method: :post }, class: 'button' - - else + - elsif !user_signed_in? .controls .remote-follow = link_to t('accounts.remote_follow'), account_remote_follow_path(@account), class: 'button' diff --git a/app/views/accounts/show.html.haml b/app/views/accounts/show.html.haml index 8840b5503..c194ce33d 100644 --- a/app/views/accounts/show.html.haml +++ b/app/views/accounts/show.html.haml @@ -15,8 +15,9 @@ %meta{ property: 'twitter:card', content: 'summary' }/ .h-feed - %data.p-name{ value: "#{@account.username} on #{Rails.configuration.x.local_domain}" } - = render partial: 'header', locals: {in_feed: true} + %data.p-name{ value: "#{@account.username} on #{Rails.configuration.x.local_domain}" }/ + + = render partial: 'header' - if @statuses.empty? .accounts-grid diff --git a/app/views/stream_entries/_status.html.haml b/app/views/stream_entries/_status.html.haml index eeb2fec00..29dcd6081 100644 --- a/app/views/stream_entries/_status.html.haml +++ b/app/views/stream_entries/_status.html.haml @@ -3,7 +3,7 @@ - is_successor ||= false - centered ||= include_threads && !is_predecessor && !is_successor -%div{ class: [is_predecessor && 'u-in-reply-to h-cite', is_successor && 'u-comment h-cite', !is_predecessor && !is_successor && 'h-entry'] } +%div{ class: [is_predecessor ? 'u-in-reply-to h-cite' : nil, is_successor ? 'u-comment h-cite' : nil, !is_predecessor && !is_successor ? 'h-entry' : nil].compact } - if status.reply? && include_threads = render partial: 'status', collection: @ancestors, as: :status, locals: { is_predecessor: true } @@ -17,7 +17,7 @@ %strong= display_name(status.account) = t('stream_entries.reblogged') - %div{ class: [status.reblog? && 'u-repost-of h-cite'] } + %div{ class: status.reblog? ? 'u-repost-of h-cite' : nil } = render partial: centered ? 'stream_entries/detailed_status' : 'stream_entries/simple_status', locals: { status: proper_status(status) } - if include_threads -- cgit From 1bfbce7b4542739a3601e0722a975b450e86bfb2 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 6 Jan 2017 20:24:51 +0100 Subject: Clean up h-card mess of divs --- app/helpers/stream_entries_helper.rb | 8 ++++---- app/views/stream_entries/_status.html.haml | 30 ++++++++++++++---------------- 2 files changed, 18 insertions(+), 20 deletions(-) (limited to 'app') diff --git a/app/helpers/stream_entries_helper.rb b/app/helpers/stream_entries_helper.rb index ae2f575b5..15601a079 100644 --- a/app/helpers/stream_entries_helper.rb +++ b/app/helpers/stream_entries_helper.rb @@ -15,10 +15,10 @@ module StreamEntriesHelper def entry_classes(status, is_predecessor, is_successor, include_threads) classes = ['entry'] - classes << 'entry-reblog' if status.reblog? - classes << 'entry-predecessor' if is_predecessor - classes << 'entry-successor' if is_successor - classes << 'entry-center' if include_threads + classes << 'entry-reblog u-repost-of h-cite' if status.reblog? + classes << 'entry-predecessor u-in-reply-to h-cite' if is_predecessor + classes << 'entry-successor u-comment h-cite' if is_successor + classes << 'entry-center h-entry' if include_threads classes.join(' ') end diff --git a/app/views/stream_entries/_status.html.haml b/app/views/stream_entries/_status.html.haml index 29dcd6081..f70e2c890 100644 --- a/app/views/stream_entries/_status.html.haml +++ b/app/views/stream_entries/_status.html.haml @@ -3,22 +3,20 @@ - is_successor ||= false - centered ||= include_threads && !is_predecessor && !is_successor -%div{ class: [is_predecessor ? 'u-in-reply-to h-cite' : nil, is_successor ? 'u-comment h-cite' : nil, !is_predecessor && !is_successor ? 'h-entry' : nil].compact } - - if status.reply? && include_threads - = render partial: 'status', collection: @ancestors, as: :status, locals: { is_predecessor: true } +- if status.reply? && include_threads + = render partial: 'status', collection: @ancestors, as: :status, locals: { is_predecessor: true } - .entry{ class: entry_classes(status, is_predecessor, is_successor, include_threads) } - - if status.reblog? - .pre-header - %div.pre-header__icon - = fa_icon('retweet fw') - %span - = link_to TagManager.instance.url_for(status.account), class: 'status__display-name muted' do - %strong= display_name(status.account) - = t('stream_entries.reblogged') +.entry{ class: entry_classes(status, is_predecessor, is_successor, include_threads) } + - if status.reblog? + .pre-header + %div.pre-header__icon + = fa_icon('retweet fw') + %span + = link_to TagManager.instance.url_for(status.account), class: 'status__display-name muted' do + %strong= display_name(status.account) + = t('stream_entries.reblogged') - %div{ class: status.reblog? ? 'u-repost-of h-cite' : nil } - = render partial: centered ? 'stream_entries/detailed_status' : 'stream_entries/simple_status', locals: { status: proper_status(status) } + = render partial: centered ? 'stream_entries/detailed_status' : 'stream_entries/simple_status', locals: { status: proper_status(status) } - - if include_threads - = render partial: 'status', collection: @descendants, as: :status, locals: { is_successor: true } +- if include_threads + = render partial: 'status', collection: @descendants, as: :status, locals: { is_successor: true } -- cgit From 989c3f40022bc65d69915be597acda3c4d58de60 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 6 Jan 2017 22:09:55 +0100 Subject: Add tab bar alternative to desktop UI, upgrade react & react-redux --- .../javascripts/components/containers/mastodon.jsx | 7 +-- .../features/compose/components/drawer.jsx | 73 ++++++++++++++++++---- .../features/compose/components/navigation_bar.jsx | 2 +- .../components/features/compose/index.jsx | 5 +- .../components/features/ui/components/tabs_bar.jsx | 2 +- .../javascripts/components/features/ui/index.jsx | 9 ++- app/assets/stylesheets/components.scss | 22 +++++++ package.json | 9 +-- yarn.lock | 6 +- 9 files changed, 103 insertions(+), 32 deletions(-) (limited to 'app') diff --git a/app/assets/javascripts/components/containers/mastodon.jsx b/app/assets/javascripts/components/containers/mastodon.jsx index 026daeb06..6c0d28053 100644 --- a/app/assets/javascripts/components/containers/mastodon.jsx +++ b/app/assets/javascripts/components/containers/mastodon.jsx @@ -9,7 +9,6 @@ import { import { updateNotifications } from '../actions/notifications'; import { setAccessToken } from '../actions/meta'; import { setAccountSelf } from '../actions/accounts'; -import PureRenderMixin from 'react-addons-pure-render-mixin'; import createBrowserHistory from 'history/lib/createBrowserHistory'; import { applyRouterMiddleware, @@ -63,8 +62,6 @@ const Mastodon = React.createClass({ locale: React.PropTypes.string.isRequired }, - mixins: [PureRenderMixin], - componentWillMount() { const { token, account, locale } = this.props; @@ -108,9 +105,9 @@ const Mastodon = React.createClass({ - + - + diff --git a/app/assets/javascripts/components/features/compose/components/drawer.jsx b/app/assets/javascripts/components/features/compose/components/drawer.jsx index d31d0e453..b694cdd2a 100644 --- a/app/assets/javascripts/components/features/compose/components/drawer.jsx +++ b/app/assets/javascripts/components/features/compose/components/drawer.jsx @@ -1,26 +1,75 @@ -import PureRenderMixin from 'react-addons-pure-render-mixin'; +import { Link } from 'react-router'; +import { injectIntl, defineMessages } from 'react-intl'; -const style = { +const messages = defineMessages({ + start: { id: 'getting_started.heading', defaultMessage: 'Getting started' }, + public: { id: 'navigation_bar.public_timeline', defaultMessage: 'Public timeline' }, + preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' }, + logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' } +}); + +const outerStyle = { + boxSizing: 'border-box', + display: 'flex', + flexDirection: 'column', + overflowY: 'hidden' +}; + +const innerStyle = { boxSizing: 'border-box', - background: '#454b5e', padding: '0', display: 'flex', flexDirection: 'column', - overflowY: 'auto' + overflowY: 'auto', + flexGrow: '1' +}; + +const tabStyle = { + display: 'block', + flex: '1 1 auto', + padding: '15px', + paddingBottom: '13px', + color: '#9baec8', + textDecoration: 'none', + textAlign: 'center', + fontSize: '16px', + borderBottom: '2px solid transparent' }; -const Drawer = React.createClass({ +const tabActiveStyle = { + color: '#2b90d9', + borderBottom: '2px solid #2b90d9' +}; - mixins: [PureRenderMixin], +const Drawer = ({ children, withHeader, intl }) => { + let header = ''; - render () { - return ( -

- {this.props.children} + if (withHeader) { + header = ( +
+ + + +
); } -}); + return ( +
+ {header} + +
+ {children} +
+
+ ); +}; + +Drawer.propTypes = { + withHeader: React.PropTypes.bool, + children: React.PropTypes.node, + intl: React.PropTypes.object +}; -export default Drawer; +export default injectIntl(Drawer); diff --git a/app/assets/javascripts/components/features/compose/components/navigation_bar.jsx b/app/assets/javascripts/components/features/compose/components/navigation_bar.jsx index 71b50fc3a..289e2dddf 100644 --- a/app/assets/javascripts/components/features/compose/components/navigation_bar.jsx +++ b/app/assets/javascripts/components/features/compose/components/navigation_bar.jsx @@ -21,7 +21,7 @@ const NavigationBar = React.createClass({
{this.props.account.get('acct')} - · +
); diff --git a/app/assets/javascripts/components/features/compose/index.jsx b/app/assets/javascripts/components/features/compose/index.jsx index 4017c8949..f6095c0c6 100644 --- a/app/assets/javascripts/components/features/compose/index.jsx +++ b/app/assets/javascripts/components/features/compose/index.jsx @@ -10,7 +10,8 @@ import { mountCompose, unmountCompose } from '../../actions/compose'; const Compose = React.createClass({ propTypes: { - dispatch: React.PropTypes.func.isRequired + dispatch: React.PropTypes.func.isRequired, + withHeader: React.PropTypes.bool }, mixins: [PureRenderMixin], @@ -25,7 +26,7 @@ const Compose = React.createClass({ render () { return ( - + diff --git a/app/assets/javascripts/components/features/ui/components/tabs_bar.jsx b/app/assets/javascripts/components/features/ui/components/tabs_bar.jsx index aa40a488f..2f8a28fad 100644 --- a/app/assets/javascripts/components/features/ui/components/tabs_bar.jsx +++ b/app/assets/javascripts/components/features/ui/components/tabs_bar.jsx @@ -30,7 +30,7 @@ const TabsBar = () => { - +
); }; 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 = ( - + {this.props.children} diff --git a/app/assets/stylesheets/components.scss b/app/assets/stylesheets/components.scss index c2fcd76b3..69595995c 100644 --- a/app/assets/stylesheets/components.scss +++ b/app/assets/stylesheets/components.scss @@ -349,6 +349,28 @@ width: 280px; } +.drawer__inner { + background: linear-gradient(rgba(69, 75, 94, 1), rgba(69, 75, 94, 0.65)); +} + +.drawer__header { + flex: 0 0 auto; + font-size: 16px; + background: darken(#454b5e, 5%); + margin-bottom: 10px; + display: flex; + flex-direction: row; + + a { + transition: all 100ms ease-in; + + &:hover { + background: darken(#454b5e, 10%); + transition: all 200ms ease-out; + } + } +} + .column, .drawer { margin-left: 5px; margin-right: 5px; diff --git a/package.json b/package.json index 3a9365d3e..6a072ca06 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "chai": "^3.5.0", "chai-enzyme": "^0.5.2", "css-loader": "^0.26.1", - "emojione": "^2.2.6", + "emojione": "latest", "enzyme": "^2.4.1", "es6-promise": "^3.2.1", "http-link-header": "^0.5.0", @@ -39,22 +39,19 @@ "react-motion": "^0.4.5", "react-notification": "^6.4.0", "react-proxy": "^1.1.8", - "react-redux": "^5.0.0-beta.3", + "react-redux": "^5.0.1", "react-redux-loading-bar": "^2.4.1", "react-router": "^2.8.0", "react-router-scroll": "^0.3.2", "react-simple-dropdown": "^1.1.4", "react-storybook-addon-intl": "^0.1.0", "react-toggle": "^2.1.1", - "redux": "^3.5.2", + "redux": "^3.6.0", "redux-immutable": "^3.0.8", "redux-thunk": "^2.1.0", "reselect": "^2.5.4", "sass-loader": "^4.0.2", "sinon": "^1.17.6", "style-loader": "^0.13.1" - }, - "dependencies": { - "emojione": "latest" } } diff --git a/yarn.lock b/yarn.lock index db5f7d408..948de9ba8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4301,9 +4301,9 @@ react-redux@^4.4.5: lodash "^4.2.0" loose-envify "^1.1.0" -react-redux@^5.0.0-beta.3: - version "5.0.0-beta.3" - resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.0.0-beta.3.tgz#d50bfb00799cf7d2a9fd55fe34d6b3ecc24d3072" +react-redux@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.0.1.tgz#84a41bd4cdd180452bb6922bc79ad25bd5abb7c4" dependencies: hoist-non-react-statics "^1.0.3" invariant "^2.0.0" -- cgit From 5c7add21761fc6b7d3c0af0819865242ce381960 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 7 Jan 2017 15:44:22 +0100 Subject: Fix #147 - Unreblogging will leave original status in feeds --- app/assets/javascripts/components/actions/timelines.jsx | 4 +++- app/assets/javascripts/components/reducers/timelines.jsx | 9 +++++++-- app/services/remove_status_service.rb | 7 ++++++- 3 files changed, 16 insertions(+), 4 deletions(-) (limited to 'app') diff --git a/app/assets/javascripts/components/actions/timelines.jsx b/app/assets/javascripts/components/actions/timelines.jsx index 0e6f09190..8bb939d31 100644 --- a/app/assets/javascripts/components/actions/timelines.jsx +++ b/app/assets/javascripts/components/actions/timelines.jsx @@ -39,12 +39,14 @@ export function deleteFromTimelines(id) { return (dispatch, getState) => { const accountId = getState().getIn(['statuses', id, 'account']); const references = getState().get('statuses').filter(status => status.get('reblog') === id).map(status => [status.get('id'), status.get('account')]); + const reblogOf = getState().getIn(['statuses', id, 'reblog'], null); dispatch({ type: TIMELINE_DELETE, id, accountId, - references + references, + reblogOf }); }; }; diff --git a/app/assets/javascripts/components/reducers/timelines.jsx b/app/assets/javascripts/components/reducers/timelines.jsx index b73c83e0f..c5e3a253f 100644 --- a/app/assets/javascripts/components/reducers/timelines.jsx +++ b/app/assets/javascripts/components/reducers/timelines.jsx @@ -145,7 +145,12 @@ const updateTimeline = (state, timeline, status, references) => { return state; }; -const deleteStatus = (state, id, accountId, references) => { +const deleteStatus = (state, id, accountId, references, reblogOf) => { + if (reblogOf) { + // If we are deleting a reblog, just replace reblog with its original + return state.updateIn(['home', 'items'], list => list.map(item => item === id ? reblogOf : item)); + } + // Remove references from timelines ['home', 'mentions', 'public', 'tag'].forEach(function (timeline) { state = state.updateIn([timeline, 'items'], list => list.filterNot(item => item === id)); @@ -220,7 +225,7 @@ export default function timelines(state = initialState, action) { case TIMELINE_UPDATE: return updateTimeline(state, action.timeline, Immutable.fromJS(action.status), action.references); case TIMELINE_DELETE: - return deleteStatus(state, action.id, action.accountId, action.references); + return deleteStatus(state, action.id, action.accountId, action.references, action.reblogOf); case CONTEXT_FETCH_SUCCESS: return normalizeContext(state, action.id, Immutable.fromJS(action.ancestors), Immutable.fromJS(action.descendants)); case ACCOUNT_TIMELINE_FETCH_SUCCESS: diff --git a/app/services/remove_status_service.rb b/app/services/remove_status_service.rb index 836b8fdc5..7aca24d12 100644 --- a/app/services/remove_status_service.rb +++ b/app/services/remove_status_service.rb @@ -53,7 +53,12 @@ class RemoveStatusService < BaseService end def unpush(type, receiver, status) - redis.zremrangebyscore(FeedManager.instance.key(type, receiver.id), status.id, status.id) + if status.reblog? + redis.zadd(FeedManager.instance.key(type, receiver.id), status.reblog_of_id, status.reblog_of_id) + else + redis.zremrangebyscore(FeedManager.instance.key(type, receiver.id), status.id, status.id) + end + FeedManager.instance.broadcast(receiver.id, type: 'delete', id: status.id) end -- cgit From be6ae3546f1673e497e5d9e74e5a7dfad9f1ff72 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 7 Jan 2017 15:46:39 +0100 Subject: Replace getting started icon on desktop nav bar with asterisk instead of hamburger --- .../javascripts/components/features/compose/components/drawer.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app') diff --git a/app/assets/javascripts/components/features/compose/components/drawer.jsx b/app/assets/javascripts/components/features/compose/components/drawer.jsx index b694cdd2a..d0e865d29 100644 --- a/app/assets/javascripts/components/features/compose/components/drawer.jsx +++ b/app/assets/javascripts/components/features/compose/components/drawer.jsx @@ -47,7 +47,7 @@ const Drawer = ({ children, withHeader, intl }) => { if (withHeader) { header = (
- + -- cgit From 61211b509c935e0ecc50809f8bb9bea08b7cbfb9 Mon Sep 17 00:00:00 2001 From: blackle Date: Sat, 7 Jan 2017 16:39:30 -0500 Subject: Improve contrast of bio and username text in account info --- .../javascripts/components/features/account/components/header.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'app') diff --git a/app/assets/javascripts/components/features/account/components/header.jsx b/app/assets/javascripts/components/features/account/components/header.jsx index 6ae5ac002..dead11265 100644 --- a/app/assets/javascripts/components/features/account/components/header.jsx +++ b/app/assets/javascripts/components/features/account/components/header.jsx @@ -71,8 +71,8 @@ const Header = React.createClass({ - @{account.get('acct')} {lockedIcon} -
+ @{account.get('acct')} {lockedIcon} +
{info} {actionBtn} -- cgit From 57ff221c0fb2424a7ce5eea043e54bb31725ef7c Mon Sep 17 00:00:00 2001 From: blackle Date: Sat, 7 Jan 2017 18:16:14 -0500 Subject: Emojify display names in notifcations --- .../components/features/notifications/components/notification.jsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'app') diff --git a/app/assets/javascripts/components/features/notifications/components/notification.jsx b/app/assets/javascripts/components/features/notifications/components/notification.jsx index 37715dd05..140ba9134 100644 --- a/app/assets/javascripts/components/features/notifications/components/notification.jsx +++ b/app/assets/javascripts/components/features/notifications/components/notification.jsx @@ -4,6 +4,8 @@ import StatusContainer from '../../../containers/status_container'; import AccountContainer from '../../../containers/account_container'; import { FormattedMessage } from 'react-intl'; import Permalink from '../../../components/permalink'; +import emojify from '../../../emoji'; +import escapeTextContentForBrowser from 'react/lib/escapeTextContentForBrowser'; const messageStyle = { marginLeft: '68px', @@ -83,7 +85,8 @@ const Notification = React.createClass({ const { notification } = this.props; const account = notification.get('account'); const displayName = account.get('display_name').length > 0 ? account.get('display_name') : account.get('username'); - const link = {displayName}; + const displayNameHTML = { __html: emojify(escapeTextContentForBrowser(displayName)) }; + const link = ; switch(notification.get('type')) { case 'follow': -- cgit From e1ca3549561b133a5c878f5f8ace2c8d2dec4eee Mon Sep 17 00:00:00 2001 From: blackle Date: Sat, 7 Jan 2017 18:41:57 -0500 Subject: Show logout button on getting started so it's available on mobile --- app/assets/javascripts/components/features/getting_started/index.jsx | 4 +++- .../javascripts/components/features/ui/components/column_link.jsx | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'app') diff --git a/app/assets/javascripts/components/features/getting_started/index.jsx b/app/assets/javascripts/components/features/getting_started/index.jsx index 8f56ba39f..8b4096b34 100644 --- a/app/assets/javascripts/components/features/getting_started/index.jsx +++ b/app/assets/javascripts/components/features/getting_started/index.jsx @@ -9,7 +9,8 @@ const messages = defineMessages({ heading: { id: 'getting_started.heading', defaultMessage: 'Getting started' }, public_timeline: { id: 'navigation_bar.public_timeline', defaultMessage: 'Public timeline' }, preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' }, - follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' } + follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' }, + sign_out: { id: 'navigation_bar.logout', defaultMessage: 'Sign out' } }); const mapStateToProps = state => ({ @@ -28,6 +29,7 @@ const GettingStarted = ({ intl, me }) => {
+ {followRequests}
diff --git a/app/assets/javascripts/components/features/ui/components/column_link.jsx b/app/assets/javascripts/components/features/ui/components/column_link.jsx index a2f7c13a6..901a29f5c 100644 --- a/app/assets/javascripts/components/features/ui/components/column_link.jsx +++ b/app/assets/javascripts/components/features/ui/components/column_link.jsx @@ -13,10 +13,10 @@ const iconStyle = { marginRight: '5px' }; -const ColumnLink = ({ icon, text, to, href }) => { +const ColumnLink = ({ icon, text, to, href, method }) => { if (href) { return ( - + {text} -- cgit From 8a571158c92bfc8ba2dde9dd5a237be247cc4b74 Mon Sep 17 00:00:00 2001 From: blackle Date: Sat, 7 Jan 2017 20:21:50 -0500 Subject: Show correct volume icons on video player --- app/assets/javascripts/components/components/video_player.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app') diff --git a/app/assets/javascripts/components/components/video_player.jsx b/app/assets/javascripts/components/components/video_player.jsx index 8f64ad3cd..bd6e746ef 100644 --- a/app/assets/javascripts/components/components/video_player.jsx +++ b/app/assets/javascripts/components/components/video_player.jsx @@ -113,7 +113,7 @@ const VideoPlayer = React.createClass({ return (
-
+
); -- cgit From e5d9009d710ae6516e45789059e45118e9a0cb5d Mon Sep 17 00:00:00 2001 From: Kurtis Rainbolt-Greene Date: Sat, 7 Jan 2017 17:55:40 -0800 Subject: Using "rails style" order arguments --- app/models/account.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'app') diff --git a/app/models/account.rb b/app/models/account.rb index 5c1f6e7c1..41fcc864b 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -62,8 +62,8 @@ class Account < ApplicationRecord scope :expiring, ->(time) { where(subscription_expires_at: nil).or(where('subscription_expires_at < ?', time)).remote.with_followers } scope :silenced, -> { where(silenced: true) } scope :suspended, -> { where(suspended: true) } - scope :recent, -> { reorder('id desc') } - scope :alphabetic, -> { order('domain ASC, username ASC') } + scope :recent, -> { reorder(id: :desc) } + scope :alphabetic, -> { order(domain: :asc, username: :asc) } def follow!(other_account) active_relationships.where(target_account: other_account).first_or_create!(target_account: other_account) -- cgit From 4a7e3e5082ed2a974a3938e8fb404445bd1d08f9 Mon Sep 17 00:00:00 2001 From: Kurtis Rainbolt-Greene Date: Sat, 7 Jan 2017 17:59:51 -0800 Subject: Less complicated determination --- app/models/account.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app') diff --git a/app/models/account.rb b/app/models/account.rb index 41fcc864b..db110a9e1 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -104,7 +104,7 @@ class Account < ApplicationRecord end def subscribed? - !subscription_expires_at.nil? + subscription_expires_at end def favourited?(status) -- cgit From abe3ae1cc2c21243b570dbe34f54ea7b568cc629 Mon Sep 17 00:00:00 2001 From: Kurtis Rainbolt-Greene Date: Sat, 7 Jan 2017 18:09:00 -0800 Subject: Simplifying followers mappings Deduplicating some logic and switching to pluck, to avoid pulling the entire model in memory. --- app/models/account.rb | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'app') diff --git a/app/models/account.rb b/app/models/account.rb index 41fcc864b..1b87210c7 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -175,19 +175,23 @@ class Account < ApplicationRecord end def following_map(target_account_ids, account_id) - Follow.where(target_account_id: target_account_ids).where(account_id: account_id).map { |f| [f.target_account_id, true] }.to_h + follow_mapping(Follow.where(target_account_id: target_account_ids, account_id: account_id), :target_account_id) end def followed_by_map(target_account_ids, account_id) - Follow.where(account_id: target_account_ids).where(target_account_id: account_id).map { |f| [f.account_id, true] }.to_h + follow_mapping(Follow.where(account_id: target_account_ids, target_account_id: account_id), :account_id) end def blocking_map(target_account_ids, account_id) - Block.where(target_account_id: target_account_ids).where(account_id: account_id).map { |b| [b.target_account_id, true] }.to_h + follow_mapping(Block.where(target_account_id: target_account_ids, account_id: account_id), :target_account_id) end def requested_map(target_account_ids, account_id) - FollowRequest.where(target_account_id: target_account_ids).where(account_id: account_id).map { |r| [r.target_account_id, true] }.to_h + follow_mapping(FollowRequest.where(target_account_id: target_account_ids, account_id: account_id), :target_account_id) + end + + private def follow_mapping(query, field) + query.pluck(field).inject({}) { |mapping, id| mapping[id] = true } end end -- cgit From b4f09bae1dced6f4877567f9e5e68dab6a6609b0 Mon Sep 17 00:00:00 2001 From: blackle Date: Sun, 8 Jan 2017 00:23:22 -0500 Subject: Add call-to developers on getting started page --- app/assets/javascripts/components/features/getting_started/index.jsx | 1 + app/assets/javascripts/components/locales/en.jsx | 1 + 2 files changed, 2 insertions(+) (limited to 'app') diff --git a/app/assets/javascripts/components/features/getting_started/index.jsx b/app/assets/javascripts/components/features/getting_started/index.jsx index 8b4096b34..58623fbba 100644 --- a/app/assets/javascripts/components/features/getting_started/index.jsx +++ b/app/assets/javascripts/components/features/getting_started/index.jsx @@ -38,6 +38,7 @@ const GettingStarted = ({ intl, me }) => {

+

tootsuite/mastodon }} />

diff --git a/app/assets/javascripts/components/locales/en.jsx b/app/assets/javascripts/components/locales/en.jsx index 05fb0243c..b166c48ba 100644 --- a/app/assets/javascripts/components/locales/en.jsx +++ b/app/assets/javascripts/components/locales/en.jsx @@ -27,6 +27,7 @@ const en = { "getting_started.about_addressing": "You can follow people if you know their username and the domain they are on by entering an e-mail-esque address into the search form.", "getting_started.about_shortcuts": "If the target user is on the same domain as you, just the username will work. The same rule applies to mentioning people in statuses.", "getting_started.about_developer": "The developer of this project can be followed as Gargron@mastodon.social", + "getting_started.open_source_notice": "Mastodon is open source software. You can contribute or report issues on github at {github}.", "column.home": "Home", "column.mentions": "Mentions", "column.public": "Public", -- cgit From 05e964688d1cfb0e485eb275ba92fb7d2ccf4510 Mon Sep 17 00:00:00 2001 From: blackle Date: Sun, 8 Jan 2017 06:32:37 -0500 Subject: Expand dropdown leftways on status view --- app/assets/javascripts/components/components/dropdown_menu.jsx | 6 ++++-- .../javascripts/components/components/status_action_bar.jsx | 2 +- .../components/features/account/components/action_bar.jsx | 2 +- .../components/features/status/components/action_bar.jsx | 2 +- app/assets/stylesheets/components.scss | 9 +++++++++ 5 files changed, 16 insertions(+), 5 deletions(-) (limited to 'app') diff --git a/app/assets/javascripts/components/components/dropdown_menu.jsx b/app/assets/javascripts/components/components/dropdown_menu.jsx index 450550d55..ffef29c00 100644 --- a/app/assets/javascripts/components/components/dropdown_menu.jsx +++ b/app/assets/javascripts/components/components/dropdown_menu.jsx @@ -1,13 +1,15 @@ import Dropdown, { DropdownTrigger, DropdownContent } from 'react-simple-dropdown'; -const DropdownMenu = ({ icon, items, size }) => { +const DropdownMenu = ({ icon, items, size, direction }) => { + const directionClass = (direction == "left") ? "dropdown__left" : "dropdown__right"; + return ( - +
); diff --git a/app/assets/javascripts/components/features/account/components/action_bar.jsx b/app/assets/javascripts/components/features/account/components/action_bar.jsx index 45de75d97..ab7b08dc7 100644 --- a/app/assets/javascripts/components/features/account/components/action_bar.jsx +++ b/app/assets/javascripts/components/features/account/components/action_bar.jsx @@ -66,7 +66,7 @@ const ActionBar = React.createClass({ return (
- +
diff --git a/app/assets/javascripts/components/features/status/components/action_bar.jsx b/app/assets/javascripts/components/features/status/components/action_bar.jsx index 030428440..d989a1693 100644 --- a/app/assets/javascripts/components/features/status/components/action_bar.jsx +++ b/app/assets/javascripts/components/features/status/components/action_bar.jsx @@ -62,7 +62,7 @@ const ActionBar = React.createClass({
-
+
); } diff --git a/app/assets/stylesheets/components.scss b/app/assets/stylesheets/components.scss index 69595995c..0abe8c808 100644 --- a/app/assets/stylesheets/components.scss +++ b/app/assets/stylesheets/components.scss @@ -641,3 +641,12 @@ background: image-url('mastodon-getting-started.png') no-repeat 0 100% local; height: 100%; } + +.dropdown__content.dropdown__left { + transform: translateX(-108px); + + &::before { + right: 8px !important; + left: initial !important; + } +} \ No newline at end of file -- cgit From 2e71bb031b2dff90a2b0f9854bdcd804c069268a Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 8 Jan 2017 19:12:54 +0100 Subject: Fix Paperclip timeout setting. Fix bug introduced in #437 --- app/models/account.rb | 6 ++++-- config/initializers/paperclip.rb | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'app') diff --git a/app/models/account.rb b/app/models/account.rb index 3a7763a64..ba24cf153 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -190,8 +190,10 @@ class Account < ApplicationRecord follow_mapping(FollowRequest.where(target_account_id: target_account_ids, account_id: account_id), :target_account_id) end - private def follow_mapping(query, field) - query.pluck(field).inject({}) { |mapping, id| mapping[id] = true } + private + + def follow_mapping(query, field) + query.pluck(field).inject({}) { |mapping, id| mapping[id] = true; mapping } end end diff --git a/config/initializers/paperclip.rb b/config/initializers/paperclip.rb index 4d6154562..999ff47c7 100644 --- a/config/initializers/paperclip.rb +++ b/config/initializers/paperclip.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -Paperclip::Attachment.default_options[:read_timeout] = 60 +Paperclip.options[:read_timeout] = 60 if ENV['S3_ENABLED'] == 'true' Aws.eager_autoload!(services: %w(S3)) -- cgit From d64c454cfe0db2e0f8205e37be4b0161309c5c2c Mon Sep 17 00:00:00 2001 From: blackle 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') 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 = ( {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 131bae89fd3ea601b032c9650edb3693801c9467 Mon Sep 17 00:00:00 2001 From: blackle Date: Sun, 8 Jan 2017 21:00:13 -0500 Subject: Generate key for each input so we can upload the same file even after deleting --- .../components/features/compose/components/upload_button.jsx | 5 +++-- .../features/compose/containers/upload_button_container.jsx | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'app') diff --git a/app/assets/javascripts/components/features/compose/components/upload_button.jsx b/app/assets/javascripts/components/features/compose/components/upload_button.jsx index 5250ff748..f00ef3f8f 100644 --- a/app/assets/javascripts/components/features/compose/components/upload_button.jsx +++ b/app/assets/javascripts/components/features/compose/components/upload_button.jsx @@ -11,7 +11,8 @@ const UploadButton = React.createClass({ propTypes: { disabled: React.PropTypes.bool, onSelectFile: React.PropTypes.func.isRequired, - style: React.PropTypes.object + style: React.PropTypes.object, + key: React.PropTypes.number }, mixins: [PureRenderMixin], @@ -36,7 +37,7 @@ const UploadButton = React.createClass({ return (
- +
); } diff --git a/app/assets/javascripts/components/features/compose/containers/upload_button_container.jsx b/app/assets/javascripts/components/features/compose/containers/upload_button_container.jsx index 4154b0737..7afa7d355 100644 --- a/app/assets/javascripts/components/features/compose/containers/upload_button_container.jsx +++ b/app/assets/javascripts/components/features/compose/containers/upload_button_container.jsx @@ -4,6 +4,7 @@ import { uploadCompose } from '../../../actions/compose'; const mapStateToProps = state => ({ disabled: state.getIn(['compose', 'is_uploading']) || (state.getIn(['compose', 'media_attachments']).size > 3 || state.getIn(['compose', 'media_attachments']).some(m => m.get('type') === 'video')), + key: Math.floor((Math.random() * 0x10000)) }); const mapDispatchToProps = dispatch => ({ -- cgit From 23ebf60b95984764992c4b356048786ed0ab2953 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 9 Jan 2017 12:37:15 +0100 Subject: Improve initialState loading --- .../javascripts/components/actions/accounts.jsx | 9 --- app/assets/javascripts/components/actions/meta.jsx | 8 --- .../javascripts/components/actions/store.jsx | 17 ++++++ .../javascripts/components/containers/mastodon.jsx | 28 ++++----- .../compose/containers/navigation_container.jsx | 8 ++- .../javascripts/components/reducers/accounts.jsx | 69 +++++++++++----------- .../javascripts/components/reducers/compose.jsx | 8 +-- .../javascripts/components/reducers/meta.jsx | 18 +++--- .../components/store/configureStore.jsx | 13 ++-- app/helpers/home_helper.rb | 2 - app/views/home/index.html.haml | 18 ++++++ 11 files changed, 108 insertions(+), 90 deletions(-) delete mode 100644 app/assets/javascripts/components/actions/meta.jsx create mode 100644 app/assets/javascripts/components/actions/store.jsx (limited to 'app') diff --git a/app/assets/javascripts/components/actions/accounts.jsx b/app/assets/javascripts/components/actions/accounts.jsx index 8d28b051f..7ae87f30e 100644 --- a/app/assets/javascripts/components/actions/accounts.jsx +++ b/app/assets/javascripts/components/actions/accounts.jsx @@ -1,8 +1,6 @@ import api, { getLinks } from '../api' import Immutable from 'immutable'; -export const ACCOUNT_SET_SELF = 'ACCOUNT_SET_SELF'; - export const ACCOUNT_FETCH_REQUEST = 'ACCOUNT_FETCH_REQUEST'; export const ACCOUNT_FETCH_SUCCESS = 'ACCOUNT_FETCH_SUCCESS'; export const ACCOUNT_FETCH_FAIL = 'ACCOUNT_FETCH_FAIL'; @@ -67,13 +65,6 @@ export const FOLLOW_REQUEST_REJECT_REQUEST = 'FOLLOW_REQUEST_REJECT_REQUEST'; export const FOLLOW_REQUEST_REJECT_SUCCESS = 'FOLLOW_REQUEST_REJECT_SUCCESS'; export const FOLLOW_REQUEST_REJECT_FAIL = 'FOLLOW_REQUEST_REJECT_FAIL'; -export function setAccountSelf(account) { - return { - type: ACCOUNT_SET_SELF, - account - }; -}; - export function fetchAccount(id) { return (dispatch, getState) => { dispatch(fetchAccountRequest(id)); diff --git a/app/assets/javascripts/components/actions/meta.jsx b/app/assets/javascripts/components/actions/meta.jsx deleted file mode 100644 index d0adbce3f..000000000 --- a/app/assets/javascripts/components/actions/meta.jsx +++ /dev/null @@ -1,8 +0,0 @@ -export const ACCESS_TOKEN_SET = 'ACCESS_TOKEN_SET'; - -export function setAccessToken(token) { - return { - type: ACCESS_TOKEN_SET, - token: token - }; -}; diff --git a/app/assets/javascripts/components/actions/store.jsx b/app/assets/javascripts/components/actions/store.jsx new file mode 100644 index 000000000..3bba99549 --- /dev/null +++ b/app/assets/javascripts/components/actions/store.jsx @@ -0,0 +1,17 @@ +import Immutable from 'immutable'; + +export const STORE_HYDRATE = 'STORE_HYDRATE'; + +const convertState = rawState => + Immutable.fromJS(rawState, (k, v) => + Immutable.Iterable.isIndexed(v) ? v.toList() : v.toMap().mapKeys(x => + Number.isNaN(x * 1) ? x : x * 1)); + +export function hydrateStore(rawState) { + const state = convertState(rawState); + + return { + type: STORE_HYDRATE, + state + }; +}; diff --git a/app/assets/javascripts/components/containers/mastodon.jsx b/app/assets/javascripts/components/containers/mastodon.jsx index 6c0d28053..143a280c3 100644 --- a/app/assets/javascripts/components/containers/mastodon.jsx +++ b/app/assets/javascripts/components/containers/mastodon.jsx @@ -7,8 +7,6 @@ import { refreshTimeline } from '../actions/timelines'; import { updateNotifications } from '../actions/notifications'; -import { setAccessToken } from '../actions/meta'; -import { setAccountSelf } from '../actions/accounts'; import createBrowserHistory from 'history/lib/createBrowserHistory'; import { applyRouterMiddleware, @@ -44,9 +42,12 @@ import pt from 'react-intl/locale-data/pt'; import hu from 'react-intl/locale-data/hu'; import uk from 'react-intl/locale-data/uk'; import getMessagesForLocale from '../locales'; +import { hydrateStore } from '../actions/store'; const store = configureStore(); +store.dispatch(hydrateStore(window.INITIAL_STATE)); + const browserHistory = useRouterHistory(createBrowserHistory)({ basename: '/web' }); @@ -56,29 +57,26 @@ addLocaleData([...en, ...de, ...es, ...fr, ...pt, ...hu, ...uk]); const Mastodon = React.createClass({ propTypes: { - token: React.PropTypes.string.isRequired, - timelines: React.PropTypes.object, - account: React.PropTypes.string, locale: React.PropTypes.string.isRequired }, componentWillMount() { - const { token, account, locale } = this.props; - - store.dispatch(setAccessToken(token)); - store.dispatch(setAccountSelf(JSON.parse(account))); + const { locale } = this.props; if (typeof App !== 'undefined') { this.subscription = App.cable.subscriptions.create('TimelineChannel', { received (data) { switch(data.type) { - case 'update': - return store.dispatch(updateTimeline(data.timeline, JSON.parse(data.message))); - case 'delete': - return store.dispatch(deleteFromTimelines(data.id)); - case 'notification': - return store.dispatch(updateNotifications(JSON.parse(data.message), getMessagesForLocale(locale), locale)); + case 'update': + store.dispatch(updateTimeline(data.timeline, JSON.parse(data.message))); + break; + case 'delete': + store.dispatch(deleteFromTimelines(data.id)); + break; + case 'notification': + store.dispatch(updateNotifications(JSON.parse(data.message), getMessagesForLocale(locale), locale)); + break; } } diff --git a/app/assets/javascripts/components/features/compose/containers/navigation_container.jsx b/app/assets/javascripts/components/features/compose/containers/navigation_container.jsx index 51e2513d8..0006608da 100644 --- a/app/assets/javascripts/components/features/compose/containers/navigation_container.jsx +++ b/app/assets/javascripts/components/features/compose/containers/navigation_container.jsx @@ -1,8 +1,10 @@ import { connect } from 'react-redux'; import NavigationBar from '../components/navigation_bar'; -const mapStateToProps = (state, props) => ({ - account: state.getIn(['accounts', state.getIn(['meta', 'me'])]) -}); +const mapStateToProps = (state, props) => { + return { + account: state.getIn(['accounts', state.getIn(['meta', 'me'])]) + }; +}; export default connect(mapStateToProps)(NavigationBar); diff --git a/app/assets/javascripts/components/reducers/accounts.jsx b/app/assets/javascripts/components/reducers/accounts.jsx index 7f2f89d0a..ae048df3b 100644 --- a/app/assets/javascripts/components/reducers/accounts.jsx +++ b/app/assets/javascripts/components/reducers/accounts.jsx @@ -1,5 +1,4 @@ import { - ACCOUNT_SET_SELF, ACCOUNT_FETCH_SUCCESS, FOLLOWERS_FETCH_SUCCESS, FOLLOWERS_EXPAND_SUCCESS, @@ -33,6 +32,7 @@ import { NOTIFICATIONS_REFRESH_SUCCESS, NOTIFICATIONS_EXPAND_SUCCESS } from '../actions/notifications'; +import { STORE_HYDRATE } from '../actions/store'; import Immutable from 'immutable'; const normalizeAccount = (state, account) => state.set(account.id, Immutable.fromJS(account)); @@ -67,38 +67,39 @@ const initialState = Immutable.Map(); export default function accounts(state = initialState, action) { switch(action.type) { - case ACCOUNT_SET_SELF: - case ACCOUNT_FETCH_SUCCESS: - case NOTIFICATIONS_UPDATE: - return normalizeAccount(state, action.account); - case FOLLOWERS_FETCH_SUCCESS: - case FOLLOWERS_EXPAND_SUCCESS: - case FOLLOWING_FETCH_SUCCESS: - case FOLLOWING_EXPAND_SUCCESS: - case REBLOGS_FETCH_SUCCESS: - case FAVOURITES_FETCH_SUCCESS: - case COMPOSE_SUGGESTIONS_READY: - case SEARCH_SUGGESTIONS_READY: - case FOLLOW_REQUESTS_FETCH_SUCCESS: - return normalizeAccounts(state, action.accounts); - case NOTIFICATIONS_REFRESH_SUCCESS: - case NOTIFICATIONS_EXPAND_SUCCESS: - return normalizeAccountsFromStatuses(normalizeAccounts(state, action.accounts), action.statuses); - case TIMELINE_REFRESH_SUCCESS: - case TIMELINE_EXPAND_SUCCESS: - case ACCOUNT_TIMELINE_FETCH_SUCCESS: - case ACCOUNT_TIMELINE_EXPAND_SUCCESS: - case CONTEXT_FETCH_SUCCESS: - return normalizeAccountsFromStatuses(state, action.statuses); - case REBLOG_SUCCESS: - case FAVOURITE_SUCCESS: - case UNREBLOG_SUCCESS: - case UNFAVOURITE_SUCCESS: - return normalizeAccountFromStatus(state, action.response); - case TIMELINE_UPDATE: - case STATUS_FETCH_SUCCESS: - return normalizeAccountFromStatus(state, action.status); - default: - return state; + case STORE_HYDRATE: + return state.merge(action.state.get('accounts')); + case ACCOUNT_FETCH_SUCCESS: + case NOTIFICATIONS_UPDATE: + return normalizeAccount(state, action.account); + case FOLLOWERS_FETCH_SUCCESS: + case FOLLOWERS_EXPAND_SUCCESS: + case FOLLOWING_FETCH_SUCCESS: + case FOLLOWING_EXPAND_SUCCESS: + case REBLOGS_FETCH_SUCCESS: + case FAVOURITES_FETCH_SUCCESS: + case COMPOSE_SUGGESTIONS_READY: + case SEARCH_SUGGESTIONS_READY: + case FOLLOW_REQUESTS_FETCH_SUCCESS: + return normalizeAccounts(state, action.accounts); + case NOTIFICATIONS_REFRESH_SUCCESS: + case NOTIFICATIONS_EXPAND_SUCCESS: + return normalizeAccountsFromStatuses(normalizeAccounts(state, action.accounts), action.statuses); + case TIMELINE_REFRESH_SUCCESS: + case TIMELINE_EXPAND_SUCCESS: + case ACCOUNT_TIMELINE_FETCH_SUCCESS: + case ACCOUNT_TIMELINE_EXPAND_SUCCESS: + case CONTEXT_FETCH_SUCCESS: + return normalizeAccountsFromStatuses(state, action.statuses); + case REBLOG_SUCCESS: + case FAVOURITE_SUCCESS: + case UNREBLOG_SUCCESS: + case UNFAVOURITE_SUCCESS: + return normalizeAccountFromStatus(state, action.response); + case TIMELINE_UPDATE: + case STATUS_FETCH_SUCCESS: + return normalizeAccountFromStatus(state, action.status); + default: + return state; } }; diff --git a/app/assets/javascripts/components/reducers/compose.jsx b/app/assets/javascripts/components/reducers/compose.jsx index 16215684e..baa7d7f5a 100644 --- a/app/assets/javascripts/components/reducers/compose.jsx +++ b/app/assets/javascripts/components/reducers/compose.jsx @@ -21,7 +21,7 @@ import { COMPOSE_LISTABILITY_CHANGE } from '../actions/compose'; import { TIMELINE_DELETE } from '../actions/timelines'; -import { ACCOUNT_SET_SELF } from '../actions/accounts'; +import { STORE_HYDRATE } from '../actions/store'; import Immutable from 'immutable'; const initialState = Immutable.Map({ @@ -88,6 +88,8 @@ const insertSuggestion = (state, position, token, completion) => { export default function compose(state = initialState, action) { switch(action.type) { + case STORE_HYDRATE: + return state.merge(action.state.get('compose')); case COMPOSE_MOUNT: return state.set('mounted', true); case COMPOSE_UNMOUNT: @@ -97,7 +99,7 @@ export default function compose(state = initialState, action) { case COMPOSE_VISIBILITY_CHANGE: return state.set('private', action.checked); case COMPOSE_LISTABILITY_CHANGE: - return state.set('unlisted', action.checked); + return state.set('unlisted', action.checked); case COMPOSE_CHANGE: return state.set('text', action.text); case COMPOSE_REPLY: @@ -143,8 +145,6 @@ export default function compose(state = initialState, action) { } else { return state; } - case ACCOUNT_SET_SELF: - return state.set('me', action.account.id).set('private', action.account.locked); default: return state; } diff --git a/app/assets/javascripts/components/reducers/meta.jsx b/app/assets/javascripts/components/reducers/meta.jsx index c7222c60b..cd4b313d5 100644 --- a/app/assets/javascripts/components/reducers/meta.jsx +++ b/app/assets/javascripts/components/reducers/meta.jsx @@ -1,16 +1,16 @@ -import { ACCESS_TOKEN_SET } from '../actions/meta'; -import { ACCOUNT_SET_SELF } from '../actions/accounts'; +import { STORE_HYDRATE } from '../actions/store'; import Immutable from 'immutable'; -const initialState = Immutable.Map(); +const initialState = Immutable.Map({ + access_token: null, + me: null +}); export default function meta(state = initialState, action) { switch(action.type) { - case ACCESS_TOKEN_SET: - return state.set('access_token', action.token); - case ACCOUNT_SET_SELF: - return state.set('me', action.account.id); - default: - return state; + case STORE_HYDRATE: + return state.merge(action.state.get('meta')); + default: + return state; } }; diff --git a/app/assets/javascripts/components/store/configureStore.jsx b/app/assets/javascripts/components/store/configureStore.jsx index 3d03d4c19..2c1476e5d 100644 --- a/app/assets/javascripts/components/store/configureStore.jsx +++ b/app/assets/javascripts/components/store/configureStore.jsx @@ -1,11 +1,12 @@ import { createStore, applyMiddleware, compose } from 'redux'; -import thunk from 'redux-thunk'; -import appReducer from '../reducers'; -import { loadingBarMiddleware } from 'react-redux-loading-bar'; -import errorsMiddleware from '../middleware/errors'; +import thunk from 'redux-thunk'; +import appReducer from '../reducers'; +import { loadingBarMiddleware } from 'react-redux-loading-bar'; +import errorsMiddleware from '../middleware/errors'; +import Immutable from 'immutable'; -export default function configureStore(initialState) { - return createStore(appReducer, initialState, compose(applyMiddleware(thunk, loadingBarMiddleware({ +export default function configureStore() { + return createStore(appReducer, compose(applyMiddleware(thunk, loadingBarMiddleware({ promiseTypeSuffixes: ['REQUEST', 'SUCCESS', 'FAIL'], }), errorsMiddleware()), window.devToolsExtension ? window.devToolsExtension() : f => f)); }; diff --git a/app/helpers/home_helper.rb b/app/helpers/home_helper.rb index 6f87c7b72..d3c6b13a6 100644 --- a/app/helpers/home_helper.rb +++ b/app/helpers/home_helper.rb @@ -3,8 +3,6 @@ module HomeHelper def default_props { - token: @token, - account: render(file: 'api/v1/accounts/show', locals: { account: current_user.account }, formats: :json), locale: I18n.locale, } end diff --git a/app/views/home/index.html.haml b/app/views/home/index.html.haml index 498fae105..b4e935041 100644 --- a/app/views/home/index.html.haml +++ b/app/views/home/index.html.haml @@ -1,4 +1,22 @@ - content_for :header_tags do + :javascript + window.INITIAL_STATE = { + "meta": { + "access_token": "#{@token}", + "locale": "#{I18n.locale}", + "me": #{current_account.id} + }, + + "compose": { + "me": #{current_account.id}, + "private": #{current_account.locked?} + }, + + "accounts": { + #{current_account.id}: #{render(file: 'api/v1/accounts/show', locals: { account: current_user.account }, formats: :json)} + } + }; + = javascript_include_tag 'application' = react_component 'Mastodon', default_props, class: 'app-holder', prerender: false -- cgit From 75f80bef107cfe9e9c0e6ba3dc51ef86c89e40cc Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 9 Jan 2017 14:00:55 +0100 Subject: Persist UI settings, add missing localizations for German --- .../components/actions/notifications.jsx | 10 --- .../javascripts/components/actions/settings.jsx | 17 +++++ .../containers/column_settings_container.jsx | 6 +- .../components/features/notifications/index.jsx | 2 +- app/assets/javascripts/components/locales/de.jsx | 18 ++++- app/assets/javascripts/components/locales/en.jsx | 2 +- .../javascripts/components/reducers/index.jsx | 4 +- .../components/reducers/notifications.jsx | 41 +++-------- .../javascripts/components/reducers/settings.jsx | 32 +++++++++ app/controllers/api/web/settings_controller.rb | 15 ++++ app/controllers/home_controller.rb | 1 + app/models/account.rb | 4 +- app/models/web.rb | 5 ++ app/models/web/setting.rb | 7 ++ app/views/home/index.html.haml | 17 +---- app/views/home/initial_state.json.rabl | 24 +++++++ config/locales/de.yml | 33 +++++++++ config/locales/simple_form.de.yml | 5 ++ config/routes.rb | 4 ++ db/migrate/20170109120109_create_web_settings.rb | 12 ++++ db/schema.rb | 79 +++++++++++++++++++++- spec/fabricators/media_attachment_fabricator.rb | 1 + spec/fabricators/web_setting_fabricator.rb | 3 + spec/models/account_spec.rb | 25 +++++++ spec/models/web/setting_spec.rb | 5 ++ 25 files changed, 305 insertions(+), 67 deletions(-) create mode 100644 app/assets/javascripts/components/actions/settings.jsx create mode 100644 app/assets/javascripts/components/reducers/settings.jsx create mode 100644 app/controllers/api/web/settings_controller.rb create mode 100644 app/models/web.rb create mode 100644 app/models/web/setting.rb create mode 100644 app/views/home/initial_state.json.rabl create mode 100644 db/migrate/20170109120109_create_web_settings.rb create mode 100644 spec/fabricators/web_setting_fabricator.rb create mode 100644 spec/models/web/setting_spec.rb (limited to 'app') diff --git a/app/assets/javascripts/components/actions/notifications.jsx b/app/assets/javascripts/components/actions/notifications.jsx index 182b598aa..1e5b2c382 100644 --- a/app/assets/javascripts/components/actions/notifications.jsx +++ b/app/assets/javascripts/components/actions/notifications.jsx @@ -14,8 +14,6 @@ export const NOTIFICATIONS_EXPAND_REQUEST = 'NOTIFICATIONS_EXPAND_REQUEST'; export const NOTIFICATIONS_EXPAND_SUCCESS = 'NOTIFICATIONS_EXPAND_SUCCESS'; export const NOTIFICATIONS_EXPAND_FAIL = 'NOTIFICATIONS_EXPAND_FAIL'; -export const NOTIFICATIONS_SETTING_CHANGE = 'NOTIFICATIONS_SETTING_CHANGE'; - const fetchRelatedRelationships = (dispatch, notifications) => { const accountIds = notifications.filter(item => item.type === 'follow').map(item => item.account.id); @@ -133,11 +131,3 @@ export function expandNotificationsFail(error) { error }; }; - -export function changeNotificationsSetting(key, checked) { - return { - type: NOTIFICATIONS_SETTING_CHANGE, - key, - checked - }; -}; diff --git a/app/assets/javascripts/components/actions/settings.jsx b/app/assets/javascripts/components/actions/settings.jsx new file mode 100644 index 000000000..0a6fb7cdb --- /dev/null +++ b/app/assets/javascripts/components/actions/settings.jsx @@ -0,0 +1,17 @@ +import axios from 'axios'; + +export const SETTING_CHANGE = 'SETTING_CHANGE'; + +export function changeSetting(key, value) { + return (dispatch, getState) => { + dispatch({ + type: SETTING_CHANGE, + key, + value + }); + + axios.put('/api/web/settings', { + data: getState().get('settings').toJS() + }); + }; +}; diff --git a/app/assets/javascripts/components/features/notifications/containers/column_settings_container.jsx b/app/assets/javascripts/components/features/notifications/containers/column_settings_container.jsx index 6907fd351..5792e97e3 100644 --- a/app/assets/javascripts/components/features/notifications/containers/column_settings_container.jsx +++ b/app/assets/javascripts/components/features/notifications/containers/column_settings_container.jsx @@ -1,15 +1,15 @@ import { connect } from 'react-redux'; import ColumnSettings from '../components/column_settings'; -import { changeNotificationsSetting } from '../../../actions/notifications'; +import { changeSetting } from '../../../actions/settings'; const mapStateToProps = state => ({ - settings: state.getIn(['notifications', 'settings']) + settings: state.getIn(['settings', 'notifications']) }); const mapDispatchToProps = dispatch => ({ onChange (key, checked) { - dispatch(changeNotificationsSetting(key, checked)); + dispatch(changeSetting(['notifications', ...key], checked)); } }); diff --git a/app/assets/javascripts/components/features/notifications/index.jsx b/app/assets/javascripts/components/features/notifications/index.jsx index 7e706ad6a..29be491eb 100644 --- a/app/assets/javascripts/components/features/notifications/index.jsx +++ b/app/assets/javascripts/components/features/notifications/index.jsx @@ -18,7 +18,7 @@ const messages = defineMessages({ }); const getNotifications = createSelector([ - state => Immutable.List(state.getIn(['notifications', 'settings', 'shows']).filter(item => !item).keys()), + state => Immutable.List(state.getIn(['settings', 'notifications', 'shows']).filter(item => !item).keys()), state => state.getIn(['notifications', 'items']) ], (excludedTypes, notifications) => notifications.filterNot(item => excludedTypes.includes(item.get('type')))); diff --git a/app/assets/javascripts/components/locales/de.jsx b/app/assets/javascripts/components/locales/de.jsx index 97df67480..c37a71c21 100644 --- a/app/assets/javascripts/components/locales/de.jsx +++ b/app/assets/javascripts/components/locales/de.jsx @@ -8,6 +8,9 @@ const en = { "status.reblog": "Teilen", "status.favourite": "Favorisieren", "status.reblogged_by": "{name} teilte", + "status.sensitive_warning": "Sensible Inhalte", + "status.sensitive_toggle": "Klicken um zu zeigen", + "status.open": "Öffnen", "video_player.toggle_sound": "Ton umschalten", "account.mention": "Erwähnen", "account.edit_profile": "Profil bearbeiten", @@ -19,14 +22,17 @@ const en = { "account.follows": "Folgt", "account.followers": "Folger", "account.follows_you": "Folgt dir", + "account.requested": "Warte auf Erlaubnis", "getting_started.heading": "Erste Schritte", "getting_started.about_addressing": "Du kannst Leuten folgen, falls du ihren Nutzernamen und ihre Domain kennst, in dem du eine e-mail-artige Addresse in das Suchfeld oben an der Seite eingibst.", "getting_started.about_shortcuts": "Falls der Zielnutzer an derselben Domain ist wie du, funktioniert der Nutzername auch alleine. Das gilt auch für Erwähnungen in Beiträgen.", "getting_started.about_developer": "Der Entwickler des Projekts kann unter Gargron@mastodon.social gefunden werden", + "getting_started.open_source_notice": "Mastodon ist quelloffene Software. Du kannst auf {github} dazu beitragen oder Probleme melden.", "column.home": "Home", "column.mentions": "Erwähnungen", "column.public": "Gesamtes Bekanntes Netz", "column.notifications": "Mitteilungen", + "column.follow_requests": "Folgeanfragen", "tabs_bar.compose": "Schreiben", "tabs_bar.home": "Home", "tabs_bar.mentions": "Erwähnungen", @@ -36,10 +42,12 @@ const en = { "compose_form.publish": "Veröffentlichen", "compose_form.sensitive": "Medien als sensitiv markieren", "compose_form.unlisted": "Öffentlich nicht auflisten", + "compose_form.private": "Als privat markieren", "navigation_bar.edit_profile": "Profil bearbeiten", "navigation_bar.preferences": "Einstellungen", "navigation_bar.public_timeline": "Öffentlich", "navigation_bar.logout": "Abmelden", + "navigation_bar.follow_requests": "Folgeanfragen", "reply_indicator.cancel": "Abbrechen", "search.placeholder": "Suche", "search.account": "Konto", @@ -49,7 +57,15 @@ const en = { "notification.follow": "{name} folgt dir", "notification.favourite": "{name} favorisierte deinen Status", "notification.reblog": "{name} teilte deinen Status", - "notification.mention": "{name} erwähnte dich" + "notification.mention": "{name} erwähnte dich", + "notifications.column_settings.alert": "Desktop-Benachrichtigunen", + "notifications.column_settings.show": "In der Spalte anzeigen", + "notifications.column_settings.follow": "Neue Folger:", + "notifications.column_settings.favourite": "Favorisierungen:", + "notifications.column_settings.mention": "Erwähnungen:", + "notifications.column_settings.reblog": "Geteilte Beiträge:", + "follow_request.authorize": "Erlauben", + "follow_request.reject": "Ablehnen" }; export default en; diff --git a/app/assets/javascripts/components/locales/en.jsx b/app/assets/javascripts/components/locales/en.jsx index b166c48ba..92dcbaeb9 100644 --- a/app/assets/javascripts/components/locales/en.jsx +++ b/app/assets/javascripts/components/locales/en.jsx @@ -17,7 +17,6 @@ const en = { "account.unfollow": "Unfollow", "account.block": "Block", "account.follow": "Follow", - "account.block": "Block", "account.posts": "Posts", "account.follows": "Follows", "account.followers": "Followers", @@ -41,6 +40,7 @@ const en = { "compose_form.publish": "Toot", "compose_form.sensitive": "Mark media as sensitive", "compose_form.private": "Mark as private", + "compose_form.unlisted": "Do not display in public timeline", "navigation_bar.edit_profile": "Edit profile", "navigation_bar.preferences": "Preferences", "navigation_bar.public_timeline": "Public timeline", diff --git a/app/assets/javascripts/components/reducers/index.jsx b/app/assets/javascripts/components/reducers/index.jsx index aea9239f8..068491949 100644 --- a/app/assets/javascripts/components/reducers/index.jsx +++ b/app/assets/javascripts/components/reducers/index.jsx @@ -11,6 +11,7 @@ import statuses from './statuses'; import relationships from './relationships'; import search from './search'; import notifications from './notifications'; +import settings from './settings'; export default combineReducers({ timelines, @@ -24,5 +25,6 @@ export default combineReducers({ statuses, relationships, search, - notifications + notifications, + settings }); diff --git a/app/assets/javascripts/components/reducers/notifications.jsx b/app/assets/javascripts/components/reducers/notifications.jsx index e0d1ccf83..c85e7b460 100644 --- a/app/assets/javascripts/components/reducers/notifications.jsx +++ b/app/assets/javascripts/components/reducers/notifications.jsx @@ -2,7 +2,6 @@ import { NOTIFICATIONS_UPDATE, NOTIFICATIONS_REFRESH_SUCCESS, NOTIFICATIONS_EXPAND_SUCCESS, - NOTIFICATIONS_SETTING_CHANGE } from '../actions/notifications'; import { ACCOUNT_BLOCK_SUCCESS } from '../actions/accounts'; import Immutable from 'immutable'; @@ -10,23 +9,7 @@ import Immutable from 'immutable'; const initialState = Immutable.Map({ items: Immutable.List(), next: null, - loaded: false, - - settings: Immutable.Map({ - alerts: Immutable.Map({ - follow: true, - favourite: true, - reblog: true, - mention: true - }), - - shows: Immutable.Map({ - follow: true, - favourite: true, - reblog: true, - mention: true - }) - }) + loaded: false }); const notificationToMap = notification => Immutable.Map({ @@ -67,17 +50,15 @@ const filterNotifications = (state, relationship) => { export default function notifications(state = initialState, action) { switch(action.type) { - case NOTIFICATIONS_UPDATE: - return normalizeNotification(state, action.notification); - case NOTIFICATIONS_REFRESH_SUCCESS: - return normalizeNotifications(state, action.notifications, action.next); - case NOTIFICATIONS_EXPAND_SUCCESS: - return appendNormalizedNotifications(state, action.notifications, action.next); - case ACCOUNT_BLOCK_SUCCESS: - return filterNotifications(state, action.relationship); - case NOTIFICATIONS_SETTING_CHANGE: - return state.setIn(['settings', ...action.key], action.checked); - default: - return state; + case NOTIFICATIONS_UPDATE: + return normalizeNotification(state, action.notification); + case NOTIFICATIONS_REFRESH_SUCCESS: + return normalizeNotifications(state, action.notifications, action.next); + case NOTIFICATIONS_EXPAND_SUCCESS: + return appendNormalizedNotifications(state, action.notifications, action.next); + case ACCOUNT_BLOCK_SUCCESS: + return filterNotifications(state, action.relationship); + default: + return state; } }; diff --git a/app/assets/javascripts/components/reducers/settings.jsx b/app/assets/javascripts/components/reducers/settings.jsx new file mode 100644 index 000000000..2a834d81c --- /dev/null +++ b/app/assets/javascripts/components/reducers/settings.jsx @@ -0,0 +1,32 @@ +import { SETTING_CHANGE } from '../actions/settings'; +import { STORE_HYDRATE } from '../actions/store'; +import Immutable from 'immutable'; + +const initialState = Immutable.Map({ + notifications: Immutable.Map({ + alerts: Immutable.Map({ + follow: true, + favourite: true, + reblog: true, + mention: true + }), + + shows: Immutable.Map({ + follow: true, + favourite: true, + reblog: true, + mention: true + }) + }) +}); + +export default function settings(state = initialState, action) { + switch(action.type) { + case STORE_HYDRATE: + return state.merge(action.state.get('settings')); + case SETTING_CHANGE: + return state.setIn(action.key, action.value); + default: + return state; + } +}; diff --git a/app/controllers/api/web/settings_controller.rb b/app/controllers/api/web/settings_controller.rb new file mode 100644 index 000000000..e6f690114 --- /dev/null +++ b/app/controllers/api/web/settings_controller.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class Api::Web::SettingsController < ApiController + respond_to :json + + before_action :require_user! + + def update + setting = Web::Setting.where(user: current_user).first_or_initialize(user: current_user) + setting.data = params[:data] + setting.save! + + render_empty + end +end diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index a25fe77da..814b1f758 100644 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -6,6 +6,7 @@ class HomeController < ApplicationController def index @body_classes = 'app-body' @token = find_or_create_access_token.token + @web_settings = Web::Setting.find_by(user: current_user)&.data || {} end private diff --git a/app/models/account.rb b/app/models/account.rb index ba24cf153..ec0e81f7c 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -104,7 +104,7 @@ class Account < ApplicationRecord end def subscribed? - subscription_expires_at + !subscription_expires_at.blank? end def favourited?(status) @@ -189,7 +189,7 @@ class Account < ApplicationRecord def requested_map(target_account_ids, account_id) follow_mapping(FollowRequest.where(target_account_id: target_account_ids, account_id: account_id), :target_account_id) end - + private def follow_mapping(query, field) diff --git a/app/models/web.rb b/app/models/web.rb new file mode 100644 index 000000000..3c6eebbe2 --- /dev/null +++ b/app/models/web.rb @@ -0,0 +1,5 @@ +module Web + def self.table_name_prefix + 'web_' + end +end diff --git a/app/models/web/setting.rb b/app/models/web/setting.rb new file mode 100644 index 000000000..3d601189b --- /dev/null +++ b/app/models/web/setting.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class Web::Setting < ApplicationRecord + belongs_to :user + + validates :user, uniqueness: true +end diff --git a/app/views/home/index.html.haml b/app/views/home/index.html.haml index b4e935041..730249129 100644 --- a/app/views/home/index.html.haml +++ b/app/views/home/index.html.haml @@ -1,21 +1,6 @@ - content_for :header_tags do :javascript - window.INITIAL_STATE = { - "meta": { - "access_token": "#{@token}", - "locale": "#{I18n.locale}", - "me": #{current_account.id} - }, - - "compose": { - "me": #{current_account.id}, - "private": #{current_account.locked?} - }, - - "accounts": { - #{current_account.id}: #{render(file: 'api/v1/accounts/show', locals: { account: current_user.account }, formats: :json)} - } - }; + window.INITIAL_STATE = #{render(file: 'home/initial_state', formats: :json)} = javascript_include_tag 'application' diff --git a/app/views/home/initial_state.json.rabl b/app/views/home/initial_state.json.rabl new file mode 100644 index 000000000..0e9736f5f --- /dev/null +++ b/app/views/home/initial_state.json.rabl @@ -0,0 +1,24 @@ +object false + +node(:meta) { + { + access_token: @token, + locale: I18n.locale, + me: current_account.id, + } +} + +node(:compose) { + { + me: current_account.id, + private: current_account.locked?, + } +} + +node(:accounts) { + { + current_account.id => partial('api/v1/accounts/show', object: current_account), + } +} + +node(:settings) { @web_settings } diff --git a/config/locales/de.yml b/config/locales/de.yml index ead3ae514..f36cc64c8 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -14,6 +14,7 @@ de: people_followed_by: Nutzer, denen %{name} folgt people_who_follow: Nutzer, die %{name} folgen posts: Beiträge + remote_follow: Folgen unfollow: Entfolgen application_mailer: signature: Mastodon-Benachrichtigungen von %{instance} @@ -26,6 +27,25 @@ de: resend_confirmation: Bestätigung nochmal versenden reset_password: Passwort zurücksetzen set_new_password: Neues Passwort setzen + authorize_follow: + error: Das entfernte Profil konnte nicht geladen werden + follow: Folgen + prompt_html: 'Du (%{self}) möchtest dieser Person folgen:' + title: "%{acct} folgen" + datetime: + distance_in_words: + about_x_hours: "%{count}h" + about_x_months: "%{count}mo" + about_x_years: "%{count}y" + almost_x_years: "%{count}y" + half_a_minute: Gerade eben + less_than_x_minutes: "%{count}m" + less_than_x_seconds: Gerade eben + over_x_years: "%{count}y" + x_days: "%{count}d" + x_minutes: "%{count}m" + x_months: "%{count}mo" + x_seconds: "%{count}s" generic: changes_saved_msg: Änderungen gespeichert! powered_by: angetrieben von %{link} @@ -40,6 +60,9 @@ de: follow: body: "%{name} folgt dir jetzt!" subject: "%{name} folgt dir nun" + follow_request: + body: "%{name} möchte dir folgen:" + subject: "%{name} möchte dir folgen" mention: body: "%{name} hat dich erwähnt:" subject: "%{name} hat dich erwähnt" @@ -49,13 +72,23 @@ de: pagination: next: Vorwärts prev: Zurück + remote_follow: + acct: Dein Nutzername@Domain, von dem du dieser Person folgen möchtest + missing_resource: Die erforderliche Weiterleitungs-URL konnte leider in deinem Profil nicht gefunden werden + proceed: Weiter + prompt: 'Du wirst dieser Person folgen:' settings: edit_profile: Profil bearbeiten preferences: Einstellungen stream_entries: + click_to_show: Klicken um zu zeigen favourited: favorisierte einen Beitrag von is_now_following: folgt nun reblogged: teilte + sensitive_content: Sensible Inhalte + time: + formats: + default: "%d.%m.%Y %H:%M" users: invalid_email: Inkorrekte E-mail-Addresse will_paginate: diff --git a/config/locales/simple_form.de.yml b/config/locales/simple_form.de.yml index d0aed9d0e..614cd4911 100644 --- a/config/locales/simple_form.de.yml +++ b/config/locales/simple_form.de.yml @@ -1,6 +1,9 @@ --- de: simple_form: + hints: + defaults: + locked: Erlaubt dir, Folger zu überprüfen, bevor sie dir folgen können labels: defaults: avatar: Avatar @@ -11,6 +14,7 @@ de: email: E-mail-Addresse header: Kopfbild locale: Sprache + locked: Gesperrter Profil new_password: Neues Passwort note: Über mich password: Passwort @@ -21,6 +25,7 @@ de: notification_emails: favourite: E-mail senden, wenn jemand meinen Beitrag favorisiert follow: E-mail senden, wenn mir jemand folgt + follow_request: E-mail senden, wenn mir jemand folgen möchte mention: E-mail senden, wenn mich jemand erwähnt reblog: E-mail senden, wenn jemand meinen Beitrag teilt 'no': Nein diff --git a/config/routes.rb b/config/routes.rb index e46b27f1f..dd6944b29 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -134,6 +134,10 @@ Rails.application.routes.draw do end end end + + namespace :web do + resource :settings, only: [:update] + end end get '/web/(*any)', to: 'home#index', as: :web diff --git a/db/migrate/20170109120109_create_web_settings.rb b/db/migrate/20170109120109_create_web_settings.rb new file mode 100644 index 000000000..2aeae1f91 --- /dev/null +++ b/db/migrate/20170109120109_create_web_settings.rb @@ -0,0 +1,12 @@ +class CreateWebSettings < ActiveRecord::Migration[5.0] + def change + create_table :web_settings do |t| + t.integer :user_id + t.json :data + + t.timestamps + end + + add_index :web_settings, :user_id, unique: true + end +end diff --git a/db/schema.rb b/db/schema.rb index a535c5fdb..5a5dd83c7 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170105224407) do +ActiveRecord::Schema.define(version: 20170109120109) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -169,6 +169,74 @@ ActiveRecord::Schema.define(version: 20170105224407) do t.index ["topic", "callback"], name: "index_pubsubhubbub_subscriptions_on_topic_and_callback", unique: true, using: :btree end + create_table "push_devices", force: :cascade do |t| + t.string "service", default: "", null: false + t.string "token", default: "", null: false + t.integer "account", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["service", "token"], name: "index_push_devices_on_service_and_token", unique: true, using: :btree + end + + create_table "rpush_apps", force: :cascade do |t| + t.string "name", null: false + t.string "environment" + t.text "certificate" + t.string "password" + t.integer "connections", default: 1, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "type", null: false + t.string "auth_key" + t.string "client_id" + t.string "client_secret" + t.string "access_token" + t.datetime "access_token_expiration" + end + + create_table "rpush_feedback", force: :cascade do |t| + t.string "device_token", limit: 64, null: false + t.datetime "failed_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.integer "app_id" + t.index ["device_token"], name: "index_rpush_feedback_on_device_token", using: :btree + end + + create_table "rpush_notifications", force: :cascade do |t| + t.integer "badge" + t.string "device_token", limit: 64 + t.string "sound", default: "default" + t.text "alert" + t.text "data" + t.integer "expiry", default: 86400 + t.boolean "delivered", default: false, null: false + t.datetime "delivered_at" + t.boolean "failed", default: false, null: false + t.datetime "failed_at" + t.integer "error_code" + t.text "error_description" + t.datetime "deliver_after" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.boolean "alert_is_json", default: false + t.string "type", null: false + t.string "collapse_key" + t.boolean "delay_while_idle", default: false, null: false + t.text "registration_ids" + t.integer "app_id", null: false + t.integer "retries", default: 0 + t.string "uri" + t.datetime "fail_after" + t.boolean "processing", default: false, null: false + t.integer "priority" + t.text "url_args" + t.string "category" + t.boolean "content_available", default: false + t.text "notification" + t.index ["delivered", "failed"], name: "index_rpush_notifications_multi", where: "((NOT delivered) AND (NOT failed))", using: :btree + end + create_table "settings", force: :cascade do |t| t.string "var", null: false t.text "value" @@ -191,7 +259,6 @@ ActiveRecord::Schema.define(version: 20170105224407) do t.boolean "sensitive", default: false t.integer "visibility", default: 0, null: false t.integer "in_reply_to_account_id" - t.string "conversation_uri" t.index ["account_id"], name: "index_statuses_on_account_id", using: :btree t.index ["in_reply_to_id"], name: "index_statuses_on_in_reply_to_id", using: :btree t.index ["reblog_of_id"], name: "index_statuses_on_reblog_of_id", using: :btree @@ -260,4 +327,12 @@ ActiveRecord::Schema.define(version: 20170105224407) do t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree end + create_table "web_settings", force: :cascade do |t| + t.integer "user_id" + t.json "data" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["user_id"], name: "index_web_settings_on_user_id", unique: true, using: :btree + end + end diff --git a/spec/fabricators/media_attachment_fabricator.rb b/spec/fabricators/media_attachment_fabricator.rb index b1a0cd991..59db2440d 100644 --- a/spec/fabricators/media_attachment_fabricator.rb +++ b/spec/fabricators/media_attachment_fabricator.rb @@ -1,2 +1,3 @@ Fabricator(:media_attachment) do + end diff --git a/spec/fabricators/web_setting_fabricator.rb b/spec/fabricators/web_setting_fabricator.rb new file mode 100644 index 000000000..e5136829b9 --- /dev/null +++ b/spec/fabricators/web_setting_fabricator.rb @@ -0,0 +1,3 @@ +Fabricator('Web::Setting') do + +end diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb index a72369b1c..287f389ac 100644 --- a/spec/models/account_spec.rb +++ b/spec/models/account_spec.rb @@ -154,6 +154,31 @@ RSpec.describe Account, type: :model do end end + describe '.following_map' do + it 'returns an hash' do + expect(Account.following_map([], 1)).to be_a Hash + end + end + + describe '.followed_by_map' do + it 'returns an hash' do + expect(Account.followed_by_map([], 1)).to be_a Hash + end + end + + describe '.blocking_map' do + it 'returns an hash' do + expect(Account.blocking_map([], 1)).to be_a Hash + end + end + + describe '.requested_map' do + it 'returns an hash' do + expect(Account.requested_map([], 1)).to be_a Hash + end + end + + describe 'MENTION_RE' do subject { Account::MENTION_RE } diff --git a/spec/models/web/setting_spec.rb b/spec/models/web/setting_spec.rb new file mode 100644 index 000000000..90e7695aa --- /dev/null +++ b/spec/models/web/setting_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe Web::Setting, type: :model do + +end -- cgit From 205ba000174e1c553b871ee4ef1313e282e68a68 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 9 Jan 2017 19:43:53 +0100 Subject: Fix sandboxed iOS login again --- app/views/about/index.html.haml | 3 +++ 1 file changed, 3 insertions(+) (limited to 'app') diff --git a/app/views/about/index.html.haml b/app/views/about/index.html.haml index b0260dc51..6bc416cbd 100644 --- a/app/views/about/index.html.haml +++ b/app/views/about/index.html.haml @@ -1,3 +1,6 @@ +- content_for :header_tags do + = javascript_include_tag 'application_public' + - content_for :page_title do = Rails.configuration.x.local_domain -- cgit From 917cf0bf5d248469a5ce464cf0bba1cfd1ab9d50 Mon Sep 17 00:00:00 2001 From: blackle Date: Mon, 9 Jan 2017 22:40:45 -0500 Subject: simplify emojification on public pages with .emojify class --- app/assets/javascripts/extras.jsx | 2 +- app/views/accounts/_grid_card.html.haml | 4 ++-- app/views/accounts/_header.html.haml | 4 ++-- app/views/authorize_follow/_card.html.haml | 4 ++-- app/views/stream_entries/_detailed_status.html.haml | 4 ++-- app/views/stream_entries/_favourite.html.haml | 2 +- app/views/stream_entries/_follow.html.haml | 2 +- app/views/stream_entries/_simple_status.html.haml | 4 ++-- 8 files changed, 13 insertions(+), 13 deletions(-) (limited to 'app') diff --git a/app/assets/javascripts/extras.jsx b/app/assets/javascripts/extras.jsx index c1df182de..5738863dd 100644 --- a/app/assets/javascripts/extras.jsx +++ b/app/assets/javascripts/extras.jsx @@ -1,7 +1,7 @@ import emojify from './components/emoji' $(() => { - $.each($('.entry .content, .entry .status__content, .status__display-name, .display-name, .name, .account__header__content'), (_, content) => { + $.each($('.emojify'), (_, content) => { const $content = $(content); $content.html(emojify($content.html())); }); diff --git a/app/views/accounts/_grid_card.html.haml b/app/views/accounts/_grid_card.html.haml index dfdb23161..d5418fca5 100644 --- a/app/views/accounts/_grid_card.html.haml +++ b/app/views/accounts/_grid_card.html.haml @@ -3,6 +3,6 @@ .avatar= image_tag account.avatar.url(:original) .name = link_to TagManager.instance.url_for(account) do - %span.display_name= display_name(account) + %span.display_name.emojify= display_name(account) %span.username= "@#{account.acct}" - %p.note= truncate(strip_tags(account.note), length: 150) + %p.note.emojify= truncate(strip_tags(account.note), length: 150) diff --git a/app/views/accounts/_header.html.haml b/app/views/accounts/_header.html.haml index 7a5cea7ab..f575e855e 100644 --- a/app/views/accounts/_header.html.haml +++ b/app/views/accounts/_header.html.haml @@ -11,13 +11,13 @@ = link_to t('accounts.remote_follow'), account_remote_follow_path(@account), class: 'button' .avatar= image_tag @account.avatar.url(:original), class: 'u-photo' %h1.name - %span.p-name= display_name(@account) + %span.p-name.emojify= display_name(@account) %small %span.p-nickname= "@#{@account.username}" = fa_icon('lock') if @account.locked? .details .bio - .account__header__content.p-note= Formatter.instance.simplified_format(@account) + .account__header__content.p-note.emojify= Formatter.instance.simplified_format(@account) .details-counters .counter{ class: active_nav_class(account_url(@account)) } diff --git a/app/views/authorize_follow/_card.html.haml b/app/views/authorize_follow/_card.html.haml index a9b02c746..eef0bec07 100644 --- a/app/views/authorize_follow/_card.html.haml +++ b/app/views/authorize_follow/_card.html.haml @@ -4,8 +4,8 @@ = image_tag account.avatar.url(:original), alt: '', width: 48, height: 48, class: 'avatar' %span.display-name - %strong= display_name(account) + %strong.emojify= display_name(account) %span= "@#{account.acct}" - unless account.note.blank? - .account__header__content= Formatter.instance.simplified_format(account) + .account__header__content.emojify= Formatter.instance.simplified_format(account) diff --git a/app/views/stream_entries/_detailed_status.html.haml b/app/views/stream_entries/_detailed_status.html.haml index b0d36872c..32f7c2e40 100644 --- a/app/views/stream_entries/_detailed_status.html.haml +++ b/app/views/stream_entries/_detailed_status.html.haml @@ -4,10 +4,10 @@ %div.avatar = image_tag status.account.avatar.url(:original), width: 48, height: 48, alt: '', class: 'u-photo' %span.display-name - %strong.p-name= display_name(status.account) + %strong.p-name.emojify= display_name(status.account) %span.p-nickname= acct(status.account) - .status__content.e-content.p-name= Formatter.instance.format(status) + .status__content.e-content.p-name.emojify= Formatter.instance.format(status) - unless status.media_attachments.empty? - if status.media_attachments.first.video? diff --git a/app/views/stream_entries/_favourite.html.haml b/app/views/stream_entries/_favourite.html.haml index aac90dcdf..ea4879328 100644 --- a/app/views/stream_entries/_favourite.html.haml +++ b/app/views/stream_entries/_favourite.html.haml @@ -1,5 +1,5 @@ .entry.entry-favourite - .content + .content.emojify %strong= favourite.account.acct = t('stream_entries.favourited') %strong= favourite.status.account.acct diff --git a/app/views/stream_entries/_follow.html.haml b/app/views/stream_entries/_follow.html.haml index 1a2e2c554..da6d062f0 100644 --- a/app/views/stream_entries/_follow.html.haml +++ b/app/views/stream_entries/_follow.html.haml @@ -1,5 +1,5 @@ .entry.entry-follow - .content + .content.emojify %strong= link_to follow.account.acct, account_path(follow.account) = t('stream_entries.is_now_following') %strong= link_to follow.target_account.acct, TagManager.instance.url_for(follow.target_account) diff --git a/app/views/stream_entries/_simple_status.html.haml b/app/views/stream_entries/_simple_status.html.haml index b08cf5dab..eba2f9ac4 100644 --- a/app/views/stream_entries/_simple_status.html.haml +++ b/app/views/stream_entries/_simple_status.html.haml @@ -9,10 +9,10 @@ %div = image_tag status.account.avatar(:original), width: 48, height: 48, alt: '', class: 'u-photo' %span.display-name - %strong.p-name= display_name(status.account) + %strong.p-name.emojify= display_name(status.account) %span.p-nickname= acct(status.account) - .status__content.e-content.p-name= Formatter.instance.format(status) + .status__content.e-content.p-name.emojify= Formatter.instance.format(status) - unless status.media_attachments.empty? .status__attachments -- cgit From 1e9d2c4b1e00e8e68fefe5c04b48f66c827d31d5 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 10 Jan 2017 13:50:40 +0100 Subject: Add "not found" component to UI --- .../components/components/loading_indicator.jsx | 22 ++++++++++++---------- .../components/components/missing_indicator.jsx | 17 +++++++++++++++++ .../javascripts/components/containers/mastodon.jsx | 2 ++ .../features/generic_not_found/index.jsx | 10 ++++++++++ .../components/features/status/index.jsx | 4 +++- 5 files changed, 44 insertions(+), 11 deletions(-) create mode 100644 app/assets/javascripts/components/components/missing_indicator.jsx create mode 100644 app/assets/javascripts/components/features/generic_not_found/index.jsx (limited to 'app') diff --git a/app/assets/javascripts/components/components/loading_indicator.jsx b/app/assets/javascripts/components/components/loading_indicator.jsx index fd5acae84..c8a263924 100644 --- a/app/assets/javascripts/components/components/loading_indicator.jsx +++ b/app/assets/javascripts/components/components/loading_indicator.jsx @@ -1,15 +1,17 @@ import { FormattedMessage } from 'react-intl'; -const LoadingIndicator = () => { - const style = { - textAlign: 'center', - fontSize: '16px', - fontWeight: '500', - color: '#616b86', - paddingTop: '120px' - }; - - return
; +const style = { + textAlign: 'center', + fontSize: '16px', + fontWeight: '500', + color: '#616b86', + paddingTop: '120px' }; +const LoadingIndicator = () => ( +
+ +
+); + export default LoadingIndicator; diff --git a/app/assets/javascripts/components/components/missing_indicator.jsx b/app/assets/javascripts/components/components/missing_indicator.jsx new file mode 100644 index 000000000..ed8b4fe24 --- /dev/null +++ b/app/assets/javascripts/components/components/missing_indicator.jsx @@ -0,0 +1,17 @@ +import { FormattedMessage } from 'react-intl'; + +const style = { + textAlign: 'center', + fontSize: '16px', + fontWeight: '500', + color: '#616b86', + paddingTop: '120px' +}; + +const MissingIndicator = () => ( +
+ +
+); + +export default MissingIndicator; diff --git a/app/assets/javascripts/components/containers/mastodon.jsx b/app/assets/javascripts/components/containers/mastodon.jsx index 143a280c3..af495652f 100644 --- a/app/assets/javascripts/components/containers/mastodon.jsx +++ b/app/assets/javascripts/components/containers/mastodon.jsx @@ -33,6 +33,7 @@ import Favourites from '../features/favourites'; import HashtagTimeline from '../features/hashtag_timeline'; import Notifications from '../features/notifications'; import FollowRequests from '../features/follow_requests'; +import GenericNotFound from '../features/generic_not_found'; import { IntlProvider, addLocaleData } from 'react-intl'; import en from 'react-intl/locale-data/en'; import de from 'react-intl/locale-data/de'; @@ -125,6 +126,7 @@ const Mastodon = React.createClass({ + diff --git a/app/assets/javascripts/components/features/generic_not_found/index.jsx b/app/assets/javascripts/components/features/generic_not_found/index.jsx new file mode 100644 index 000000000..a7afe29b0 --- /dev/null +++ b/app/assets/javascripts/components/features/generic_not_found/index.jsx @@ -0,0 +1,10 @@ +import Column from '../ui/components/column'; +import MissingIndicator from '../../components/missing_indicator'; + +const GenericNotFound = () => ( + + + +); + +export default GenericNotFound; diff --git a/app/assets/javascripts/components/features/status/index.jsx b/app/assets/javascripts/components/features/status/index.jsx index 27a252759..389549849 100644 --- a/app/assets/javascripts/components/features/status/index.jsx +++ b/app/assets/javascripts/components/features/status/index.jsx @@ -48,7 +48,8 @@ const Status = React.createClass({ dispatch: React.PropTypes.func.isRequired, status: ImmutablePropTypes.map, ancestorsIds: ImmutablePropTypes.list, - descendantsIds: ImmutablePropTypes.list + descendantsIds: ImmutablePropTypes.list, + me: React.PropTypes.number }, mixins: [PureRenderMixin], @@ -81,6 +82,7 @@ const Status = React.createClass({ handleMentionClick (account) { this.props.dispatch(mentionCompose(account)); + if (isMobile(window.innerWidth)) { this.context.router.push('/statuses/new'); } -- cgit From 312c51b5c87e23c62d163770d550dc94df32627f Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 10 Jan 2017 17:25:10 +0100 Subject: Home column filters --- .../javascripts/components/actions/settings.jsx | 14 +- .../components/components/column_collapsable.jsx | 60 +++++++++ .../home_timeline/components/column_settings.jsx | 68 ++++++++++ .../home_timeline/components/setting_text.jsx | 41 ++++++ .../containers/column_settings_container.jsx | 21 +++ .../components/features/home_timeline/index.jsx | 5 +- .../notifications/components/column_settings.jsx | 145 +++++---------------- .../notifications/components/setting_toggle.jsx | 32 +++++ .../containers/column_settings_container.jsx | 6 +- .../ui/containers/status_list_container.jsx | 59 ++++++--- app/assets/javascripts/components/locales/de.jsx | 8 +- .../javascripts/components/reducers/settings.jsx | 7 + app/assets/stylesheets/components.scss | 14 +- app/controllers/api/web/settings_controller.rb | 2 +- package.json | 3 + yarn.lock | 127 +++++++++++++++--- 16 files changed, 462 insertions(+), 150 deletions(-) create mode 100644 app/assets/javascripts/components/components/column_collapsable.jsx create mode 100644 app/assets/javascripts/components/features/home_timeline/components/column_settings.jsx create mode 100644 app/assets/javascripts/components/features/home_timeline/components/setting_text.jsx create mode 100644 app/assets/javascripts/components/features/home_timeline/containers/column_settings_container.jsx create mode 100644 app/assets/javascripts/components/features/notifications/components/setting_toggle.jsx (limited to 'app') diff --git a/app/assets/javascripts/components/actions/settings.jsx b/app/assets/javascripts/components/actions/settings.jsx index 0a6fb7cdb..c754b30ca 100644 --- a/app/assets/javascripts/components/actions/settings.jsx +++ b/app/assets/javascripts/components/actions/settings.jsx @@ -3,13 +3,15 @@ import axios from 'axios'; export const SETTING_CHANGE = 'SETTING_CHANGE'; export function changeSetting(key, value) { - return (dispatch, getState) => { - dispatch({ - type: SETTING_CHANGE, - key, - value - }); + return { + type: SETTING_CHANGE, + key, + value + }; +}; +export function saveSettings() { + return (_, getState) => { axios.put('/api/web/settings', { data: getState().get('settings').toJS() }); diff --git a/app/assets/javascripts/components/components/column_collapsable.jsx b/app/assets/javascripts/components/components/column_collapsable.jsx new file mode 100644 index 000000000..abd65d633 --- /dev/null +++ b/app/assets/javascripts/components/components/column_collapsable.jsx @@ -0,0 +1,60 @@ +import PureRenderMixin from 'react-addons-pure-render-mixin'; +import { Motion, spring } from 'react-motion'; + +const iconStyle = { + fontSize: '16px', + padding: '15px', + position: 'absolute', + right: '0', + top: '-48px', + cursor: 'pointer' +}; + +const ColumnCollapsable = React.createClass({ + + propTypes: { + icon: React.PropTypes.string.isRequired, + fullHeight: React.PropTypes.number.isRequired, + children: React.PropTypes.node, + onCollapse: React.PropTypes.func + }, + + getInitialState () { + return { + collapsed: true + }; + }, + + mixins: [PureRenderMixin], + + handleToggleCollapsed () { + const currentState = this.state.collapsed; + + this.setState({ collapsed: !currentState }); + + if (!currentState && this.props.onCollapse) { + this.props.onCollapse(); + } + }, + + render () { + const { icon, fullHeight, children } = this.props; + const { collapsed } = this.state; + + return ( +
+
+ + + {({ opacity, height }) => +
+ {children} +
+ } +
+
+ ); + } +}); + +export default ColumnCollapsable; diff --git a/app/assets/javascripts/components/features/home_timeline/components/column_settings.jsx b/app/assets/javascripts/components/features/home_timeline/components/column_settings.jsx new file mode 100644 index 000000000..714be309b --- /dev/null +++ b/app/assets/javascripts/components/features/home_timeline/components/column_settings.jsx @@ -0,0 +1,68 @@ +import PureRenderMixin from 'react-addons-pure-render-mixin'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; +import ColumnCollapsable from '../../../components/column_collapsable'; +import SettingToggle from '../../notifications/components/setting_toggle'; +import SettingText from './setting_text'; + +const messages = defineMessages({ + filter_regex: { id: 'home.column_settings.filter_regex', defaultMessage: 'Filter by regular expressions' } +}); + +const outerStyle = { + background: '#373b4a', + padding: '15px' +}; + +const sectionStyle = { + cursor: 'default', + display: 'block', + fontWeight: '500', + color: '#9baec8', + marginBottom: '10px' +}; + +const rowStyle = { + +}; + +const ColumnSettings = React.createClass({ + + propTypes: { + settings: ImmutablePropTypes.map.isRequired, + onChange: React.PropTypes.func.isRequired, + onSave: React.PropTypes.func.isRequired, + intl: React.PropTypes.object.isRequired + }, + + mixins: [PureRenderMixin], + + render () { + const { settings, onChange, onSave, intl } = this.props; + + return ( + +
+ + +
+ } /> +
+ +
+ } /> +
+ + + +
+ +
+
+
+ ); + } + +}); + +export default injectIntl(ColumnSettings); diff --git a/app/assets/javascripts/components/features/home_timeline/components/setting_text.jsx b/app/assets/javascripts/components/features/home_timeline/components/setting_text.jsx new file mode 100644 index 000000000..79697e869 --- /dev/null +++ b/app/assets/javascripts/components/features/home_timeline/components/setting_text.jsx @@ -0,0 +1,41 @@ +import ImmutablePropTypes from 'react-immutable-proptypes'; + +const style = { + display: 'block', + fontFamily: 'inherit', + marginBottom: '10px', + padding: '7px 0', + boxSizing: 'border-box', + width: '100%' +}; + +const SettingText = React.createClass({ + + propTypes: { + settings: ImmutablePropTypes.map.isRequired, + settingKey: React.PropTypes.array.isRequired, + label: React.PropTypes.string.isRequired, + onChange: React.PropTypes.func.isRequired + }, + + handleChange (e) { + this.props.onChange(this.props.settingKey, e.target.value) + }, + + render () { + const { settings, settingKey, label } = this.props; + + return ( + + ); + } + +}); + +export default SettingText; diff --git a/app/assets/javascripts/components/features/home_timeline/containers/column_settings_container.jsx b/app/assets/javascripts/components/features/home_timeline/containers/column_settings_container.jsx new file mode 100644 index 000000000..3b3ce19bc --- /dev/null +++ b/app/assets/javascripts/components/features/home_timeline/containers/column_settings_container.jsx @@ -0,0 +1,21 @@ +import { connect } from 'react-redux'; +import ColumnSettings from '../components/column_settings'; +import { changeSetting, saveSettings } from '../../../actions/settings'; + +const mapStateToProps = state => ({ + settings: state.getIn(['settings', 'home']) +}); + +const mapDispatchToProps = dispatch => ({ + + onChange (key, checked) { + dispatch(changeSetting(['home', ...key], checked)); + }, + + onSave () { + dispatch(saveSettings()); + } + +}); + +export default connect(mapStateToProps, mapDispatchToProps)(ColumnSettings); diff --git a/app/assets/javascripts/components/features/home_timeline/index.jsx b/app/assets/javascripts/components/features/home_timeline/index.jsx index e4f4fa7c7..8703d0b70 100644 --- a/app/assets/javascripts/components/features/home_timeline/index.jsx +++ b/app/assets/javascripts/components/features/home_timeline/index.jsx @@ -4,6 +4,7 @@ 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'; const messages = defineMessages({ title: { id: 'column.home', defaultMessage: 'Home' } @@ -12,7 +13,8 @@ const messages = defineMessages({ const HomeTimeline = React.createClass({ propTypes: { - dispatch: React.PropTypes.func.isRequired + dispatch: React.PropTypes.func.isRequired, + intl: React.PropTypes.object.isRequired }, mixins: [PureRenderMixin], @@ -26,6 +28,7 @@ const HomeTimeline = React.createClass({ return ( + ); diff --git a/app/assets/javascripts/components/features/notifications/components/column_settings.jsx b/app/assets/javascripts/components/features/notifications/components/column_settings.jsx index b4035c20d..dfb59713c 100644 --- a/app/assets/javascripts/components/features/notifications/components/column_settings.jsx +++ b/app/assets/javascripts/components/features/notifications/components/column_settings.jsx @@ -1,37 +1,14 @@ import PureRenderMixin from 'react-addons-pure-render-mixin'; import ImmutablePropTypes from 'react-immutable-proptypes'; -import Toggle from 'react-toggle'; -import { Motion, spring } from 'react-motion'; import { FormattedMessage } from 'react-intl'; +import ColumnCollapsable from '../../../components/column_collapsable'; +import SettingToggle from './setting_toggle'; const outerStyle = { background: '#373b4a', padding: '15px' }; -const iconStyle = { - fontSize: '16px', - padding: '15px', - position: 'absolute', - right: '0', - top: '-48px', - cursor: 'pointer' -}; - -const labelStyle = { - display: 'block', - lineHeight: '24px', - verticalAlign: 'middle' -}; - -const labelSpanStyle = { - display: 'inline-block', - verticalAlign: 'middle', - marginBottom: '14px', - marginLeft: '8px', - color: '#9baec8' -}; - const sectionStyle = { cursor: 'default', display: 'block', @@ -48,100 +25,50 @@ const ColumnSettings = React.createClass({ propTypes: { settings: ImmutablePropTypes.map.isRequired, - onChange: React.PropTypes.func.isRequired - }, - - getInitialState () { - return { - collapsed: true - }; + onChange: React.PropTypes.func.isRequired, + onSave: React.PropTypes.func.isRequired }, mixins: [PureRenderMixin], - handleToggleCollapsed () { - this.setState({ collapsed: !this.state.collapsed }); - }, - - handleChange (key, e) { - this.props.onChange(key, e.target.checked); - }, - render () { - const { settings } = this.props; - const { collapsed } = this.state; + const { settings, onChange, onSave } = this.props; const alertStr = ; const showStr = ; return ( -
-
- - - {({ opacity, height }) => -
-
- - -
- - - -
- - - -
- - - -
- - - -
- - - -
- - - -
- - - -
-
-
- } -
-
+ +
+ + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+
+
); } diff --git a/app/assets/javascripts/components/features/notifications/components/setting_toggle.jsx b/app/assets/javascripts/components/features/notifications/components/setting_toggle.jsx new file mode 100644 index 000000000..c2438f716 --- /dev/null +++ b/app/assets/javascripts/components/features/notifications/components/setting_toggle.jsx @@ -0,0 +1,32 @@ +import ImmutablePropTypes from 'react-immutable-proptypes'; +import Toggle from 'react-toggle'; + +const labelStyle = { + display: 'block', + lineHeight: '24px', + verticalAlign: 'middle' +}; + +const labelSpanStyle = { + display: 'inline-block', + verticalAlign: 'middle', + marginBottom: '14px', + marginLeft: '8px', + color: '#9baec8' +}; + +const SettingToggle = ({ settings, settingKey, label, onChange }) => ( + +); + +SettingToggle.propTypes = { + settings: ImmutablePropTypes.map.isRequired, + settingKey: React.PropTypes.array.isRequired, + label: React.PropTypes.node.isRequired, + onChange: React.PropTypes.func.isRequired +}; + +export default SettingToggle; diff --git a/app/assets/javascripts/components/features/notifications/containers/column_settings_container.jsx b/app/assets/javascripts/components/features/notifications/containers/column_settings_container.jsx index 5792e97e3..bc24c75e0 100644 --- a/app/assets/javascripts/components/features/notifications/containers/column_settings_container.jsx +++ b/app/assets/javascripts/components/features/notifications/containers/column_settings_container.jsx @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; import ColumnSettings from '../components/column_settings'; -import { changeSetting } from '../../../actions/settings'; +import { changeSetting, saveSettings } from '../../../actions/settings'; const mapStateToProps = state => ({ settings: state.getIn(['settings', 'notifications']) @@ -10,6 +10,10 @@ const mapDispatchToProps = dispatch => ({ onChange (key, checked) { dispatch(changeSetting(['notifications', ...key], checked)); + }, + + onSave () { + dispatch(saveSettings()); } }); diff --git a/app/assets/javascripts/components/features/ui/containers/status_list_container.jsx b/app/assets/javascripts/components/features/ui/containers/status_list_container.jsx index 1621cec7b..7b893711c 100644 --- a/app/assets/javascripts/components/features/ui/containers/status_list_container.jsx +++ b/app/assets/javascripts/components/features/ui/containers/status_list_container.jsx @@ -2,26 +2,55 @@ import { connect } from 'react-redux'; import StatusList from '../../../components/status_list'; import { expandTimeline, scrollTopTimeline } from '../../../actions/timelines'; import Immutable from 'immutable'; +import { createSelector } from 'reselect'; + +const getStatusIds = createSelector([ + (state, { type }) => state.getIn(['settings', type]), + (state, { type }) => state.getIn(['timelines', type, 'items'], Immutable.List()), + (state) => state.get('statuses') +], (columnSettings, statusIds, statuses) => statusIds.filter(id => { + const statusForId = statuses.get(id); + let showStatus = true; + + if (columnSettings.getIn(['shows', 'reblog']) === false) { + showStatus = showStatus && statusForId.get('reblog') === null; + } + + if (columnSettings.getIn(['shows', 'reply']) === false) { + showStatus = showStatus && statusForId.get('in_reply_to_id') === null; + } + + if (columnSettings.getIn(['regex', 'body'], '').trim().length > 0) { + try { + const regex = new RegExp(columnSettings.getIn(['regex', 'body']).trim(), 'i'); + showStatus = showStatus && !regex.test(statusForId.get('reblog') ? statuses.getIn([statusForId.get('reblog'), 'content']) : statusForId.get('content')); + } catch(e) { + // Bad regex, don't affect filters + } + } + + return showStatus; +})); const mapStateToProps = (state, props) => ({ - statusIds: state.getIn(['timelines', props.type, 'items'], Immutable.List()) + statusIds: getStatusIds(state, props) }); -const mapDispatchToProps = function (dispatch, props) { - return { - onScrollToBottom () { - dispatch(scrollTopTimeline(props.type, false)); - dispatch(expandTimeline(props.type, props.id)); - }, +const mapDispatchToProps = (dispatch, { type, id }) => ({ - onScrollToTop () { - dispatch(scrollTopTimeline(props.type, true)); - }, + onScrollToBottom () { + dispatch(scrollTopTimeline(type, false)); + dispatch(expandTimeline(type, id)); + }, - onScroll () { - dispatch(scrollTopTimeline(props.type, false)); - } - }; -}; + onScrollToTop () { + dispatch(scrollTopTimeline(type, true)); + }, + + onScroll () { + dispatch(scrollTopTimeline(type, false)); + } + +}); export default connect(mapStateToProps, mapDispatchToProps)(StatusList); diff --git a/app/assets/javascripts/components/locales/de.jsx b/app/assets/javascripts/components/locales/de.jsx index c37a71c21..7d32824f1 100644 --- a/app/assets/javascripts/components/locales/de.jsx +++ b/app/assets/javascripts/components/locales/de.jsx @@ -65,7 +65,13 @@ const en = { "notifications.column_settings.mention": "Erwähnungen:", "notifications.column_settings.reblog": "Geteilte Beiträge:", "follow_request.authorize": "Erlauben", - "follow_request.reject": "Ablehnen" + "follow_request.reject": "Ablehnen", + "home.column_settings.basic": "Einfach", + "home.column_settings.advanced": "Fortgeschritten", + "home.column_settings.show_reblogs": "Geteilte Beiträge anzeigen", + "home.column_settings.show_replies": "Antworten anzeigen", + "home.column_settings.filter_regex": "Filter durch reguläre Ausdrücke", + "missing_indicator.label": "Nicht gefunden" }; export default en; diff --git a/app/assets/javascripts/components/reducers/settings.jsx b/app/assets/javascripts/components/reducers/settings.jsx index 2a834d81c..8bd9edae2 100644 --- a/app/assets/javascripts/components/reducers/settings.jsx +++ b/app/assets/javascripts/components/reducers/settings.jsx @@ -3,6 +3,13 @@ import { STORE_HYDRATE } from '../actions/store'; import Immutable from 'immutable'; const initialState = Immutable.Map({ + home: Immutable.Map({ + shows: Immutable.Map({ + reblog: true, + reply: true + }) + }), + notifications: Immutable.Map({ alerts: Immutable.Map({ follow: true, diff --git a/app/assets/stylesheets/components.scss b/app/assets/stylesheets/components.scss index 0abe8c808..f1edfce9d 100644 --- a/app/assets/stylesheets/components.scss +++ b/app/assets/stylesheets/components.scss @@ -649,4 +649,16 @@ right: 8px !important; left: initial !important; } -} \ No newline at end of file +} + +.setting-text { + color: #9baec8; + background: transparent; + border: none; + border-bottom: 2px solid #9baec8; + + &:focus, &:active { + color: #fff; + border-bottom-color: #2b90d9; + } +} diff --git a/app/controllers/api/web/settings_controller.rb b/app/controllers/api/web/settings_controller.rb index e6f690114..c00e016a4 100644 --- a/app/controllers/api/web/settings_controller.rb +++ b/app/controllers/api/web/settings_controller.rb @@ -6,7 +6,7 @@ class Api::Web::SettingsController < ApiController before_action :require_user! def update - setting = Web::Setting.where(user: current_user).first_or_initialize(user: current_user) + setting = ::Web::Setting.where(user: current_user).first_or_initialize(user: current_user) setting.data = params[:data] setting.save! diff --git a/package.json b/package.json index 6a072ca06..8c75d632b 100644 --- a/package.json +++ b/package.json @@ -53,5 +53,8 @@ "sass-loader": "^4.0.2", "sinon": "^1.17.6", "style-loader": "^0.13.1" + }, + "dependencies": { + "webpack": "^1.14.0" } } diff --git a/yarn.lock b/yarn.lock index 948de9ba8..fac04d0b3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1124,6 +1124,12 @@ browser-stdout@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f" +browserify-aes@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-0.4.0.tgz#067149b668df31c4b58533e02d01e806d8608e2c" + dependencies: + inherits "^2.0.1" + browserify-aes@^1.0.0, browserify-aes@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.0.6.tgz#5e7725dbdef1fd5930d4ebab48567ce451c48a0a" @@ -1186,7 +1192,7 @@ browserify-sign@^4.0.0: inherits "^2.0.1" parse-asn1 "^5.0.0" -browserify-zlib@~0.1.2, browserify-zlib@~0.1.4: +browserify-zlib@^0.1.4, browserify-zlib@~0.1.2, browserify-zlib@~0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.1.4.tgz#bb35f8a519f600e0fa6b8485241c979d0141fb2d" dependencies: @@ -1520,7 +1526,7 @@ constants-browserify@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-0.0.1.tgz#92577db527ba6c4cf0a4568d84bc031f441e21f2" -constants-browserify@~1.0.0: +constants-browserify@^1.0.0, constants-browserify@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" @@ -1596,6 +1602,15 @@ cryptiles@2.x.x: dependencies: boom "2.x.x" +crypto-browserify@3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.3.0.tgz#b9fc75bb4a0ed61dcf1cd5dae96eb30c9c3e506c" + dependencies: + browserify-aes "0.4.0" + pbkdf2-compat "2.0.1" + ripemd160 "0.2.0" + sha.js "2.2.6" + crypto-browserify@^3.0.0: version "3.11.0" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.11.0.tgz#3652a0906ab9b2a7e0c3ce66a408e957a2485522" @@ -2559,7 +2574,7 @@ https-browserify@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.0.tgz#b3ffdfe734b2a3d4a9efd58e8654c91fce86eafd" -https-browserify@~0.0.0: +https-browserify@0.0.1, https-browserify@~0.0.0: version "0.0.1" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82" @@ -3458,6 +3473,34 @@ node-libs-browser@^0.6.0: util "~0.10.3" vm-browserify "0.0.4" +node-libs-browser@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-0.7.0.tgz#3e272c0819e308935e26674408d7af0e1491b83b" + dependencies: + assert "^1.1.1" + browserify-zlib "^0.1.4" + buffer "^4.9.0" + console-browserify "^1.1.0" + constants-browserify "^1.0.0" + crypto-browserify "3.3.0" + domain-browser "^1.1.1" + events "^1.0.0" + https-browserify "0.0.1" + os-browserify "^0.2.0" + path-browserify "0.0.0" + process "^0.11.0" + punycode "^1.2.4" + querystring-es3 "^0.2.0" + readable-stream "^2.0.5" + stream-browserify "^2.0.1" + stream-http "^2.3.1" + string_decoder "^0.10.25" + timers-browserify "^2.0.2" + tty-browserify "0.0.0" + url "^0.11.0" + util "^0.10.3" + vm-browserify "0.0.4" + node-pre-gyp@^0.6.29: version "0.6.30" resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.30.tgz#64d3073a6f573003717ccfe30c89023297babba1" @@ -3663,6 +3706,10 @@ optionator@^0.8.1: type-check "~0.3.2" wordwrap "~1.0.0" +os-browserify@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.2.1.tgz#63fc4ccee5d2d7763d26bbf8601078e6c2e0044f" + os-browserify@~0.1.1, os-browserify@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.1.2.tgz#49ca0293e0b19590a5f5de10c7f265a617d8fe54" @@ -4133,7 +4180,7 @@ query-string@^4.1.0: object-assign "^4.1.0" strict-uri-encode "^1.0.0" -querystring-es3@~0.2.0: +querystring-es3@^0.2.0, querystring-es3@~0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" @@ -4397,7 +4444,7 @@ readable-stream@1.1, readable-stream@^1.0.27-1, readable-stream@^1.1.13: isarray "0.0.1" string_decoder "~0.10.x" -"readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.0, readable-stream@~2.1.4: +"readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.1.0, readable-stream@~2.1.4: version "2.1.5" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.1.5.tgz#66fa8b720e1438b364681f2ad1a63c618448c9d0" dependencies: @@ -4689,6 +4736,10 @@ set-immediate-shim@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" +setimmediate@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + setprototypeof@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.1.tgz#52009b27888c4dc48f591949c0a8275834c1ca7e" @@ -4784,6 +4835,10 @@ source-list-map@^0.1.4, source-list-map@~0.1.0: version "0.1.6" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-0.1.6.tgz#e1e6f94f0b40c4d28dcf8f5b8766e0e45636877f" +source-list-map@~0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-0.1.7.tgz#d4b5ce2a46535c72c7e8527c71a77d250618172e" + source-map-support@^0.4.2: version "0.4.3" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.3.tgz#693c8383d4389a4569486987c219744dfc601685" @@ -4861,7 +4916,7 @@ stream-browserify@^1.0.0: inherits "~2.0.1" readable-stream "^1.0.27-1" -stream-browserify@^2.0.0: +stream-browserify@^2.0.0, stream-browserify@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db" dependencies: @@ -4879,7 +4934,7 @@ stream-consume@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/stream-consume/-/stream-consume-0.1.0.tgz#a41ead1a6d6081ceb79f65b061901b6d8f3d1d0f" -stream-http@^2.0.0: +stream-http@^2.0.0, stream-http@^2.3.1: version "2.4.0" resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.4.0.tgz#9599aa8e263667ce4190e0dc04a1d065d3595a7e" dependencies: @@ -4924,7 +4979,7 @@ string.prototype.padstart@^3.0.0: es-abstract "^1.4.3" function-bind "^1.0.2" -string_decoder@~0.10.0, string_decoder@~0.10.25, string_decoder@~0.10.x: +string_decoder@^0.10.25, string_decoder@~0.10.0, string_decoder@~0.10.25, string_decoder@~0.10.x: version "0.10.31" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" @@ -5051,6 +5106,12 @@ timers-browserify@^1.0.1: dependencies: process "~0.11.0" +timers-browserify@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.2.tgz#ab4883cf597dcd50af211349a00fbca56ac86b86" + dependencies: + setimmediate "^1.0.4" + to-arraybuffer@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" @@ -5125,6 +5186,15 @@ uglify-js@~2.6.0: uglify-to-browserify "~1.0.0" yargs "~3.10.0" +uglify-js@~2.7.3: + version "2.7.5" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.7.5.tgz#4612c0c7baaee2ba7c487de4904ae122079f2ca8" + dependencies: + async "~0.2.6" + source-map "~0.5.1" + uglify-to-browserify "~1.0.0" + yargs "~3.10.0" + uglify-to-browserify@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" @@ -5162,16 +5232,16 @@ url-loader@^0.5.7: loader-utils "0.2.x" mime "1.2.x" -url@~0.10.1: - version "0.10.3" - resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64" +url@^0.11.0, url@~0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" dependencies: punycode "1.3.2" querystring "0.2.0" -url@~0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" +url@~0.10.1: + version "0.10.3" + resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64" dependencies: punycode "1.3.2" querystring "0.2.0" @@ -5184,7 +5254,7 @@ util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" -util@0.10.3, "util@>=0.10.3 <1", util@~0.10.1, util@~0.10.3: +util@0.10.3, "util@>=0.10.3 <1", util@^0.10.3, util@~0.10.1, util@~0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" dependencies: @@ -5260,6 +5330,13 @@ webpack-core@~0.6.0: source-list-map "~0.1.0" source-map "~0.4.1" +webpack-core@~0.6.9: + version "0.6.9" + resolved "https://registry.yarnpkg.com/webpack-core/-/webpack-core-0.6.9.tgz#fc571588c8558da77be9efb6debdc5a3b172bdc2" + dependencies: + source-list-map "~0.1.7" + source-map "~0.4.1" + webpack-dev-middleware@^1.6.0: version "1.8.4" resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.8.4.tgz#e8765c9122887ce9e3abd4cc9c3eb31b61e0948d" @@ -5298,6 +5375,26 @@ webpack@^1.13.1: watchpack "^0.2.1" webpack-core "~0.6.0" +webpack@^1.14.0: + version "1.14.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-1.14.0.tgz#54f1ffb92051a328a5b2057d6ae33c289462c823" + dependencies: + acorn "^3.0.0" + async "^1.3.0" + clone "^1.0.2" + enhanced-resolve "~0.9.0" + interpret "^0.6.4" + loader-utils "^0.2.11" + memory-fs "~0.3.0" + mkdirp "~0.5.0" + node-libs-browser "^0.7.0" + optimist "~0.6.0" + supports-color "^3.1.0" + tapable "~0.1.8" + uglify-js "~2.7.3" + watchpack "^0.2.1" + webpack-core "~0.6.9" + whatwg-fetch@>=0.10.0: version "1.0.0" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-1.0.0.tgz#01c2ac4df40e236aaa18480e3be74bd5c8eb798e" -- cgit From 18b11100e7d66b5e5f5a2a364423582c6b7d75a9 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 10 Jan 2017 17:33:32 +0100 Subject: Fix issue when settings are not defined for column type --- .../components/features/ui/containers/status_list_container.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app') diff --git a/app/assets/javascripts/components/features/ui/containers/status_list_container.jsx b/app/assets/javascripts/components/features/ui/containers/status_list_container.jsx index 7b893711c..ffb6f6e79 100644 --- a/app/assets/javascripts/components/features/ui/containers/status_list_container.jsx +++ b/app/assets/javascripts/components/features/ui/containers/status_list_container.jsx @@ -5,7 +5,7 @@ import Immutable from 'immutable'; import { createSelector } from 'reselect'; const getStatusIds = createSelector([ - (state, { type }) => state.getIn(['settings', type]), + (state, { type }) => state.getIn(['settings', type], Immutable.Map()), (state, { type }) => state.getIn(['timelines', type, 'items'], Immutable.List()), (state) => state.get('statuses') ], (columnSettings, statusIds, statuses) => statusIds.filter(id => { -- cgit From f876a8681d50dbff31a9ad9626ebd3df52e4c98b Mon Sep 17 00:00:00 2001 From: Effy Elden Date: Wed, 11 Jan 2017 13:24:14 +1100 Subject: Update the Mastodon repository URL from Gargron/ to tootsuite/ in various places --- README.md | 12 ++++++------ app/views/about/index.html.haml | 2 +- app/views/layouts/public.html.haml | 2 +- spec/fixtures/xml/mastodon.atom | 8 ++++---- 4 files changed, 12 insertions(+), 12 deletions(-) (limited to 'app') diff --git a/README.md b/README.md index 0d9955d4f..7e95da41f 100644 --- a/README.md +++ b/README.md @@ -25,11 +25,11 @@ If you would like, you can [support the development of this project on Patreon][ ## Resources -- [List of Mastodon instances](https://github.com/Gargron/mastodon/wiki/List-of-Mastodon-instances) +- [List of Mastodon instances](https://github.com/tootsuite/mastodon/wiki/List-of-Mastodon-instances) - [Use this tool to find Twitter friends on Mastodon](https://mastodon-bridge.herokuapp.com) -- [API overview](https://github.com/Gargron/mastodon/wiki/API) -- [How to use the API via cURL/oAuth](https://github.com/Gargron/mastodon/wiki/Testing-with-cURL) -- [Frequently Asked Questions](https://github.com/Gargron/mastodon/wiki/FAQ) +- [API overview](https://github.com/tootsuite/mastodon/wiki/API) +- [How to use the API via cURL/oAuth](https://github.com/tootsuite/mastodon/wiki/Testing-with-cURL) +- [Frequently Asked Questions](https://github.com/tootsuite/mastodon/wiki/FAQ) - [List of apps](https://github.com/tootsuite/mastodon/wiki/Apps) ## Features @@ -116,7 +116,7 @@ Which will re-create the updated containers, leaving databases and data as is. D ## Deployment without Docker -Docker is great for quickly trying out software, but it has its drawbacks too. If you prefer to run Mastodon without using Docker, refer to the [production guide](https://github.com/Gargron/mastodon/wiki/Production-guide) for examples, configuration and instructions. +Docker is great for quickly trying out software, but it has its drawbacks too. If you prefer to run Mastodon without using Docker, refer to the [production guide](https://github.com/tootsuite/mastodon/wiki/Production-guide) for examples, configuration and instructions. ## Development with Vagrant @@ -130,7 +130,7 @@ This is optional, but will update your 'hosts' file when you start the virtual m To create and provision a new virtual machine for Mastodon development: - git clone git@github.com:Gargron/mastodon.git + git clone git@github.com:tootsuite/mastodon.git cd mastodon vagrant up diff --git a/app/views/about/index.html.haml b/app/views/about/index.html.haml index 6bc416cbd..0c6516036 100644 --- a/app/views/about/index.html.haml +++ b/app/views/about/index.html.haml @@ -27,7 +27,7 @@ .actions .info = link_to t('about.terms'), terms_path - = link_to t('about.source_code'), 'https://github.com/Gargron/mastodon' + = link_to t('about.source_code'), 'https://github.com/tootsuite/mastodon' = link_to t('about.get_started'), new_user_registration_path, class: 'button webapp-btn' = link_to t('auth.login'), new_user_session_path, class: 'button webapp-btn' diff --git a/app/views/layouts/public.html.haml b/app/views/layouts/public.html.haml index e6de7d017..808fb0a0e 100644 --- a/app/views/layouts/public.html.haml +++ b/app/views/layouts/public.html.haml @@ -6,6 +6,6 @@ .footer %span.domain= link_to Rails.configuration.x.local_domain, root_path %span.powered-by - = t('generic.powered_by', link: link_to('Mastodon', 'https://github.com/Gargron/mastodon')).html_safe + = t('generic.powered_by', link: link_to('Mastodon', 'https://github.com/tootsuite/mastodon')).html_safe = render template: "layouts/application" diff --git a/spec/fixtures/xml/mastodon.atom b/spec/fixtures/xml/mastodon.atom index ce28cd77b..9ece3bc2e 100644 --- a/spec/fixtures/xml/mastodon.atom +++ b/spec/fixtures/xml/mastodon.atom @@ -107,14 +107,14 @@ https://mastodon.social/users/Gargron Gargron Gargron@mastodon.social - Developer of Mastodon, a GNU social alternative: https://github.com/Gargron/mastodon + Developer of Mastodon, a GNU social alternative: https://github.com/tootsuite/mastodon Gargron Eugen - Developer of Mastodon, a GNU social alternative: https://github.com/Gargron/mastodon + Developer of Mastodon, a GNU social alternative: https://github.com/tootsuite/mastodon @@ -192,14 +192,14 @@ https://mastodon.social/users/Gargron Gargron Gargron@mastodon.social - Developer of Mastodon, a GNU social alternative: https://github.com/Gargron/mastodon + Developer of Mastodon, a GNU social alternative: https://github.com/tootsuite/mastodon Gargron Eugen - Developer of Mastodon, a GNU social alternative: https://github.com/Gargron/mastodon + Developer of Mastodon, a GNU social alternative: https://github.com/tootsuite/mastodon -- cgit From 3ad0496ccb0f0a6f1c180b08367124e26abff682 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 11 Jan 2017 04:21:49 +0100 Subject: Better animations --- .../components/components/column_collapsable.jsx | 2 +- .../components/components/icon_button.jsx | 22 +++++++++++++++++----- .../components/components/status_action_bar.jsx | 2 +- .../features/status/components/action_bar.jsx | 2 +- 4 files changed, 20 insertions(+), 8 deletions(-) (limited to 'app') diff --git a/app/assets/javascripts/components/components/column_collapsable.jsx b/app/assets/javascripts/components/components/column_collapsable.jsx index abd65d633..8d74fd8a7 100644 --- a/app/assets/javascripts/components/components/column_collapsable.jsx +++ b/app/assets/javascripts/components/components/column_collapsable.jsx @@ -45,7 +45,7 @@ const ColumnCollapsable = React.createClass({
- + {({ opacity, height }) =>
{children} diff --git a/app/assets/javascripts/components/components/icon_button.jsx b/app/assets/javascripts/components/components/icon_button.jsx index e9a7228e4..f9b6192c0 100644 --- a/app/assets/javascripts/components/components/icon_button.jsx +++ b/app/assets/javascripts/components/components/icon_button.jsx @@ -1,4 +1,5 @@ import PureRenderMixin from 'react-addons-pure-render-mixin'; +import { Motion, spring } from 'react-motion'; const IconButton = React.createClass({ @@ -10,14 +11,16 @@ const IconButton = React.createClass({ active: React.PropTypes.bool, style: React.PropTypes.object, activeStyle: React.PropTypes.object, - disabled: React.PropTypes.bool + disabled: React.PropTypes.bool, + animate: React.PropTypes.bool }, getDefaultProps () { return { size: 18, active: false, - disabled: false + disabled: false, + animate: false }; }, @@ -49,9 +52,18 @@ const IconButton = React.createClass({ } return ( -