Skip to content
Snippets Groups Projects
Commit 75f80bef authored by Eugen Rochko's avatar Eugen Rochko
Browse files

Persist UI settings, add missing localizations for German

parent 23ebf60b
No related branches found
No related tags found
No related merge requests found
Showing
with 194 additions and 65 deletions
......@@ -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
};
};
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()
});
};
};
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));
}
});
......
......@@ -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'))));
......
......@@ -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;
......@@ -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",
......
......@@ -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
});
......@@ -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;
}
};
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;
}
};
# 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
......@@ -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
......
......@@ -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)
......
module Web
def self.table_name_prefix
'web_'
end
end
# frozen_string_literal: true
class Web::Setting < ApplicationRecord
belongs_to :user
validates :user, uniqueness: true
end
- 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'
......
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 }
......@@ -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:
......
---
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
......
......@@ -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
......
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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment