From ae7b96abbf0c027f0f746ca8f83d06b7ecf2d925 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 19 Dec 2018 01:11:57 +0300 Subject: [PATCH 01/16] Fix resize not working properly for textareas --- .../post_status_form/post_status_form.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index 0ce2aff0..fa994c87 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -246,7 +246,7 @@ const PostStatusForm = { } this.$emit('posted') let el = this.$el.querySelector('textarea') - el.style.height = '16px' + el.style.height = undefined this.error = null } else { this.error = data.error @@ -294,13 +294,15 @@ const PostStatusForm = { e.dataTransfer.dropEffect = 'copy' }, resize (e) { - if (!e.target) { return } - const vertPadding = Number(window.getComputedStyle(e.target)['padding-top'].substr(0, 1)) + - Number(window.getComputedStyle(e.target)['padding-bottom'].substr(0, 1)) - e.target.style.height = 'auto' - e.target.style.height = `${e.target.scrollHeight - vertPadding}px` - if (e.target.value === '') { - e.target.style.height = '16px' + const target = e.target || e + if (!(target instanceof window.Element)) { return } + const vertPadding = Number(window.getComputedStyle(target)['padding-top'].substr(0, 1)) + + Number(window.getComputedStyle(target)['padding-bottom'].substr(0, 1)) + // Auto is needed to make textbox shrink when removing lines + target.style.height = 'auto' + target.style.height = `${target.scrollHeight - vertPadding}px` + if (target.value === '') { + target.style.height = undefined } }, clearError () { From afa2baec0cc57fd0beb3aac26048717569d5b116 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 19 Dec 2018 01:23:52 +0300 Subject: [PATCH 02/16] I suppose SOME browsers don't do this by default... --- src/components/post_status_form/post_status_form.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index fa994c87..46dcc8a6 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -32,6 +32,8 @@ const PostStatusForm = { }, mounted () { this.resize(this.$refs.textarea) + const textLength = this.$refs.textarea.value.length + this.$refs.textarea.setSelectionRange(textLength, textLength) if (this.replyTo) { this.$refs.textarea.focus() From 5970bf4e856b6ced0bbbf53268411f95ce3e1341 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Thu, 20 Dec 2018 07:27:29 +0300 Subject: [PATCH 03/16] fixed incorrect height when empty text --- src/components/post_status_form/post_status_form.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index 46dcc8a6..94c75b6a 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -304,7 +304,7 @@ const PostStatusForm = { target.style.height = 'auto' target.style.height = `${target.scrollHeight - vertPadding}px` if (target.value === '') { - target.style.height = undefined + target.style.height = null } }, clearError () { From d0b47488fb69b2b8449fa73e8d94e2cdcc9adda8 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Thu, 20 Dec 2018 09:15:30 +0300 Subject: [PATCH 04/16] default webPush to false, because having it at true leads to some problems with local dev mode. Instances can re-enable it, and BE can default it to true in config.exs --- src/modules/config.js | 2 +- static/config.json | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/modules/config.js b/src/modules/config.js index ccfd0190..c9528f6f 100644 --- a/src/modules/config.js +++ b/src/modules/config.js @@ -24,7 +24,7 @@ const defaultState = { likes: true, repeats: true }, - webPushNotifications: true, + webPushNotifications: false, muteWords: [], highlight: {}, interfaceLanguage: browserLocale, diff --git a/static/config.json b/static/config.json index 7887f930..f508eea1 100644 --- a/static/config.json +++ b/static/config.json @@ -16,5 +16,6 @@ "alwaysShowSubjectInput": true, "hidePostStats": false, "hideUserStats": false, - "loginMethod": "password" + "loginMethod": "password", + "webPushNotifications": false } From 957b2a6f7ef8ca381fc44b79a17639e400407b67 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Thu, 20 Dec 2018 09:17:59 +0300 Subject: [PATCH 05/16] simplified some code, made it possible to unregister serviceworker altogether --- src/main.js | 23 ++++++++++------------- src/modules/users.js | 5 ++++- src/services/push/push.js | 17 ++++++++++++++++- 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/src/main.js b/src/main.js index bf92e78e..c22a762e 100644 --- a/src/main.js +++ b/src/main.js @@ -54,24 +54,21 @@ const persistedStateOptions = { const registerPushNotifications = store => { store.subscribe((mutation, state) => { const vapidPublicKey = state.instance.vapidPublicKey + const webPushNotification = state.config.webPushNotifications const permission = state.interface.notificationPermission === 'granted' - const isUserMutation = mutation.type === 'setCurrentUser' - - if (isUserMutation && vapidPublicKey && permission) { - return store.dispatch('registerPushNotifications') - } - const user = state.users.currentUser + + const isUserMutation = mutation.type === 'setCurrentUser' const isVapidMutation = mutation.type === 'setInstanceOption' && mutation.payload.name === 'vapidPublicKey' - - if (isVapidMutation && user && permission) { - return store.dispatch('registerPushNotifications') - } - const isPermMutation = mutation.type === 'setNotificationPermission' && mutation.payload === 'granted' + const isUserConfigMutation = mutation.type === 'setOption' && mutation.payload.name === 'webPushNotifications' - if (isPermMutation && user && vapidPublicKey) { - return store.dispatch('registerPushNotifications') + if (isUserMutation || isVapidMutation || isPermMutation || isUserConfigMutation) { + if (user && vapidPublicKey && permission && webPushNotification) { + return store.dispatch('registerPushNotifications') + } else if (isUserConfigMutation && !webPushNotification) { + return store.dispatch('unregisterPushNotifications') + } } }) } diff --git a/src/modules/users.js b/src/modules/users.js index 13d3f26e..2ea0919e 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -1,7 +1,7 @@ import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js' import { compact, map, each, merge } from 'lodash' import { set } from 'vue' -import registerPushNotifications from '../services/push/push.js' +import { registerPushNotifications, unregisterPushNotifications } from '../services/push/push.js' import oauthApi from '../services/new_api/oauth' import { humanizeErrors } from './errors' @@ -116,6 +116,9 @@ const users = { registerPushNotifications(isEnabled, vapidPublicKey, token) }, + unregisterPushNotifications (store) { + unregisterPushNotifications() + }, addNewStatuses (store, { statuses }) { const users = map(statuses, 'user') const retweetedUsers = compact(map(statuses, 'retweeted_status.user')) diff --git a/src/services/push/push.js b/src/services/push/push.js index 1ac304d1..ff67fd5a 100644 --- a/src/services/push/push.js +++ b/src/services/push/push.js @@ -19,6 +19,12 @@ function registerServiceWorker () { .catch((err) => console.error('Unable to register service worker.', err)) } +function unregisterServiceWorker () { + return runtime.register() + .then((registration) => registration.unregister()) + .catch((err) => console.error('Unable to unregister serviceworker', err)) +} + function subscribe (registration, isEnabled, vapidPublicKey) { if (!isEnabled) return Promise.reject(new Error('Web Push is disabled in config')) if (!vapidPublicKey) return Promise.reject(new Error('VAPID public key is not found')) @@ -59,7 +65,7 @@ function sendSubscriptionToBackEnd (subscription, token) { }) } -export default function registerPushNotifications (isEnabled, vapidPublicKey, token) { +export function registerPushNotifications (isEnabled, vapidPublicKey, token) { if (isPushSupported()) { registerServiceWorker() .then((registration) => subscribe(registration, isEnabled, vapidPublicKey)) @@ -67,3 +73,12 @@ export default function registerPushNotifications (isEnabled, vapidPublicKey, to .catch((e) => console.warn(`Failed to setup Web Push Notifications: ${e.message}`)) } } + +export function unregisterPushNotifications (isEnabled, vapidPublicKey, token) { + if (isPushSupported()) { + unregisterServiceWorker() + .then((registration) => subscribe(registration, isEnabled, vapidPublicKey)) + .then((subscription) => sendSubscriptionToBackEnd(subscription, token)) + .catch((e) => console.warn(`Failed to setup Web Push Notifications: ${e.message}`)) + } +} From 6fa993c12241d5d5d740fd83fb1a8c29bb691659 Mon Sep 17 00:00:00 2001 From: Hannah Ward Date: Tue, 25 Dec 2018 00:34:33 +0000 Subject: [PATCH 06/16] remove unneeded numbered variables who_to_follow --- .../who_to_follow_panel.js | 98 ++++++------------- .../who_to_follow_panel.vue | 9 +- 2 files changed, 38 insertions(+), 69 deletions(-) diff --git a/src/components/who_to_follow_panel/who_to_follow_panel.js b/src/components/who_to_follow_panel/who_to_follow_panel.js index c2df6899..3ead0b72 100644 --- a/src/components/who_to_follow_panel/who_to_follow_panel.js +++ b/src/components/who_to_follow_panel/who_to_follow_panel.js @@ -2,62 +2,31 @@ import apiService from '../../services/api/api.service.js' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' function showWhoToFollow (panel, reply) { - var users = reply - var cn - var index - var step = 7 - cn = Math.floor(Math.random() * step) - for (index = 0; index < 3; index++) { - var user - user = users[cn] - var img - if (user.avatar) { - img = user.avatar - } else { - img = '/images/avi.png' - } - var name = user.acct - if (index === 0) { - panel.img1 = img - panel.name1 = name - panel.$store.state.api.backendInteractor.externalProfile(name) - .then((externalUser) => { - if (!externalUser.error) { - panel.$store.commit('addNewUsers', [externalUser]) - panel.id1 = externalUser.id - } - }) - } else if (index === 1) { - panel.img2 = img - panel.name2 = name - panel.$store.state.api.backendInteractor.externalProfile(name) - .then((externalUser) => { - if (!externalUser.error) { - panel.$store.commit('addNewUsers', [externalUser]) - panel.id2 = externalUser.id - } - }) - } else if (index === 2) { - panel.img3 = img - panel.name3 = name - panel.$store.state.api.backendInteractor.externalProfile(name) - .then((externalUser) => { - if (!externalUser.error) { - panel.$store.commit('addNewUsers', [externalUser]) - panel.id3 = externalUser.id - } - }) - } - cn = (cn + step) % users.length - } + panel.usersToFollow.forEach((toFollow, index) => { + let randIndex = Math.floor(Math.random() * reply.length) + let user = reply[randIndex] + let img = user.avatar || '/images/avi.png' + let name = user.acct + + toFollow.img = img + toFollow.name = name + + panel.$store.state.api.backendInteractor.externalProfile(name) + .then((externalUser) => { + if (!externalUser.error) { + panel.$store.commit('addNewUsers', [externalUser]) + toFollow.id = externalUser.id + } + }) + }) } function getWhoToFollow (panel) { var credentials = panel.$store.state.users.currentUser.credentials if (credentials) { - panel.name1 = 'Loading...' - panel.name2 = 'Loading...' - panel.name3 = 'Loading...' + panel.usersToFollow.forEach((_, index) => { + panel.usersToFollow[index].name = 'Loading...' + }) apiService.suggestions({credentials: credentials}) .then((reply) => { showWhoToFollow(panel, reply) @@ -67,27 +36,24 @@ function getWhoToFollow (panel) { const WhoToFollowPanel = { data: () => ({ - img1: '/images/avi.png', - name1: '', - id1: 0, - img2: '/images/avi.png', - name2: '', - id2: 0, - img3: '/images/avi.png', - name3: '', - id3: 0 + usersToFollow: new Array(3).fill().map(x => ( + { + img: '/images/avi.png', + name: '', + id: 0 + } + )) }), computed: { user: function () { return this.$store.state.users.currentUser.screen_name }, moreUrl: function () { - var host = window.location.hostname - var user = this.user - var suggestionsWeb = this.$store.state.instance.suggestionsWeb - var url - url = suggestionsWeb.replace(/{{host}}/g, encodeURIComponent(host)) - url = url.replace(/{{user}}/g, encodeURIComponent(user)) + let host = window.location.hostname + let user = this.user + let suggestionsWeb = this.$store.state.instance.suggestionsWeb + let url = suggestionsWeb.replace(/{{host}}/g, encodeURIComponent(host)) + .replace(/{{user}}/g, encodeURIComponent(user)) return url }, suggestionsEnabled () { diff --git a/src/components/who_to_follow_panel/who_to_follow_panel.vue b/src/components/who_to_follow_panel/who_to_follow_panel.vue index a62e8360..ad6a028e 100644 --- a/src/components/who_to_follow_panel/who_to_follow_panel.vue +++ b/src/components/who_to_follow_panel/who_to_follow_panel.vue @@ -8,9 +8,12 @@

- {{ name1 }}
- {{ name2 }}
- {{ name3 }}
+ + + + {{user.name}} +
+
{{$t('who_to_follow.more')}}

From a4f09029260100f6d5baea67ac333593c5c4432c Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 25 Dec 2018 03:46:19 +0300 Subject: [PATCH 07/16] small refactor, added push unsub notice for BE --- src/modules/users.js | 4 +- src/services/push/push.js | 77 +++++++++++++++++++++++++++------------ 2 files changed, 56 insertions(+), 25 deletions(-) diff --git a/src/modules/users.js b/src/modules/users.js index 2ea0919e..f2b59aaa 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -117,7 +117,9 @@ const users = { registerPushNotifications(isEnabled, vapidPublicKey, token) }, unregisterPushNotifications (store) { - unregisterPushNotifications() + const token = store.state.currentUser.credentials + + unregisterPushNotifications(token) }, addNewStatuses (store, { statuses }) { const users = map(statuses, 'user') diff --git a/src/services/push/push.js b/src/services/push/push.js index ff67fd5a..9142895f 100644 --- a/src/services/push/push.js +++ b/src/services/push/push.js @@ -14,18 +14,12 @@ function isPushSupported () { return 'serviceWorker' in navigator && 'PushManager' in window } -function registerServiceWorker () { +function getOrCreateServiceWorker () { return runtime.register() - .catch((err) => console.error('Unable to register service worker.', err)) + .catch((err) => console.error('Unable to get or create a service worker.', err)) } -function unregisterServiceWorker () { - return runtime.register() - .then((registration) => registration.unregister()) - .catch((err) => console.error('Unable to unregister serviceworker', err)) -} - -function subscribe (registration, isEnabled, vapidPublicKey) { +function subscribePush (registration, isEnabled, vapidPublicKey) { if (!isEnabled) return Promise.reject(new Error('Web Push is disabled in config')) if (!vapidPublicKey) return Promise.reject(new Error('VAPID public key is not found')) @@ -36,6 +30,30 @@ function subscribe (registration, isEnabled, vapidPublicKey) { return registration.pushManager.subscribe(subscribeOptions) } +function unsubscribePush (registration) { + return registration.pushManager.getSubscription() + .then((subscribtion) => { + if (subscribtion === null) { return } + return subscribtion.unsubscribe() + }) +} + +function deleteSubscriptionFromBackEnd (token) { + return window.fetch('/api/v1/push/subscription/', { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}` + } + }).then((response) => { + if (!response.ok) throw new Error('Bad status code from server.') + return response.json() + }).then((responseData) => { + if (!responseData.id) throw new Error('Bad response from server.') + return responseData + }) +} + function sendSubscriptionToBackEnd (subscription, token) { return window.fetch('/api/v1/push/subscription/', { method: 'POST', @@ -54,31 +72,42 @@ function sendSubscriptionToBackEnd (subscription, token) { } } }) + }).then((response) => { + if (!response.ok) throw new Error('Bad status code from server.') + return response.json() + }).then((responseData) => { + if (!responseData.id) throw new Error('Bad response from server.') + return responseData }) - .then((response) => { - if (!response.ok) throw new Error('Bad status code from server.') - return response.json() - }) - .then((responseData) => { - if (!responseData.id) throw new Error('Bad response from server.') - return responseData - }) } export function registerPushNotifications (isEnabled, vapidPublicKey, token) { if (isPushSupported()) { - registerServiceWorker() - .then((registration) => subscribe(registration, isEnabled, vapidPublicKey)) + getOrCreateServiceWorker() + .then((registration) => subscribePush(registration, isEnabled, vapidPublicKey)) .then((subscription) => sendSubscriptionToBackEnd(subscription, token)) .catch((e) => console.warn(`Failed to setup Web Push Notifications: ${e.message}`)) } } -export function unregisterPushNotifications (isEnabled, vapidPublicKey, token) { +export function unregisterPushNotifications (token) { if (isPushSupported()) { - unregisterServiceWorker() - .then((registration) => subscribe(registration, isEnabled, vapidPublicKey)) - .then((subscription) => sendSubscriptionToBackEnd(subscription, token)) - .catch((e) => console.warn(`Failed to setup Web Push Notifications: ${e.message}`)) + Promise.all([ + deleteSubscriptionFromBackEnd(token), + getOrCreateServiceWorker() + .then((registration) => { + return unsubscribePush(registration).then((result) => [registration, result]) + }) + .then(([registration, unsubResult]) => { + if (!unsubResult) { + console.warn('Push subscription cancellation wasn\'t successful, killing SW anyway...') + } + return registration.unregister().then((result) => { + if (!result) { + console.warn('Failed to kill SW') + } + }) + }) + ]).catch((e) => console.warn(`Failed to disable Web Push Notifications: ${e.message}`)) } } From 18d7684f3243da4068dcc21b66854484fb61b062 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 25 Dec 2018 04:24:49 +0300 Subject: [PATCH 08/16] fix for non-shrinking main post form --- src/components/post_status_form/post_status_form.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index 94c75b6a..2435074b 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -248,6 +248,7 @@ const PostStatusForm = { } this.$emit('posted') let el = this.$el.querySelector('textarea') + el.style.height = 'auto' el.style.height = undefined this.error = null } else { From ed2393c970557d1184ddb19519102be5a1f51a9d Mon Sep 17 00:00:00 2001 From: Hannah Ward Date: Tue, 25 Dec 2018 01:32:20 +0000 Subject: [PATCH 09/16] fix inconsistencies within who_to_follow_panel --- .../who_to_follow_panel/who_to_follow_panel.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/who_to_follow_panel/who_to_follow_panel.js b/src/components/who_to_follow_panel/who_to_follow_panel.js index 3ead0b72..b2183e6d 100644 --- a/src/components/who_to_follow_panel/who_to_follow_panel.js +++ b/src/components/who_to_follow_panel/who_to_follow_panel.js @@ -24,8 +24,8 @@ function showWhoToFollow (panel, reply) { function getWhoToFollow (panel) { var credentials = panel.$store.state.users.currentUser.credentials if (credentials) { - panel.usersToFollow.forEach((_, index) => { - panel.usersToFollow[index].name = 'Loading...' + panel.usersToFollow.forEach(toFollow => { + toFollow.name = 'Loading...' }) apiService.suggestions({credentials: credentials}) .then((reply) => { @@ -49,11 +49,11 @@ const WhoToFollowPanel = { return this.$store.state.users.currentUser.screen_name }, moreUrl: function () { - let host = window.location.hostname - let user = this.user - let suggestionsWeb = this.$store.state.instance.suggestionsWeb - let url = suggestionsWeb.replace(/{{host}}/g, encodeURIComponent(host)) - .replace(/{{user}}/g, encodeURIComponent(user)) + const host = window.location.hostname + const user = this.user + const suggestionsWeb = this.$store.state.instance.suggestionsWeb + const url = suggestionsWeb.replace(/{{host}}/g, encodeURIComponent(host)) + .replace(/{{user}}/g, encodeURIComponent(user)) return url }, suggestionsEnabled () { From 8ae3518a9a2f5d59b2d3cbcf262daf6f2bbc56ed Mon Sep 17 00:00:00 2001 From: Hakaba Hitoyo Date: Tue, 25 Dec 2018 09:03:05 +0000 Subject: [PATCH 10/16] Update japanese translation --- src/i18n/ja.json | 171 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 168 insertions(+), 3 deletions(-) diff --git a/src/i18n/ja.json b/src/i18n/ja.json index 4da7ea30..31ca4a52 100644 --- a/src/i18n/ja.json +++ b/src/i18n/ja.json @@ -29,13 +29,16 @@ "username": "ユーザーめい" }, "nav": { + "back": "もどる", "chat": "ローカルチャット", "friend_requests": "フォローリクエスト", "mentions": "メンション", "dms": "ダイレクトメッセージ", "public_tl": "パブリックタイムライン", "timeline": "タイムライン", - "twkn": "つながっているすべてのネットワーク" + "twkn": "つながっているすべてのネットワーク", + "user_search": "ユーザーをさがす", + "preferences": "せってい" }, "notifications": { "broken_favorite": "ステータスがみつかりません。さがしています...", @@ -70,7 +73,17 @@ "fullname": "スクリーンネーム", "password_confirm": "パスワードのかくにん", "registration": "はじめる", - "token": "しょうたいトークン" + "token": "しょうたいトークン", + "captcha": "CAPTCHA", + "new_captcha": "もじがよめないときは、がぞうをクリックすると、あたらしいがぞうになります", + "validations": { + "username_required": "なにかかいてください", + "fullname_required": "なにかかいてください", + "email_required": "なにかかいてください", + "password_required": "なにかかいてください", + "password_confirmation_required": "なにかかいてください", + "password_confirmation_match": "パスワードがちがいます" + } }, "settings": { "attachmentRadius": "ファイル", @@ -90,6 +103,7 @@ "change_password_error": "パスワードをかえることが、できなかったかもしれません。", "changed_password": "パスワードが、かわりました!", "collapse_subject": "せつめいのあるとうこうをたたむ", + "composing": "とうこう", "confirm_new_password": "あたらしいパスワードのかくにん", "current_avatar": "いまのアバター", "current_password": "いまのパスワード", @@ -113,17 +127,22 @@ "general": "ぜんぱん", "hide_attachments_in_convo": "スレッドのファイルをかくす", "hide_attachments_in_tl": "タイムラインのファイルをかくす", + "hide_isp": "インスタンススペシフィックパネルをかくす", + "preload_images": "がぞうをさきよみする", "hide_post_stats": "とうこうのとうけいをかくす (れい: おきにいりのかず)", "hide_user_stats": "ユーザーのとうけいをかくす (れい: フォロワーのかず)", "import_followers_from_a_csv_file": "CSVファイルからフォローをインポートする", "import_theme": "ロード", "inputRadius": "インプットフィールド", + "checkboxRadius": "チェックボックス", "instance_default": "(デフォルト: {value})", + "instance_default_simple": "(デフォルト)", + "interface": "インターフェース", "interfaceLanguage": "インターフェースのことば", "invalid_theme_imported": "このファイルはPleromaのテーマではありません。テーマはへんこうされませんでした。", "limited_availability": "あなたのブラウザではできません", "links": "リンク", - "lock_account_description": "あなたがみとめたひとだけ、あなたのアカウントをフォローできます", + "lock_account_description": "あなたがみとめたひとだけ、あなたのアカウントをフォローできる", "loop_video": "ビデオをくりかえす", "loop_video_silent_only": "おとのないビデオだけくりかえす", "name": "なまえ", @@ -135,6 +154,7 @@ "notification_visibility_mentions": "メンション", "notification_visibility_repeats": "リピート", "no_rich_text_description": "リッチテキストをつかわない", + "hide_network_description": "わたしがフォローしているひとと、わたしをフォローしているひとを、みせない", "nsfw_clickthrough": "NSFWなファイルをかくす", "panelRadius": "パネル", "pause_on_unfocused": "タブにフォーカスがないときストリーミングをとめる", @@ -151,20 +171,139 @@ "saving_err": "せっていをセーブできませんでした", "saving_ok": "せっていをセーブしました", "security_tab": "セキュリティ", + "scope_copy": "リプライするとき、こうかいはんいをコピーする (DMのこうかいはんいは、つねにコピーされます)", "set_new_avatar": "あたらしいアバターをせっていする", "set_new_profile_background": "あたらしいプロフィールのバックグラウンドをせっていする", "set_new_profile_banner": "あたらしいプロフィールバナーを設定する", "settings": "せってい", + "subject_input_always_show": "サブジェクトフィールドをいつでもひょうじする", + "subject_line_behavior": "リプライするときサブジェクトをコピーする", + "subject_line_email": "メールふう: \"re: サブジェクト\"", + "subject_line_mastodon": "マストドンふう: そのままコピー", + "subject_line_noop": "コピーしない", "stop_gifs": "カーソルをかさねたとき、GIFをうごかす", "streaming": "うえまでスクロールしたとき、じどうてきにストリーミングする", "text": "もじ", "theme": "テーマ", "theme_help": "カラーテーマをカスタマイズできます", + "theme_help_v2_1": "チェックボックスをONにすると、コンポーネントごとに、いろと、とうめいどを、オーバーライドできます。「すべてクリア」ボタンをおすと、すべてのオーバーライドを、やめます。", + "theme_help_v2_2": "バックグラウンドとテキストのコントラストをあらわすアイコンがあります。マウスをホバーすると、くわしいせつめいがでます。とうめいないろをつかっているときは、もっともわるいばあいのコントラストがしめされます。", "tooltipRadius": "ツールチップとアラート", "user_settings": "ユーザーせってい", "values": { "false": "いいえ", "true": "はい" + }, + "notifications": "つうち", + "enable_web_push_notifications": "ウェブプッシュつうちをゆるす", + "style": { + "switcher": { + "keep_color": "いろをのこす", + "keep_shadows": "かげをのこす", + "keep_opacity": "とうめいどをのこす", + "keep_roundness": "まるさをのこす", + "keep_fonts": "フォントをのこす", + "save_load_hint": "「のこす」オプションをONにすると、テーマをえらんだときとロードしたとき、いまのせっていをのこします。また、テーマをエクスポートするとき、これらのオプションをストアします。すべてのチェックボックスをOFFにすると、テーマをエクスポートしたとき、すべてのせっていをセーブします。", + "reset": "リセット", + "clear_all": "すべてクリア", + "clear_opacity": "とうめいどをクリア" + }, + "common": { + "color": "いろ", + "opacity": "とうめいど", + "contrast": { + "hint": "コントラストは {ratio} です。{level}。({context})", + "level": { + "aa": "AAレベルガイドライン (ミニマル) をみたします", + "aaa": "AAAレベルガイドライン (レコメンデッド) をみたします。", + "bad": "ガイドラインをみたしません。" + }, + "context": { + "18pt": "おおきい (18ポイントいじょう) テキスト", + "text": "テキスト" + } + } + }, + "common_colors": { + "_tab_label": "きょうつう", + "main": "きょうつうのいろ", + "foreground_hint": "「くわしく」タブで、もっとこまかくせっていできます", + "rgbo": "アイコンとアクセントとバッジ" + }, + "advanced_colors": { + "_tab_label": "くわしく", + "alert": "アラートのバックグラウンド", + "alert_error": "エラー", + "badge": "バッジのバックグラウンド", + "badge_notification": "つうち", + "panel_header": "パネルヘッダー", + "top_bar": "トップバー", + "borders": "さかいめ", + "buttons": "ボタン", + "inputs": "インプットフィールド", + "faint_text": "うすいテキスト" + }, + "radii": { + "_tab_label": "まるさ" + }, + "shadows": { + "_tab_label": "ひかりとかげ", + "component": "コンポーネント", + "override": "オーバーライド", + "shadow_id": "かげ #{value}", + "blur": "ぼかし", + "spread": "ひろがり", + "inset": "うちがわ", + "hint": "かげのせっていでは、いろのあたいとして --variable をつかうことができます。これはCSS3へんすうです。ただし、とうめいどのせっていは、きかなくなります。", + "filter_hint": { + "always_drop_shadow": "ブラウザーがサポートしていれば、つねに {0} がつかわれます。", + "drop_shadow_syntax": "{0} は、{1} パラメーターと {2} キーワードをサポートしていません。", + "avatar_inset": "うちがわのかげと、そとがわのかげを、いっしょにつかうと、とうめいなアバターが、へんなみためになります。", + "spread_zero": "ひろがりが 0 よりもおおきなかげは、0 とおなじです。", + "inset_classic": "うちがわのかげは {0} をつかいます。" + }, + "components": { + "panel": "パネル", + "panelHeader": "パネルヘッダー", + "topBar": "トップバー", + "avatar": "ユーザーアバター (プロフィール)", + "avatarStatus": "ユーザーアバター (とうこう)", + "popup": "ポップアップとツールチップ", + "button": "ボタン", + "buttonHover": "ボタン (ホバー)", + "buttonPressed": "ボタン (おされているとき)", + "buttonPressedHover": "ボタン (ホバー、かつ、おされているとき)", + "input": "インプットフィールド" + } + }, + "fonts": { + "_tab_label": "フォント", + "help": "「カスタム」をえらんだときは、システムにあるフォントのなまえを、ただしくにゅうりょくしてください。", + "components": { + "interface": "インターフェース", + "input": "インプットフィールド", + "post": "とうこう", + "postCode": "モノスペース (とうこうがリッチテキストであるとき)" + }, + "family": "フォントめい", + "size": "おおきさ (px)", + "weight": "ふとさ", + "custom": "カスタム" + }, + "preview": { + "header": "プレビュー", + "content": "ほんぶん", + "error": "エラーのれい", + "button": "ボタン", + "text": "これは{0}と{1}のれいです。", + "mono": "monospace", + "input": "はねだくうこうに、つきました。", + "faint_link": "とてもたすけになるマニュアル", + "fine_print": "わたしたちの{0}を、よまないでください!", + "header_faint": "エラーではありません", + "checkbox": "りようきやくを、よみました", + "link": "ハイパーリンク" + } } }, "timeline": { @@ -183,10 +322,15 @@ "blocked": "ブロックしています!", "deny": "おことわり", "follow": "フォロー", + "follow_sent": "リクエストを、おくりました!", + "follow_progress": "リクエストしています…", + "follow_again": "ふたたびリクエストをおくりますか?", + "follow_unfollow": "フォローをやめる", "followees": "フォロー", "followers": "フォロワー", "following": "フォローしています!", "follows_you": "フォローされました!", + "its_you": "これはあなたです!", "mute": "ミュート", "muted": "ミュートしています!", "per_day": "/日", @@ -199,5 +343,26 @@ "who_to_follow": { "more": "くわしく", "who_to_follow": "おすすめユーザー" + }, + "tool_tip": { + "media_upload": "メディアをアップロード", + "repeat": "リピート", + "reply": "リプライ", + "favorite": "おきにいり", + "user_settings": "ユーザーせってい" + }, + "upload":{ + "error": { + "base": "アップロードにしっぱいしました。", + "file_too_big": "ファイルがおおきすぎます [{filesize} {filesizeunit} / {allowedsize} {allowedsizeunit}]", + "default": "しばらくしてから、ためしてください" + }, + "file_size_units": { + "B": "B", + "KiB": "KiB", + "MiB": "MiB", + "GiB": "GiB", + "TiB": "TiB" + } } } From 500b4bd37412960c7b2278b930794b2ccf00c971 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 25 Dec 2018 14:28:49 +0300 Subject: [PATCH 11/16] fix --- src/services/push/push.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/services/push/push.js b/src/services/push/push.js index 9142895f..bf0c9680 100644 --- a/src/services/push/push.js +++ b/src/services/push/push.js @@ -47,10 +47,7 @@ function deleteSubscriptionFromBackEnd (token) { } }).then((response) => { if (!response.ok) throw new Error('Bad status code from server.') - return response.json() - }).then((responseData) => { - if (!responseData.id) throw new Error('Bad response from server.') - return responseData + return response }) } From c5847349e0afb14702d1746fac142b8aeb2020e7 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Tue, 25 Dec 2018 20:43:18 +0700 Subject: [PATCH 12/16] improve web push notifications --- src/lib/push_notifications_plugin.js | 22 ++++++++++++++++++++++ src/main.js | 25 ++----------------------- src/modules/users.js | 3 ++- src/services/push/push.js | 14 +++++++------- 4 files changed, 33 insertions(+), 31 deletions(-) create mode 100644 src/lib/push_notifications_plugin.js diff --git a/src/lib/push_notifications_plugin.js b/src/lib/push_notifications_plugin.js new file mode 100644 index 00000000..f75bb823 --- /dev/null +++ b/src/lib/push_notifications_plugin.js @@ -0,0 +1,22 @@ +export default (store) => { + store.subscribe((mutation, state) => { + const vapidPublicKey = state.instance.vapidPublicKey + const webPushNotification = state.config.webPushNotifications + const permission = state.interface.notificationPermission === 'granted' + const user = state.users.currentUser + + const isUserMutation = mutation.type === 'setCurrentUser' + const isVapidMutation = mutation.type === 'setInstanceOption' && mutation.payload.name === 'vapidPublicKey' + const isPermMutation = mutation.type === 'setNotificationPermission' && mutation.payload === 'granted' + const isUserConfigMutation = mutation.type === 'setOption' && mutation.payload.name === 'webPushNotifications' + const isVisibilityMutation = mutation.type === 'setOption' && mutation.payload.name === 'notificationVisibility' + + if (isUserMutation || isVapidMutation || isPermMutation || isUserConfigMutation || isVisibilityMutation) { + if (user && vapidPublicKey && permission && webPushNotification) { + return store.dispatch('registerPushNotifications') + } else if (isUserConfigMutation && !webPushNotification) { + return store.dispatch('unregisterPushNotifications') + } + } + }) +} diff --git a/src/main.js b/src/main.js index c22a762e..f87ef9da 100644 --- a/src/main.js +++ b/src/main.js @@ -15,6 +15,7 @@ import VueTimeago from 'vue-timeago' import VueI18n from 'vue-i18n' import createPersistedState from './lib/persisted_state.js' +import pushNotifications from './lib/push_notifications_plugin.js' import messages from './i18n/messages.js' @@ -51,28 +52,6 @@ const persistedStateOptions = { ] } -const registerPushNotifications = store => { - store.subscribe((mutation, state) => { - const vapidPublicKey = state.instance.vapidPublicKey - const webPushNotification = state.config.webPushNotifications - const permission = state.interface.notificationPermission === 'granted' - const user = state.users.currentUser - - const isUserMutation = mutation.type === 'setCurrentUser' - const isVapidMutation = mutation.type === 'setInstanceOption' && mutation.payload.name === 'vapidPublicKey' - const isPermMutation = mutation.type === 'setNotificationPermission' && mutation.payload === 'granted' - const isUserConfigMutation = mutation.type === 'setOption' && mutation.payload.name === 'webPushNotifications' - - if (isUserMutation || isVapidMutation || isPermMutation || isUserConfigMutation) { - if (user && vapidPublicKey && permission && webPushNotification) { - return store.dispatch('registerPushNotifications') - } else if (isUserConfigMutation && !webPushNotification) { - return store.dispatch('unregisterPushNotifications') - } - } - }) -} - createPersistedState(persistedStateOptions).then((persistedState) => { const store = new Vuex.Store({ modules: { @@ -85,7 +64,7 @@ createPersistedState(persistedStateOptions).then((persistedState) => { chat: chatModule, oauth: oauthModule }, - plugins: [persistedState, registerPushNotifications], + plugins: [persistedState, pushNotifications], strict: false // Socket modifies itself, let's ignore this for now. // strict: process.env.NODE_ENV !== 'production' }) diff --git a/src/modules/users.js b/src/modules/users.js index f2b59aaa..cff16c47 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -113,8 +113,9 @@ const users = { const token = store.state.currentUser.credentials const vapidPublicKey = store.rootState.instance.vapidPublicKey const isEnabled = store.rootState.config.webPushNotifications + const notificationVisibility = store.rootState.config.notificationVisibility - registerPushNotifications(isEnabled, vapidPublicKey, token) + registerPushNotifications(isEnabled, vapidPublicKey, token, notificationVisibility) }, unregisterPushNotifications (store) { const token = store.state.currentUser.credentials diff --git a/src/services/push/push.js b/src/services/push/push.js index bf0c9680..1b189a29 100644 --- a/src/services/push/push.js +++ b/src/services/push/push.js @@ -51,7 +51,7 @@ function deleteSubscriptionFromBackEnd (token) { }) } -function sendSubscriptionToBackEnd (subscription, token) { +function sendSubscriptionToBackEnd (subscription, token, notificationVisibility) { return window.fetch('/api/v1/push/subscription/', { method: 'POST', headers: { @@ -62,10 +62,10 @@ function sendSubscriptionToBackEnd (subscription, token) { subscription, data: { alerts: { - follow: true, - favourite: true, - mention: true, - reblog: true + follow: notificationVisibility.follows, + favourite: notificationVisibility.likes, + mention: notificationVisibility.mentions, + reblog: notificationVisibility.repeats } } }) @@ -78,11 +78,11 @@ function sendSubscriptionToBackEnd (subscription, token) { }) } -export function registerPushNotifications (isEnabled, vapidPublicKey, token) { +export function registerPushNotifications (isEnabled, vapidPublicKey, token, notificationVisibility) { if (isPushSupported()) { getOrCreateServiceWorker() .then((registration) => subscribePush(registration, isEnabled, vapidPublicKey)) - .then((subscription) => sendSubscriptionToBackEnd(subscription, token)) + .then((subscription) => sendSubscriptionToBackEnd(subscription, token, notificationVisibility)) .catch((e) => console.warn(`Failed to setup Web Push Notifications: ${e.message}`)) } } From fa8ecb1c39d2900de69fef0a609ef31ce0ef9161 Mon Sep 17 00:00:00 2001 From: HJ Date: Wed, 26 Dec 2018 09:19:25 +0000 Subject: [PATCH 13/16] User Card Content fixes and updates --- .../user_card_content/user_card_content.vue | 145 +++++++++++------- src/modules/statuses.js | 2 + src/modules/users.js | 18 +++ 3 files changed, 109 insertions(+), 56 deletions(-) diff --git a/src/components/user_card_content/user_card_content.vue b/src/components/user_card_content/user_card_content.vue index c5222519..c76c41a9 100644 --- a/src/components/user_card_content/user_card_content.vue +++ b/src/components/user_card_content/user_card_content.vue @@ -2,22 +2,25 @@