commit: 75f80bef107cfe9e9c0e6ba3dc51ef86c89e40cc
parent: 23ebf60b95984764992c4b356048786ed0ab2953
Author: Eugen Rochko <eugen@zeonfederated.com>
Date: Mon, 9 Jan 2017 14:00:55 +0100
Persist UI settings, add missing localizations for German
Diffstat:
25 files changed, 305 insertions(+), 67 deletions(-)
diff --git 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 (<strong>%{self}</strong>) 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,3 @@
+Fabricator('Web::Setting') do
+
+end
diff --git 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
@@ -0,0 +1,5 @@
+require 'rails_helper'
+
+RSpec.describe Web::Setting, type: :model do
+
+end