From 1bbcd71cd403da843e8a4a67926a4394b31a63fd Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 4 Jan 2017 15:31:25 +0100 Subject: [PATCH 001/175] Fix #390 - fix redirect after sign-up (to login page instead of homepage) --- app/controllers/auth/registrations_controller.rb | 4 ++++ spec/controllers/auth/registrations_controller_spec.rb | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) 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 diff --git a/spec/controllers/auth/registrations_controller_spec.rb b/spec/controllers/auth/registrations_controller_spec.rb index f7ebebbcb..27ad6cbde 100644 --- a/spec/controllers/auth/registrations_controller_spec.rb +++ b/spec/controllers/auth/registrations_controller_spec.rb @@ -20,8 +20,8 @@ RSpec.describe Auth::RegistrationsController, type: :controller do post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678' } } end - it 'redirects to home page' do - expect(response).to redirect_to root_path + it 'redirects to login page' do + expect(response).to redirect_to new_user_session_path end it 'creates user' do From 2b0b7ff1b8b134e97c8827721a59cac21897932c Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 4 Jan 2017 15:35:36 +0100 Subject: [PATCH 002/175] Fix #385 - /web now loads a page --- config/routes.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/routes.rb b/config/routes.rb index 18c239c48..e46b27f1f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -136,7 +136,7 @@ Rails.application.routes.draw do end end - get '/web/*any', to: 'home#index', as: :web + get '/web/(*any)', to: 'home#index', as: :web get :about, to: 'about#index' get :terms, to: 'about#terms' From 98b83aca372fabdfc32b05c1eb72c80a79102e53 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 4 Jan 2017 15:43:28 +0100 Subject: [PATCH 003/175] Fix #391 - relative timestamps now contain an exact datetime in title --- .../components/relative_timestamp.jsx | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) 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'; +import { injectIntl, FormattedRelative } from 'react-intl'; -const RelativeTimestamp = ({ timestamp }) => { - return ; +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); From 3807b0b171d588ccccfc6210c823e5ce87b9b90f Mon Sep 17 00:00:00 2001 From: Jessica Stokes Date: Wed, 4 Jan 2017 17:25:48 -0800 Subject: [PATCH 004/175] 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. --- .../features/ui/components/tabs_bar.jsx | 3 +-- app/assets/stylesheets/components.scss | 27 ++++++++++++++++--- 2 files changed, 24 insertions(+), 6 deletions(-) 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; From 5b75f6d0f36940f40db9f1064f37b0069068b691 Mon Sep 17 00:00:00 2001 From: Jessica Stokes Date: Wed, 4 Jan 2017 17:32:11 -0800 Subject: [PATCH 005/175] Make tabs bar take up less room on 4-inch phones --- .../javascripts/components/features/ui/components/tabs_bar.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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', From 312736cd1baaea12935e7decbb457c06299cc7d4 Mon Sep 17 00:00:00 2001 From: Jessica Stokes Date: Wed, 4 Jan 2017 17:33:05 -0800 Subject: [PATCH 006/175] =?UTF-8?q?Stop=20Mastodon=20friend=20from=20overl?= =?UTF-8?q?apping=20text=20=F0=9F=90=98?= 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(-) 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; } From aaee8c9b5d935143cf749fbea78f8cfee74ee37f Mon Sep 17 00:00:00 2001 From: Jessica Stokes Date: Wed, 4 Jan 2017 17:44:41 -0800 Subject: [PATCH 007/175] Disallow compose navbar from being shrunk --- .../components/features/compose/components/navigation_bar.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ( -
+
From bb033c1d37db11a871011ac8cdf6737721fbc13e Mon Sep 17 00:00:00 2001 From: Jessica Stokes Date: Wed, 4 Jan 2017 18:00:50 -0800 Subject: [PATCH 008/175] "Reblog" -> "boost" in more places A couple of places were using "reblog" rather than "boost" - this updates them to match the web UI --- .../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(-) 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 From cbcb7e1241c4d0655ca7c6ad0840585d61e23e03 Mon Sep 17 00:00:00 2001 From: Jessica Stokes Date: Wed, 4 Jan 2017 18:10:45 -0800 Subject: [PATCH 009/175] Don't render the media list when there's no media This stops the empty compose view from scrolling on 4-inch devices. --- .../features/compose/components/upload_form.jsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) 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}
); From 98729d50c80760e076607ecf625a95caea817aed Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 5 Jan 2017 03:14:33 +0100 Subject: [PATCH 010/175] Make shortcode emojis work, make getting started area scrollable --- app/assets/javascripts/components/emoji.jsx | 2 +- .../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(-) 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%; } From 251b04298e826818ecb48c2fdf96a83649e14e93 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 5 Jan 2017 03:17:23 +0100 Subject: [PATCH 011/175] 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(-) 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) From 6c28886317bdf837e7e3f399115af91ac282735e Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 5 Jan 2017 03:28:21 +0100 Subject: [PATCH 012/175] 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(-) 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 From 1da73ecadedf26c5f04dc2de3bb65349ffc52ab9 Mon Sep 17 00:00:00 2001 From: Jessica Stokes Date: Wed, 4 Jan 2017 18:29:43 -0800 Subject: [PATCH 013/175] Fix Command-enter tooting metaKey is only set correctly on keyDown, not keyUp, so this swaps to using that --- .../components/components/autosuggest_textarea.jsx | 9 ++++++++- .../features/compose/components/compose_form.jsx | 4 ++-- 2 files changed, 10 insertions(+), 3 deletions(-) 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} From cc46c6b4936a3dbe1c38b97ab77a3fcfd3f22f03 Mon Sep 17 00:00:00 2001 From: Jessica Stokes Date: Wed, 4 Jan 2017 18:31:45 -0800 Subject: [PATCH 014/175] 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(-) 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.')); } } } From 0c600e9db6f343cdf8068554e27590e126035229 Mon Sep 17 00:00:00 2001 From: Jessica Stokes Date: Wed, 4 Jan 2017 19:30:02 -0800 Subject: [PATCH 015/175] Move "getting started" to its own route --- app/assets/javascripts/components/containers/mastodon.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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({ - + + From 9c493b1ea28ac3d36be367c019236394b6bd341d Mon Sep 17 00:00:00 2001 From: Jessica Stokes Date: Wed, 4 Jan 2017 19:30:22 -0800 Subject: [PATCH 016/175] Replace "Public" in tab bar with "More" hamburger --- .../javascripts/components/features/ui/components/tabs_bar.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 = () => { - +
); }; From 05cc5636d8d56f95e1428408f6fce87ffb7c1a42 Mon Sep 17 00:00:00 2001 From: Jessica Stokes Date: Wed, 4 Jan 2017 19:30:39 -0800 Subject: [PATCH 017/175] Remove hamburger from "getting started" --- .../components/features/getting_started/index.jsx | 12 ------------ 1 file changed, 12 deletions(-) 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} From 7ac55d2674b65bae78e6559a51ce97d859074bba Mon Sep 17 00:00:00 2001 From: Jessica Stokes Date: Wed, 4 Jan 2017 19:47:02 -0800 Subject: [PATCH 018/175] 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 +- .../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(-) 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": "Відмінити", From c100b83b98b8a6ce24d7f0d9140f47e722b17a06 Mon Sep 17 00:00:00 2001 From: Misty De Meo Date: Wed, 4 Jan 2017 20:04:14 -0800 Subject: [PATCH 019/175] Automatically position cursor when writing a reply toot --- .../features/compose/components/compose_form.jsx | 7 +++++++ 1 file changed, 7 insertions(+) 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(); } }, From 1f3c895ffb320c188b77b0036603b40a22733902 Mon Sep 17 00:00:00 2001 From: Jessica Stokes Date: Wed, 4 Jan 2017 20:24:27 -0800 Subject: [PATCH 020/175] 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 +- .../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(-) 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; From c318e6e42e2903af90be205760a5a2ba00e378b8 Mon Sep 17 00:00:00 2001 From: Misty De Meo Date: Wed, 4 Jan 2017 22:23:02 -0800 Subject: [PATCH 021/175] 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(-) 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" From 53b765f4b150d7671d697eed7333c948622045cf Mon Sep 17 00:00:00 2001 From: Misty De Meo Date: Wed, 4 Jan 2017 22:47:51 -0800 Subject: [PATCH 022/175] Bump emoji requirement to Unicode 9 --- app/assets/javascripts/components/emoji.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 { From 9e6ceb3201a2737e676decf3df9605714632a300 Mon Sep 17 00:00:00 2001 From: Eugen Date: Thu, 5 Jan 2017 13:45:21 +0100 Subject: [PATCH 023/175] 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(-) 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" From 10e6288444567106570baf317801d99989e2df83 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 5 Jan 2017 13:59:58 +0100 Subject: [PATCH 024/175] Revert to Roboto for all --- app/assets/stylesheets/application.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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; From ca7dce4a5a0234461fee554153d9328bc246ee55 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 5 Jan 2017 14:06:09 +0100 Subject: [PATCH 025/175] Fix selection resetting in compose form after unrelated data updates --- .../features/compose/components/compose_form.jsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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(); } }, From a1de2e332d4406eb0d5934aa22fb96958d291a5f Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 5 Jan 2017 14:18:38 +0100 Subject: [PATCH 026/175] Fix compose form bug --- .../components/features/compose/components/compose_form.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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. From 00b9ba64c92715ba86d45209215694497eecefce Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 5 Jan 2017 14:23:59 +0100 Subject: [PATCH 027/175] Fixed unexpected error --- .../components/features/compose/components/compose_form.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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(); From 10a9ebae3b2fc0addb4604798a62d6c4e066d491 Mon Sep 17 00:00:00 2001 From: Effy Elden Date: Fri, 6 Jan 2017 08:26:45 +1100 Subject: [PATCH 028/175] 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(-) 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 }); } }; }; From 819bfb75c6135d7acd4d5473f47c4e10497dec2b Mon Sep 17 00:00:00 2001 From: Effy Elden Date: Fri, 6 Jan 2017 09:47:40 +1100 Subject: [PATCH 029/175] 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(+) 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 } From 28a2f79dff04c8593e4228d8a3fa38dabafe3dbe Mon Sep 17 00:00:00 2001 From: Jessica Stokes Date: Thu, 5 Jan 2017 15:16:13 -0800 Subject: [PATCH 030/175] Upgrade EmojiOne to 2.2.7 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds support for, among other things, 🏳️‍🌈 --- package.json | 3 +++ yarn.lock | 10 +++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 05663a729..3a9365d3e 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": { + "emojione": "latest" } } diff --git a/yarn.lock b/yarn.lock index f71a8ae10..db5f7d408 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1935,9 +1935,9 @@ elliptic@^6.0.0: hash.js "^1.0.0" inherits "^2.0.1" -emojione: - version "2.2.6" - resolved "https://registry.yarnpkg.com/emojione/-/emojione-2.2.6.tgz#67dec452937d5b14ee669207ea41cdb1f69fb8f6" +emojione@latest: + version "2.2.7" + resolved "https://registry.yarnpkg.com/emojione/-/emojione-2.2.7.tgz#46457cf6b9b2f8da13ae8a2e4e547de06ee15e96" emojis-list@^2.0.0: version "2.1.0" @@ -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: From 7b9f8766e88dceb9519085deada3fa673e4c015b Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 6 Jan 2017 00:21:12 +0100 Subject: [PATCH 031/175] 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 ++++++++++++++++++ ...24407_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 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 From 18deeb9db58391ed9ecb4d4d48ae47acb3e7fb46 Mon Sep 17 00:00:00 2001 From: Greg V Date: Fri, 6 Jan 2017 18:08:40 +0300 Subject: [PATCH 032/175] 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 ++++++----- .../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(-) 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 From 72c3a41befb6407f21068ef4cb9e34187e3515f0 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 6 Jan 2017 20:15:24 +0100 Subject: [PATCH 033/175] 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(-) 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 From 1bfbce7b4542739a3601e0722a975b450e86bfb2 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 6 Jan 2017 20:24:51 +0100 Subject: [PATCH 034/175] 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(-) 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 } From 989c3f40022bc65d69915be597acda3c4d58de60 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 6 Jan 2017 22:09:55 +0100 Subject: [PATCH 035/175] Add tab bar alternative to desktop UI, upgrade react & react-redux --- .../components/containers/mastodon.jsx | 7 +- .../features/compose/components/drawer.jsx | 73 ++++++++++++++++--- .../compose/components/navigation_bar.jsx | 2 +- .../components/features/compose/index.jsx | 5 +- .../features/ui/components/tabs_bar.jsx | 2 +- .../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(-) 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 Drawer = React.createClass({ +const tabStyle = { + display: 'block', + flex: '1 1 auto', + padding: '15px', + paddingBottom: '13px', + color: '#9baec8', + textDecoration: 'none', + textAlign: 'center', + fontSize: '16px', + borderBottom: '2px solid transparent' +}; - mixins: [PureRenderMixin], +const tabActiveStyle = { + color: '#2b90d9', + borderBottom: '2px solid #2b90d9' +}; - render () { - return ( -

- {this.props.children} +const Drawer = ({ children, withHeader, intl }) => { + let header = ''; + + if (withHeader) { + header = ( +
+ + + +
); } -}); + return ( +
+ {header} -export default Drawer; +
+ {children} +
+
+ ); +}; + +Drawer.propTypes = { + withHeader: React.PropTypes.bool, + children: React.PropTypes.node, + intl: React.PropTypes.object +}; + +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" From f1289ca3c0b0a785b47d569e919a345b08136d28 Mon Sep 17 00:00:00 2001 From: Effy Elden Date: Sat, 7 Jan 2017 23:22:24 +1100 Subject: [PATCH 036/175] Add Vagrant development environment support --- .gitignore | 3 ++ README.md | 30 +++++++++++++++++++ Vagrantfile | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 Vagrantfile diff --git a/.gitignore b/.gitignore index a60603c7d..7f51045aa 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,6 @@ public/assets .env.production node_modules/ neo4j/ + +# Ignore Vagrant files +.vagrant/ diff --git a/README.md b/README.md index 2d84062a7..59aa409d5 100644 --- a/README.md +++ b/README.md @@ -117,6 +117,36 @@ Which will re-create the updated containers, leaving databases and data as is. D 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. +## Development with Vagrant + +A quick way to get a development environment up and running is with Vagrant. You will need recent versions of [Vagrant](https://www.vagrantup.com/) and [VirtualBox](https://www.virtualbox.org/) installed. + +Install the latest version for your operating systems, and then run: + + vagrant plugin install vagrant-hostsupdater + +This is optional, but will update your 'hosts' file when you start the virtual machine, allowing you to access the site at http://mastodon.dev (instead of http://localhost:3000). + +To create and provision a new virtual machine for Mastodon development: + + git clone git@github.com:Gargron/mastodon.git + cd mastodon + vagrant up + +Running `vagrant up` for the first time will run provisioning, which will: + +- Download the Ubuntu 14.04 base image, if there isn't already a copy on your machine +- Create a new VirtualBox virtual machine from that image +- Run the provisioning script (located inside the Vagrantfile), which installs the system packages, Ruby gems, and JS modules required for Mastodon + +Once this has completed, the virtual machine will start a rails process. You can then access your development site at http://mastodon.dev (or at http://localhost:3000 if you haven't installed vagrants-hostupdater). Any changes you make should be reflected on the server instantly. + +When you are finished with your session, run `vagrant halt` to stop the VM. Next time, running `vagrant up` should boot the VM, and skip provsioning. + +If you no longer need your environment, or if things have gone terribly wrong, running `vagrant destroy` will delete the virtual machine (after which, running `vagrant up` will create a new one, and run provisioning). + + + ## Contributing You can open issues for bugs you've found or features you think are missing. You can also submit pull requests to this repository. This section may be updated with more details in the future. diff --git a/Vagrantfile b/Vagrantfile new file mode 100644 index 000000000..dabfd70c8 --- /dev/null +++ b/Vagrantfile @@ -0,0 +1,86 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +$script = <