From 56fec664a9bb5c1539423e396c127c4a45e8f4e9 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Mon, 19 Nov 2018 20:22:46 +0300 Subject: cleanup and optimization --- src/modules/config.js | 6 +++--- src/modules/instance.js | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src/modules') diff --git a/src/modules/config.js b/src/modules/config.js index 375d0167..96f2fd5e 100644 --- a/src/modules/config.js +++ b/src/modules/config.js @@ -1,5 +1,5 @@ import { set, delete as del } from 'vue' -import StyleSetter from '../services/style_setter/style_setter.js' +import { setPreset, setColors } from '../services/style_setter/style_setter.js' const browserLocale = (window.navigator.language || 'en').split('-')[0] @@ -51,10 +51,10 @@ const config = { commit('setOption', {name, value}) switch (name) { case 'theme': - StyleSetter.setPreset(value, commit) + setPreset(value, commit) break case 'customTheme': - StyleSetter.setColors(value, commit) + setColors(value, commit) } } } diff --git a/src/modules/instance.js b/src/modules/instance.js index cb724821..611212c3 100644 --- a/src/modules/instance.js +++ b/src/modules/instance.js @@ -1,5 +1,5 @@ import { set } from 'vue' -import StyleSetter from '../services/style_setter/style_setter.js' +import { setPreset } from '../services/style_setter/style_setter.js' const defaultState = { // Stuff from static/config.json and apiConfig @@ -54,7 +54,7 @@ const instance = { dispatch('setPageTitle') break case 'theme': - StyleSetter.setPreset(value, commit) + setPreset(value, commit) } } } -- cgit v1.2.3-70-g09d2 From 406df4399b630268c1028664f3b818571d6f8e4f Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 30 Nov 2018 16:39:07 +0300 Subject: avatars shadows, also allows drop-shadow use --- src/components/notification/notification.js | 3 ++- src/components/notification/notification.vue | 2 +- src/components/notifications/notifications.scss | 6 +++++ src/components/status/status.js | 3 ++- src/components/status/status.vue | 13 +++++++++- src/components/style_switcher/style_switcher.vue | 12 ++++++++++ .../user_card_content/user_card_content.js | 3 ++- .../user_card_content/user_card_content.vue | 7 +++++- src/i18n/en.json | 7 ++++++ src/modules/interface.js | 6 +++++ src/services/style_setter/style_setter.js | 28 ++++++++++++++++++++-- 11 files changed, 82 insertions(+), 8 deletions(-) (limited to 'src/modules') diff --git a/src/components/notification/notification.js b/src/components/notification/notification.js index c786f2cc..4dea63bb 100644 --- a/src/components/notification/notification.js +++ b/src/components/notification/notification.js @@ -6,7 +6,8 @@ import { highlightClass, highlightStyle } from '../../services/user_highlighter/ const Notification = { data () { return { - userExpanded: false + userExpanded: false, + betterShadow: this.$store.state.interface.browserSupport.cssFilter } }, props: [ diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue index 72c1ca69..f98afbe0 100644 --- a/src/components/notification/notification.vue +++ b/src/components/notification/notification.vue @@ -2,7 +2,7 @@
- +
diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss index 87c89f6a..d17ae25d 100644 --- a/src/components/notifications/notifications.scss +++ b/src/components/notifications/notifications.scss @@ -49,11 +49,17 @@ .avatar-compact { width: 32px; height: 32px; + box-shadow: var(--avatarStatusShadow); border-radius: $fallback--avatarAltRadius; border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius); overflow: hidden; line-height: 0; + &.better-shadow { + box-shadow: none; + filter: drop-shadow(var(--avatarStatusShadowFilter)) + } + &.animated::before { display: none; } diff --git a/src/components/status/status.js b/src/components/status/status.js index 10716583..725bc3f8 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -33,7 +33,8 @@ const Status = { showingTall: false, expandingSubject: typeof this.$store.state.config.collapseMessageWithSubject === 'undefined' ? !this.$store.state.instance.collapseMessageWithSubject - : !this.$store.state.config.collapseMessageWithSubject + : !this.$store.state.config.collapseMessageWithSubject, + betterShadow: this.$store.state.interface.browserSupport.cssFilter } }, computed: { diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 4541c560..26be335c 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -21,7 +21,7 @@
@@ -464,8 +464,14 @@ .status .avatar-compact { width: 32px; height: 32px; + box-shadow: var(--avatarStatusShadow); border-radius: $fallback--avatarAltRadius; border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius); + + &.better-shadow { + box-shadow: none; + filter: drop-shadow(var(--avatarStatusShadowFilter)) + } } .avatar { @@ -477,6 +483,11 @@ overflow: hidden; position: relative; + &.better-shadow { + box-shadow: none; + filter: drop-shadow(var(--avatarStatusShadowFilter)) + } + img { width: 100%; height: 100%; diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index fa173b98..66fe0f6b 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -278,6 +278,18 @@
+
+ + filter: drop-shadow() + + + drop-shadow + spread-radius + inset + +

{{$t('settings.style.shadows.filter_hint.inset_ignored')}}

+

{{$t('settings.style.shadows.filter_hint.spread_zero')}}

+
diff --git a/src/components/user_card_content/user_card_content.js b/src/components/user_card_content/user_card_content.js index 254d1666..97cd4983 100644 --- a/src/components/user_card_content/user_card_content.js +++ b/src/components/user_card_content/user_card_content.js @@ -7,7 +7,8 @@ export default { return { hideUserStatsLocal: typeof this.$store.state.config.hideUserStats === 'undefined' ? this.$store.state.instance.hideUserStats - : this.$store.state.config.hideUserStats + : this.$store.state.config.hideUserStats, + betterShadow: this.$store.state.interface.browserSupport.cssFilter } }, computed: { diff --git a/src/components/user_card_content/user_card_content.vue b/src/components/user_card_content/user_card_content.vue index 5529948e..cca418ff 100644 --- a/src/components/user_card_content/user_card_content.vue +++ b/src/components/user_card_content/user_card_content.vue @@ -10,7 +10,7 @@
- +
@@ -159,6 +159,11 @@ box-shadow: var(--avatarShadow); object-fit: cover; + &.better-shadow { + box-shadow: none; + filter: drop-shadow(var(--avatarStatusShadowFilter)) + } + &.animated::before { display: none; } diff --git a/src/i18n/en.json b/src/i18n/en.json index 8847b11e..7f5a2a4f 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -236,6 +236,13 @@ "spread": "Spread", "inset": "Inset", "hint": "For shadows you can also use --variable as a color value to use CSS3 variables. Please note that setting opacity won't work in this case.", + "filter_hint": { + "always_drop_shadow": "Warning, this shadow always uses {0} when browser supports it.", + "text": "Please note that {0} does not support {1} parameter and {2} keyword.", + "spread_zero": "Shadows with spread > 0 will appear as if it was set to zero", + "inset_ignored": "Inset shadows using will be ignored", + "inset_substituted": "Inset shadows will be substituted with {1} equivalent" + }, "components": { "panel": "Panel", "panelHeader": "Panel header", diff --git a/src/modules/interface.js b/src/modules/interface.js index 07489685..132fb08d 100644 --- a/src/modules/interface.js +++ b/src/modules/interface.js @@ -4,6 +4,12 @@ const defaultState = { settings: { currentSaveStateNotice: null, noticeClearTimeout: null + }, + browserSupport: { + cssFilter: window.CSS && window.CSS.supports && ( + window.CSS.supports('filter', 'drop-shadow(0 0)') || + window.CSS.supports('-webkit-filter', 'drop-shadow(0 0)') + ) } } diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 7c375206..cff81c40 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -110,6 +110,24 @@ const getCssShadow = (input) => { ]).join(' ')).join(', ') } +const getCssShadowFilter = (input) => { + if (input.length === 0) { + return 'none' + } + + return input + // drop-shadow doesn't support inset or spread + .filter((shad) => console.log(shad) || !shad.inset && Number(shad.spread) === 0) + .map((shad) => [ + shad.x, + shad.y, + // drop-shadow's blur is twice as strong compared to box-shadow + shad.blur / 2 + ].map(_ => _ + 'px').concat([ + getCssColor(shad.color, shad.alpha) + ]).join(' ')).join(', ') +} + const getCssColor = (input, a) => { let rgb = {} if (typeof input === 'object') { @@ -384,7 +402,12 @@ const generateShadows = (input) => { return { rules: { - shadows: Object.entries(shadows).map(([k, v]) => `--${k}Shadow: ${getCssShadow(v)}`).join(';') + shadows: Object + .entries(shadows) + // TODO for v2.1: if shadow doesn't have non-inset shadows with spread > 0 - optionally + // convert all non-inset shadows into filter: drop-shadow() to boost performance + .map(([k, v]) => `--${k}Shadow: ${getCssShadow(v)}; --${k}ShadowFilter: ${getCssShadowFilter(v)}`) + .join(';') }, theme: { shadows @@ -467,5 +490,6 @@ export { generateFonts, generatePreset, composePreset, - getCssShadow + getCssShadow, + getCssShadowFilter } -- cgit v1.2.3-70-g09d2 From 09147cacea6b80d348d4c8364b2815d9b4cac102 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Thu, 6 Dec 2018 20:34:00 +0700 Subject: add service worker and push notifications --- src/boot/after_store.js | 5 ++- src/modules/users.js | 4 ++ src/services/push/push.js | 96 +++++++++++++++++++++++++++++++++++++++++++++++ static/sw.js | 32 ++++++++++++++++ 4 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 src/services/push/push.js create mode 100644 static/sw.js (limited to 'src/modules') diff --git a/src/boot/after_store.js b/src/boot/after_store.js index a80baaf5..0c121fe2 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -17,16 +17,17 @@ import FollowRequests from '../components/follow_requests/follow_requests.vue' import OAuthCallback from '../components/oauth_callback/oauth_callback.vue' import UserSearch from '../components/user_search/user_search.vue' -const afterStoreSetup = ({store, i18n}) => { +const afterStoreSetup = ({ store, i18n }) => { window.fetch('/api/statusnet/config.json') .then((res) => res.json()) .then((data) => { - const {name, closed: registrationClosed, textlimit, server} = data.site + const { name, closed: registrationClosed, textlimit, server, vapidPublicKey } = data.site store.dispatch('setInstanceOption', { name: 'name', value: name }) store.dispatch('setInstanceOption', { name: 'registrationOpen', value: (registrationClosed === '0') }) store.dispatch('setInstanceOption', { name: 'textlimit', value: parseInt(textlimit) }) store.dispatch('setInstanceOption', { name: 'server', value: server }) + store.dispatch('setInstanceOption', { name: 'vapidPublicKey', value: vapidPublicKey }) var apiConfig = data.site.pleromafe diff --git a/src/modules/users.js b/src/modules/users.js index 8630ee0d..791f1680 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -2,6 +2,8 @@ import backendInteractorService from '../services/backend_interactor_service/bac import { compact, map, each, merge } from 'lodash' import { set } from 'vue' +import registerPushNotifications from '../services/push/push.js' + // TODO: Unify with mergeOrAdd in statuses.js export const mergeOrAdd = (arr, obj, item) => { if (!item) { return false } @@ -125,6 +127,8 @@ const users = { // Fetch our friends store.rootState.api.backendInteractor.fetchFriends({id: user.id}) .then((friends) => commit('addNewUsers', friends)) + + registerPushNotifications(store) }) } else { // Authentication failed diff --git a/src/services/push/push.js b/src/services/push/push.js new file mode 100644 index 00000000..4e4551bf --- /dev/null +++ b/src/services/push/push.js @@ -0,0 +1,96 @@ + +function urlBase64ToUint8Array (base64String) { + const padding = '='.repeat((4 - base64String.length % 4) % 4) + const base64 = (base64String + padding) + .replace(/-/g, '+') + .replace(/_/g, '/') + + const rawData = window.atob(base64) + return Uint8Array.from([...rawData].map((char) => char.charCodeAt(0))) +} + +function isPushSupported () { + return 'serviceWorker' in navigator && 'PushManager' in window +} + +function registerServiceWorker () { + return navigator.serviceWorker.register('/static/sw.js') + .then(function (registration) { + console.log('Service worker successfully registered.') + return registration + }) + .catch(function (err) { + console.error('Unable to register service worker.', err) + }) +} + +function askPermission () { + return new Promise(function (resolve, reject) { + if (!window.Notification) return resolve('Notifications disabled') + + const permissionResult = window.Notification.requestPermission(function (result) { + resolve(result) + }) + + if (permissionResult) permissionResult.then(resolve, reject) + }).then(function (permissionResult) { + if (permissionResult !== 'granted') { + throw new Error('We weren\'t granted permission.') + } + return permissionResult + }) +} + +function subscribe (registration, store) { + const subscribeOptions = { + userVisibleOnly: true, + applicationServerKey: urlBase64ToUint8Array(store.rootState.instance.vapidPublicKey) + } + return registration.pushManager.subscribe(subscribeOptions) +} + +function sendSubscriptionToBackEnd (subscription, store) { + return window.fetch('/api/v1/push/subscription/', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${store.rootState.oauth.token}` + }, + body: JSON.stringify({ + subscription, + data: { + alerts: { + follow: true, + favourite: true, + mention: true, + reblog: true + } + } + }) + }) + .then(function (response) { + if (!response.ok) { + throw new Error('Bad status code from server.') + } + + return response.json() + }) + .then(function (responseData) { + if (!responseData.id) { + throw new Error('Bad response from server.') + } + return responseData + }) +} + +export default function registerPushNotifications (store) { + if (isPushSupported()) { + registerServiceWorker() + .then(function (registration) { + return askPermission() + .then(() => subscribe(registration, store)) + .then((subscription) => sendSubscriptionToBackEnd(subscription, store)) + .catch((e) => console.warn(`Failed to setup Web Push Notifications: ${e.message}`)) + }) + } +} diff --git a/static/sw.js b/static/sw.js new file mode 100644 index 00000000..0402a220 --- /dev/null +++ b/static/sw.js @@ -0,0 +1,32 @@ +/* eslint-env serviceworker */ +self.addEventListener('push', function (event) { + if (event.data) { + const data = event.data.json() + + const promiseChain = clients.matchAll({ + includeUncontrolled: true + }).then(function (clientList) { + const list = clientList.filter((item) => item.type === 'window') + if (list.length) return + return self.registration.showNotification(data.title, data) + }) + + event.waitUntil(promiseChain) + } +}) + +self.addEventListener('notificationclick', function (event) { + event.notification.close() + + event.waitUntil(clients.matchAll({ + includeUncontrolled: true + }).then(function (clientList) { + const list = clientList.filter((item) => item.type === 'window') + + for (var i = 0; i < list.length; i++) { + var client = list[i] + if (client.url === '/' && 'focus' in client) { return client.focus() } + } + if (clients.openWindow) { return clients.openWindow('/') } + })) +}) -- cgit v1.2.3-70-g09d2 From ebe2a951409ccb21d828924860a03c6052abbeb1 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Fri, 7 Dec 2018 14:57:35 +0700 Subject: improve web push notifications --- src/modules/users.js | 7 +------ src/services/push/push.js | 10 +++++++++- 2 files changed, 10 insertions(+), 7 deletions(-) (limited to 'src/modules') diff --git a/src/modules/users.js b/src/modules/users.js index 791f1680..d2c7fdf6 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -1,9 +1,8 @@ import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js' +import registerPushNotifications from '../services/push/push.js' import { compact, map, each, merge } from 'lodash' import { set } from 'vue' -import registerPushNotifications from '../services/push/push.js' - // TODO: Unify with mergeOrAdd in statuses.js export const mergeOrAdd = (arr, obj, item) => { if (!item) { return false } @@ -120,10 +119,6 @@ const users = { store.commit('addNewUsers', mutedUsers) }) - if ('Notification' in window && window.Notification.permission === 'default') { - window.Notification.requestPermission() - } - // Fetch our friends store.rootState.api.backendInteractor.fetchFriends({id: user.id}) .then((friends) => commit('addNewUsers', friends)) diff --git a/src/services/push/push.js b/src/services/push/push.js index 4e4551bf..7d99648a 100644 --- a/src/services/push/push.js +++ b/src/services/push/push.js @@ -26,7 +26,11 @@ function registerServiceWorker () { function askPermission () { return new Promise(function (resolve, reject) { - if (!window.Notification) return resolve('Notifications disabled') + if (!window.Notification) return reject(new Error('Notifications disabled')) + + if (window.Notification.permission !== 'default') { + return resolve(window.Notification.permission) + } const permissionResult = window.Notification.requestPermission(function (result) { resolve(result) @@ -42,6 +46,10 @@ function askPermission () { } function subscribe (registration, store) { + if (!store.rootState.instance.vapidPublicKey) { + return Promise.reject(new Error('VAPID publick key is not found')) + } + const subscribeOptions = { userVisibleOnly: true, applicationServerKey: urlBase64ToUint8Array(store.rootState.instance.vapidPublicKey) -- cgit v1.2.3-70-g09d2 From bbae2e10f3214d4a38b31234fa01f92a52417b0b Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Fri, 7 Dec 2018 15:15:31 +0700 Subject: Add configuration to enable/disable web push notifications --- src/modules/config.js | 1 + src/services/push/push.js | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'src/modules') diff --git a/src/modules/config.js b/src/modules/config.js index f23cacb7..1ad9df07 100644 --- a/src/modules/config.js +++ b/src/modules/config.js @@ -23,6 +23,7 @@ const defaultState = { likes: true, repeats: true }, + webPushNotifications: true, muteWords: [], highlight: {}, interfaceLanguage: browserLocale, diff --git a/src/services/push/push.js b/src/services/push/push.js index 7d99648a..28954948 100644 --- a/src/services/push/push.js +++ b/src/services/push/push.js @@ -1,4 +1,3 @@ - function urlBase64ToUint8Array (base64String) { const padding = '='.repeat((4 - base64String.length % 4) % 4) const base64 = (base64String + padding) @@ -46,6 +45,10 @@ function askPermission () { } function subscribe (registration, store) { + if (!store.rootState.config.webPushNotifications) { + return Promise.reject(new Error('Web Push is disabled in config')) + } + if (!store.rootState.instance.vapidPublicKey) { return Promise.reject(new Error('VAPID publick key is not found')) } -- cgit v1.2.3-70-g09d2 From 07f1b8523eddd596d154e68561a389d6b9742d95 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Fri, 7 Dec 2018 18:13:04 +0700 Subject: add subscribe module and fix race condition --- src/main.js | 4 +++- src/modules/subscribe.js | 21 +++++++++++++++++++++ src/modules/users.js | 5 +---- src/services/push/push.js | 8 ++++---- 4 files changed, 29 insertions(+), 9 deletions(-) create mode 100644 src/modules/subscribe.js (limited to 'src/modules') diff --git a/src/main.js b/src/main.js index 378fe95c..4285e70b 100644 --- a/src/main.js +++ b/src/main.js @@ -10,6 +10,7 @@ import apiModule from './modules/api.js' import configModule from './modules/config.js' import chatModule from './modules/chat.js' import oauthModule from './modules/oauth.js' +import subscribeModule from './modules/subscribe.js' import VueTimeago from 'vue-timeago' import VueI18n from 'vue-i18n' @@ -60,7 +61,8 @@ createPersistedState(persistedStateOptions).then((persistedState) => { api: apiModule, config: configModule, chat: chatModule, - oauth: oauthModule + oauth: oauthModule, + subscribe: subscribeModule }, plugins: [persistedState], strict: false // Socket modifies itself, let's ignore this for now. diff --git a/src/modules/subscribe.js b/src/modules/subscribe.js new file mode 100644 index 00000000..e705904c --- /dev/null +++ b/src/modules/subscribe.js @@ -0,0 +1,21 @@ +import registerPushNotifications from '../services/push/push.js' + +const subscribe = { + state: { + token: null, + vapidPublicKey: null + }, + mutations: { + setCurrentUser (state, user) { + state.token = user.credentials + if (state.token && state.vapidPublicKey) registerPushNotifications(this) + }, + setInstanceOption (state, { name, value }) { + if (name !== 'vapidPublicKey') return + state.vapidPublicKey = value + if (state.token && state.vapidPublicKey) registerPushNotifications(this) + } + } +} + +export default subscribe diff --git a/src/modules/users.js b/src/modules/users.js index d2c7fdf6..88ec7115 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -1,5 +1,4 @@ import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js' -import registerPushNotifications from '../services/push/push.js' import { compact, map, each, merge } from 'lodash' import { set } from 'vue' @@ -120,10 +119,8 @@ const users = { }) // Fetch our friends - store.rootState.api.backendInteractor.fetchFriends({id: user.id}) + store.rootState.api.backendInteractor.fetchFriends({ id: user.id }) .then((friends) => commit('addNewUsers', friends)) - - registerPushNotifications(store) }) } else { // Authentication failed diff --git a/src/services/push/push.js b/src/services/push/push.js index ffab5918..923c2178 100644 --- a/src/services/push/push.js +++ b/src/services/push/push.js @@ -45,17 +45,17 @@ function askPermission () { } function subscribe (registration, store) { - if (!store.rootState.config.webPushNotifications) { + if (!store.state.config.webPushNotifications) { return Promise.reject(new Error('Web Push is disabled in config')) } - if (!store.rootState.instance.vapidPublicKey) { + if (!store.state.subscribe.vapidPublicKey) { return Promise.reject(new Error('VAPID public key is not found')) } const subscribeOptions = { userVisibleOnly: true, - applicationServerKey: urlBase64ToUint8Array(store.rootState.instance.vapidPublicKey) + applicationServerKey: urlBase64ToUint8Array(store.state.subscribe.vapidPublicKey) } return registration.pushManager.subscribe(subscribeOptions) } @@ -65,7 +65,7 @@ function sendSubscriptionToBackEnd (subscription, store) { method: 'POST', headers: { 'Content-Type': 'application/json', - 'Authorization': `Bearer ${store.rootState.oauth.token}` + 'Authorization': `Bearer ${store.state.subscribe.token}` }, body: JSON.stringify({ subscription, -- cgit v1.2.3-70-g09d2 From a85d128d3754c97c9124352863ab6bcafd42bf35 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Fri, 7 Dec 2018 18:53:40 +0700 Subject: Revert "add subscribe module and fix race condition" This reverts commit 07f1b8523eddd596d154e68561a389d6b9742d95. --- src/main.js | 4 +--- src/modules/subscribe.js | 21 --------------------- src/modules/users.js | 5 ++++- src/services/push/push.js | 8 ++++---- 4 files changed, 9 insertions(+), 29 deletions(-) delete mode 100644 src/modules/subscribe.js (limited to 'src/modules') diff --git a/src/main.js b/src/main.js index 4285e70b..378fe95c 100644 --- a/src/main.js +++ b/src/main.js @@ -10,7 +10,6 @@ import apiModule from './modules/api.js' import configModule from './modules/config.js' import chatModule from './modules/chat.js' import oauthModule from './modules/oauth.js' -import subscribeModule from './modules/subscribe.js' import VueTimeago from 'vue-timeago' import VueI18n from 'vue-i18n' @@ -61,8 +60,7 @@ createPersistedState(persistedStateOptions).then((persistedState) => { api: apiModule, config: configModule, chat: chatModule, - oauth: oauthModule, - subscribe: subscribeModule + oauth: oauthModule }, plugins: [persistedState], strict: false // Socket modifies itself, let's ignore this for now. diff --git a/src/modules/subscribe.js b/src/modules/subscribe.js deleted file mode 100644 index e705904c..00000000 --- a/src/modules/subscribe.js +++ /dev/null @@ -1,21 +0,0 @@ -import registerPushNotifications from '../services/push/push.js' - -const subscribe = { - state: { - token: null, - vapidPublicKey: null - }, - mutations: { - setCurrentUser (state, user) { - state.token = user.credentials - if (state.token && state.vapidPublicKey) registerPushNotifications(this) - }, - setInstanceOption (state, { name, value }) { - if (name !== 'vapidPublicKey') return - state.vapidPublicKey = value - if (state.token && state.vapidPublicKey) registerPushNotifications(this) - } - } -} - -export default subscribe diff --git a/src/modules/users.js b/src/modules/users.js index 88ec7115..d2c7fdf6 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -1,4 +1,5 @@ import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js' +import registerPushNotifications from '../services/push/push.js' import { compact, map, each, merge } from 'lodash' import { set } from 'vue' @@ -119,8 +120,10 @@ const users = { }) // Fetch our friends - store.rootState.api.backendInteractor.fetchFriends({ id: user.id }) + store.rootState.api.backendInteractor.fetchFriends({id: user.id}) .then((friends) => commit('addNewUsers', friends)) + + registerPushNotifications(store) }) } else { // Authentication failed diff --git a/src/services/push/push.js b/src/services/push/push.js index 923c2178..ffab5918 100644 --- a/src/services/push/push.js +++ b/src/services/push/push.js @@ -45,17 +45,17 @@ function askPermission () { } function subscribe (registration, store) { - if (!store.state.config.webPushNotifications) { + if (!store.rootState.config.webPushNotifications) { return Promise.reject(new Error('Web Push is disabled in config')) } - if (!store.state.subscribe.vapidPublicKey) { + if (!store.rootState.instance.vapidPublicKey) { return Promise.reject(new Error('VAPID public key is not found')) } const subscribeOptions = { userVisibleOnly: true, - applicationServerKey: urlBase64ToUint8Array(store.state.subscribe.vapidPublicKey) + applicationServerKey: urlBase64ToUint8Array(store.rootState.instance.vapidPublicKey) } return registration.pushManager.subscribe(subscribeOptions) } @@ -65,7 +65,7 @@ function sendSubscriptionToBackEnd (subscription, store) { method: 'POST', headers: { 'Content-Type': 'application/json', - 'Authorization': `Bearer ${store.state.subscribe.token}` + 'Authorization': `Bearer ${store.rootState.oauth.token}` }, body: JSON.stringify({ subscription, -- cgit v1.2.3-70-g09d2 From 11716a7a5381c8f4fe06c869a4a21c52a2120e6c Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Fri, 7 Dec 2018 20:13:36 +0700 Subject: second attempt to add subscribe module and fix race condition --- src/main.js | 6 ++++-- src/modules/pushNotifications.js | 29 +++++++++++++++++++++++++++++ src/modules/users.js | 8 ++++---- 3 files changed, 37 insertions(+), 6 deletions(-) create mode 100644 src/modules/pushNotifications.js (limited to 'src/modules') diff --git a/src/main.js b/src/main.js index 378fe95c..91592191 100644 --- a/src/main.js +++ b/src/main.js @@ -10,6 +10,7 @@ import apiModule from './modules/api.js' import configModule from './modules/config.js' import chatModule from './modules/chat.js' import oauthModule from './modules/oauth.js' +import pushNotificationsModule from './modules/pushNotifications.js' import VueTimeago from 'vue-timeago' import VueI18n from 'vue-i18n' @@ -60,12 +61,13 @@ createPersistedState(persistedStateOptions).then((persistedState) => { api: apiModule, config: configModule, chat: chatModule, - oauth: oauthModule + oauth: oauthModule, + pushNotifications: pushNotificationsModule }, plugins: [persistedState], strict: false // Socket modifies itself, let's ignore this for now. // strict: process.env.NODE_ENV !== 'production' }) - afterStoreSetup({store, i18n}) + afterStoreSetup({ store, i18n }) }) diff --git a/src/modules/pushNotifications.js b/src/modules/pushNotifications.js new file mode 100644 index 00000000..43143b1f --- /dev/null +++ b/src/modules/pushNotifications.js @@ -0,0 +1,29 @@ +import registerPushNotifications from '../services/push/push.js' + +const subscribe = { + state: { + token: null, + vapidPublicKey: null + }, + mutations: { + setApiToken (state, user) { + state.token = user.credentials + }, + setVapidPublicKey (state, vapidPublicKey) { + state.vapidPublicKey = vapidPublicKey + } + + }, + actions: { + setInstanceOption (store, { name, value }) { + store.commit('setVapidPublicKey', value) + if (store.state.token) registerPushNotifications(this) + }, + setCurrentUser (store, user) { + store.commit('setApiToken', user.credentials) + if (store.state.vapidPublicKey) registerPushNotifications(this) + } + } +} + +export default subscribe diff --git a/src/modules/users.js b/src/modules/users.js index d2c7fdf6..1256e9df 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -1,5 +1,4 @@ import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js' -import registerPushNotifications from '../services/push/push.js' import { compact, map, each, merge } from 'lodash' import { set } from 'vue' @@ -87,6 +86,9 @@ const users = { store.dispatch('stopFetching', 'friends') store.commit('setBackendInteractor', backendInteractorService()) }, + setCurrentUser (store, user) { + store.commit('setCurrentUser', user) + }, loginUser (store, accessToken) { return new Promise((resolve, reject) => { const commit = store.commit @@ -98,7 +100,7 @@ const users = { .then((user) => { // user.credentials = userCredentials user.credentials = accessToken - commit('setCurrentUser', user) + store.dispatch('setCurrentUser', user) commit('addNewUsers', [user]) // Set our new backend interactor @@ -122,8 +124,6 @@ const users = { // Fetch our friends store.rootState.api.backendInteractor.fetchFriends({id: user.id}) .then((friends) => commit('addNewUsers', friends)) - - registerPushNotifications(store) }) } else { // Authentication failed -- cgit v1.2.3-70-g09d2 From 73b17d70ec3a9b1d73789c236601628bfa8c498b Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Sun, 9 Dec 2018 19:25:43 +0700 Subject: improve push notifications code --- src/modules/pushNotifications.js | 15 +++++++--- src/services/push/push.js | 65 ++++++++++++++-------------------------- static/sw.js | 33 ++++++++++---------- 3 files changed, 48 insertions(+), 65 deletions(-) (limited to 'src/modules') diff --git a/src/modules/pushNotifications.js b/src/modules/pushNotifications.js index 43143b1f..ea92f811 100644 --- a/src/modules/pushNotifications.js +++ b/src/modules/pushNotifications.js @@ -12,16 +12,23 @@ const subscribe = { setVapidPublicKey (state, vapidPublicKey) { state.vapidPublicKey = vapidPublicKey } - }, actions: { setInstanceOption (store, { name, value }) { - store.commit('setVapidPublicKey', value) - if (store.state.token) registerPushNotifications(this) + if (name === 'vapidPublicKey') { + store.commit('setVapidPublicKey', value) + + if (store.state.token) { + registerPushNotifications(store.rootState.config.webPushNotifications, value, store.state.token) + } + } }, setCurrentUser (store, user) { store.commit('setApiToken', user.credentials) - if (store.state.vapidPublicKey) registerPushNotifications(this) + + if (store.state.vapidPublicKey) { + registerPushNotifications(store.rootState.config.webPushNotifications, store.state.vapidPublicKey, user.credentials) + } } } } diff --git a/src/services/push/push.js b/src/services/push/push.js index ffab5918..4a03a73d 100644 --- a/src/services/push/push.js +++ b/src/services/push/push.js @@ -14,58 +14,42 @@ function isPushSupported () { function registerServiceWorker () { return navigator.serviceWorker.register('/static/sw.js') - .then(function (registration) { - console.log('Service worker successfully registered.') - return registration - }) - .catch(function (err) { - console.error('Unable to register service worker.', err) - }) + .catch((err) => console.error('Unable to register service worker.', err)) } function askPermission () { - return new Promise(function (resolve, reject) { - if (!window.Notification) return reject(new Error('Notifications disabled')) + return new Promise((resolve, reject) => { + const Notification = window.Notification - if (window.Notification.permission !== 'default') { - return resolve(window.Notification.permission) - } + if (!Notification) return reject(new Error('Notifications disabled')) + if (Notification.permission !== 'default') return resolve(Notification.permission) - const permissionResult = window.Notification.requestPermission(function (result) { - resolve(result) - }) + const permissionResult = Notification.requestPermission(resolve) if (permissionResult) permissionResult.then(resolve, reject) - }).then(function (permissionResult) { - if (permissionResult !== 'granted') { - throw new Error('We weren\'t granted permission.') - } + }).then((permissionResult) => { + if (permissionResult !== 'granted') throw new Error('We weren\'t granted permission.') return permissionResult }) } -function subscribe (registration, store) { - if (!store.rootState.config.webPushNotifications) { - return Promise.reject(new Error('Web Push is disabled in config')) - } - - if (!store.rootState.instance.vapidPublicKey) { - return Promise.reject(new Error('VAPID public key is not found')) - } +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')) const subscribeOptions = { userVisibleOnly: true, - applicationServerKey: urlBase64ToUint8Array(store.rootState.instance.vapidPublicKey) + applicationServerKey: urlBase64ToUint8Array(vapidPublicKey) } return registration.pushManager.subscribe(subscribeOptions) } -function sendSubscriptionToBackEnd (subscription, store) { +function sendSubscriptionToBackEnd (subscription, token) { return window.fetch('/api/v1/push/subscription/', { method: 'POST', headers: { 'Content-Type': 'application/json', - 'Authorization': `Bearer ${store.rootState.oauth.token}` + 'Authorization': `Bearer ${token}` }, body: JSON.stringify({ subscription, @@ -79,28 +63,23 @@ function sendSubscriptionToBackEnd (subscription, store) { } }) }) - .then(function (response) { - if (!response.ok) { - throw new Error('Bad status code from server.') - } - + .then((response) => { + if (!response.ok) throw new Error('Bad status code from server.') return response.json() }) - .then(function (responseData) { - if (!responseData.id) { - throw new Error('Bad response from server.') - } + .then((responseData) => { + if (!responseData.id) throw new Error('Bad response from server.') return responseData }) } -export default function registerPushNotifications (store) { +export default function registerPushNotifications (isEnabled, vapidPublicKey, token) { if (isPushSupported()) { registerServiceWorker() - .then(function (registration) { + .then((registration) => { return askPermission() - .then(() => subscribe(registration, store)) - .then((subscription) => sendSubscriptionToBackEnd(subscription, store)) + .then(() => subscribe(registration, isEnabled, vapidPublicKey)) + .then((subscription) => sendSubscriptionToBackEnd(subscription, token)) .catch((e) => console.warn(`Failed to setup Web Push Notifications: ${e.message}`)) }) } diff --git a/static/sw.js b/static/sw.js index 0402a220..d51870d5 100644 --- a/static/sw.js +++ b/static/sw.js @@ -1,32 +1,29 @@ /* eslint-env serviceworker */ -self.addEventListener('push', function (event) { - if (event.data) { - const data = event.data.json() - const promiseChain = clients.matchAll({ - includeUncontrolled: true - }).then(function (clientList) { - const list = clientList.filter((item) => item.type === 'window') - if (list.length) return - return self.registration.showNotification(data.title, data) - }) +function getWindowClients () { + return clients.matchAll({ includeUncontrolled: true }) + .then((clientList) => clientList.filter(({ type }) => type === 'window')) +} + +self.addEventListener('push', (event) => { + if (event.data) { + event.waitUntil(getWindowClients().then((list) => { + const data = event.data.json() - event.waitUntil(promiseChain) + if (list.length === 0) return self.registration.showNotification(data.title, data) + })) } }) -self.addEventListener('notificationclick', function (event) { +self.addEventListener('notificationclick', (event) => { event.notification.close() - event.waitUntil(clients.matchAll({ - includeUncontrolled: true - }).then(function (clientList) { - const list = clientList.filter((item) => item.type === 'window') - + event.waitUntil(getWindowClients().then((list) => { for (var i = 0; i < list.length; i++) { var client = list[i] if (client.url === '/' && 'focus' in client) { return client.focus() } } - if (clients.openWindow) { return clients.openWindow('/') } + + if (clients.openWindow) return clients.openWindow('/') })) }) -- cgit v1.2.3-70-g09d2 From ee70ec4c7efb49c08f0a76b6b2694c0e9910978c Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Mon, 10 Dec 2018 22:36:25 +0700 Subject: fix race condition --- src/boot/after_store.js | 5 ++++- src/main.js | 11 ++++++++--- src/modules/pushNotifications.js | 36 ------------------------------------ src/modules/users.js | 13 +++++++++---- 4 files changed, 21 insertions(+), 44 deletions(-) delete mode 100644 src/modules/pushNotifications.js (limited to 'src/modules') diff --git a/src/boot/after_store.js b/src/boot/after_store.js index 0c121fe2..0d1cabd5 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -27,7 +27,10 @@ const afterStoreSetup = ({ store, i18n }) => { store.dispatch('setInstanceOption', { name: 'registrationOpen', value: (registrationClosed === '0') }) store.dispatch('setInstanceOption', { name: 'textlimit', value: parseInt(textlimit) }) store.dispatch('setInstanceOption', { name: 'server', value: server }) - store.dispatch('setInstanceOption', { name: 'vapidPublicKey', value: vapidPublicKey }) + + if (vapidPublicKey) { + store.dispatch('setInstanceOption', { name: 'vapidPublicKey', value: vapidPublicKey }) + } var apiConfig = data.site.pleromafe diff --git a/src/main.js b/src/main.js index 91592191..e4621482 100644 --- a/src/main.js +++ b/src/main.js @@ -10,7 +10,6 @@ import apiModule from './modules/api.js' import configModule from './modules/config.js' import chatModule from './modules/chat.js' import oauthModule from './modules/oauth.js' -import pushNotificationsModule from './modules/pushNotifications.js' import VueTimeago from 'vue-timeago' import VueI18n from 'vue-i18n' @@ -61,13 +60,19 @@ createPersistedState(persistedStateOptions).then((persistedState) => { api: apiModule, config: configModule, chat: chatModule, - oauth: oauthModule, - pushNotifications: pushNotificationsModule + oauth: oauthModule }, plugins: [persistedState], strict: false // Socket modifies itself, let's ignore this for now. // strict: process.env.NODE_ENV !== 'production' }) + store.subscribe((mutation, state) => { + if ((mutation.type === 'setCurrentUser' && state.instance.vapidPublicKey) || // Login + existing key + (mutation.type === 'setInstanceOption' && mutation.payload.name === 'vapidPublicKey' && state.users.currentUser)) { // Logged in, key arrives late + store.dispatch('registerPushNotifications') + } + }) + afterStoreSetup({ store, i18n }) }) diff --git a/src/modules/pushNotifications.js b/src/modules/pushNotifications.js deleted file mode 100644 index ea92f811..00000000 --- a/src/modules/pushNotifications.js +++ /dev/null @@ -1,36 +0,0 @@ -import registerPushNotifications from '../services/push/push.js' - -const subscribe = { - state: { - token: null, - vapidPublicKey: null - }, - mutations: { - setApiToken (state, user) { - state.token = user.credentials - }, - setVapidPublicKey (state, vapidPublicKey) { - state.vapidPublicKey = vapidPublicKey - } - }, - actions: { - setInstanceOption (store, { name, value }) { - if (name === 'vapidPublicKey') { - store.commit('setVapidPublicKey', value) - - if (store.state.token) { - registerPushNotifications(store.rootState.config.webPushNotifications, value, store.state.token) - } - } - }, - setCurrentUser (store, user) { - store.commit('setApiToken', user.credentials) - - if (store.state.vapidPublicKey) { - registerPushNotifications(store.rootState.config.webPushNotifications, store.state.vapidPublicKey, user.credentials) - } - } - } -} - -export default subscribe diff --git a/src/modules/users.js b/src/modules/users.js index 1256e9df..5e0c087d 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -1,6 +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' // TODO: Unify with mergeOrAdd in statuses.js export const mergeOrAdd = (arr, obj, item) => { @@ -65,6 +66,13 @@ const users = { store.rootState.api.backendInteractor.fetchUser({id}) .then((user) => store.commit('addNewUsers', user)) }, + registerPushNotifications (store) { + const token = store.state.currentUser.credentials + const vapidPublicKey = store.rootState.instance.vapidPublicKey + const isEnabled = store.rootState.config.webPushNotifications + + registerPushNotifications(isEnabled, vapidPublicKey, token) + }, addNewStatuses (store, { statuses }) { const users = map(statuses, 'user') const retweetedUsers = compact(map(statuses, 'retweeted_status.user')) @@ -86,9 +94,6 @@ const users = { store.dispatch('stopFetching', 'friends') store.commit('setBackendInteractor', backendInteractorService()) }, - setCurrentUser (store, user) { - store.commit('setCurrentUser', user) - }, loginUser (store, accessToken) { return new Promise((resolve, reject) => { const commit = store.commit @@ -100,7 +105,7 @@ const users = { .then((user) => { // user.credentials = userCredentials user.credentials = accessToken - store.dispatch('setCurrentUser', user) + commit('setCurrentUser', user) commit('addNewUsers', [user]) // Set our new backend interactor -- cgit v1.2.3-70-g09d2 From 51dccb788798364bbb662d378f2aa2647f1845cf Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 11 Dec 2018 02:46:17 +0300 Subject: separated preview and exported from style_switcher --- src/components/export_import/export_import.vue | 75 ++++++++++++++++++ src/components/style_switcher/preview.vue | 78 +++++++++++++++++++ src/components/style_switcher/style_switcher.js | 97 ++++++++---------------- src/components/style_switcher/style_switcher.vue | 93 +++-------------------- src/modules/config.js | 4 +- 5 files changed, 200 insertions(+), 147 deletions(-) create mode 100644 src/components/export_import/export_import.vue create mode 100644 src/components/style_switcher/preview.vue (limited to 'src/modules') diff --git a/src/components/export_import/export_import.vue b/src/components/export_import/export_import.vue new file mode 100644 index 00000000..9914d54a --- /dev/null +++ b/src/components/export_import/export_import.vue @@ -0,0 +1,75 @@ + + + diff --git a/src/components/style_switcher/preview.vue b/src/components/style_switcher/preview.vue new file mode 100644 index 00000000..09a136e9 --- /dev/null +++ b/src/components/style_switcher/preview.vue @@ -0,0 +1,78 @@ + diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index adcbee25..50cd1e6f 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -8,6 +8,8 @@ import ShadowControl from '../shadow_control/shadow_control.vue' import FontControl from '../font_control/font_control.vue' import ContrastRatio from '../contrast_ratio/contrast_ratio.vue' import TabSwitcher from '../tab_switcher/tab_switcher.jsx' +import Preview from './preview.vue' +import ExportImport from '../export_import/export_import.vue' // List of color values used in v1 const v1OnlyNames = [ @@ -26,7 +28,6 @@ export default { return { availableStyles: [], selected: this.$store.state.config.theme, - invalidThemeImported: false, previewShadows: {}, previewColors: {}, @@ -293,20 +294,11 @@ export default { }, themeValid () { return !this.shadowsInvalid && !this.colorsInvalid && !this.radiiInvalid - } - }, - components: { - ColorInput, - OpacityInput, - RangeInput, - ContrastRatio, - ShadowControl, - FontControl, - TabSwitcher - }, - methods: { - exportCurrentTheme () { + }, + exportedTheme () { const saveEverything = !this.keepFonts && !this.keepShadows && !this.keepColors && !this.keepOpacity && !this.keepRoundness + + // TODO change into delete-less version. const theme = { shadows: this.shadowsLocal, fonts: this.fontsLocal, @@ -331,57 +323,24 @@ export default { delete theme.radii } - const stringified = JSON.stringify({ + return { // To separate from other random JSON files and possible future theme formats _pleroma_theme_version: 2, theme - }, null, 2) // Pretty-print and indent with 2 spaces - - // Create an invisible link with a data url and simulate a click - const e = document.createElement('a') - e.setAttribute('download', 'pleroma_theme.json') - e.setAttribute('href', 'data:application/json;base64,' + window.btoa(stringified)) - e.style.display = 'none' - - document.body.appendChild(e) - e.click() - document.body.removeChild(e) - }, - - importTheme () { - this.invalidThemeImported = false - const filePicker = document.createElement('input') - filePicker.setAttribute('type', 'file') - filePicker.setAttribute('accept', '.json') - - filePicker.addEventListener('change', event => { - if (event.target.files[0]) { - // eslint-disable-next-line no-undef - const reader = new FileReader() - reader.onload = ({target}) => { - try { - const parsed = JSON.parse(target.result) - if (parsed._pleroma_theme_version === 1) { - this.normalizeLocalState(parsed, 1) - } else if (parsed._pleroma_theme_version === 2) { - this.normalizeLocalState(parsed.theme, 2) - } else { - // A theme from the future, spooky - this.invalidThemeImported = true - } - } catch (e) { - // This will happen both if there is a JSON syntax error or the theme is missing components - this.invalidThemeImported = true - } - } - reader.readAsText(event.target.files[0]) - } - }) - - document.body.appendChild(filePicker) - filePicker.click() - document.body.removeChild(filePicker) - }, - + } + } + }, + components: { + ColorInput, + OpacityInput, + RangeInput, + ContrastRatio, + ShadowControl, + FontControl, + TabSwitcher, + Preview, + ExportImport + }, + methods: { setCustomTheme () { this.$store.dispatch('setOption', { name: 'customTheme', @@ -394,7 +353,17 @@ export default { } }) }, - + onImport (parsed) { + if (parsed._pleroma_theme_version === 1) { + this.normalizeLocalState(parsed, 1) + } else if (parsed._pleroma_theme_version === 2) { + this.normalizeLocalState(parsed.theme, 2) + } + }, + importValidator (parsed) { + const version = parsed._pleroma_theme_version + return version >= 1 || version <= 2 + }, clearAll () { const state = this.$store.state.config.customTheme const version = state.colors ? 2 : 'l1' diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index 9de60f7b..730bfef0 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -18,11 +18,14 @@
-
- - -

{{ $t('settings.invalid_theme_imported') }}

-
+
@@ -58,82 +61,7 @@
-
-
-
- {{$t('settings.style.preview.header')}} - - 99 - -
- - {{$t('settings.style.preview.header_faint')}} - - - {{$t('settings.style.preview.error')}} - - -
-
-
-
- ( ͡° ͜ʖ ͡°) -
-
-

- {{$t('settings.style.preview.content')}} -

- - - - {{$t('settings.style.preview.mono')}} - - - {{$t('settings.style.preview.link')}} - - - -
- - - - -
-
-
- -
-
- :^) -
- -
-
- - - {{$t('settings.style.preview.error')}} - - - -
- - - - - -
-
-
+
@@ -235,6 +163,7 @@
+

{{$t('settings.radii_help')}}

@@ -249,6 +178,7 @@
+
@@ -294,6 +224,7 @@

{{$t('settings.style.shadows.filter_hint.spread_zero')}}

+

{{$t('settings.style.fonts.help')}}

diff --git a/src/modules/config.js b/src/modules/config.js index fb9b3ca6..45ac8f65 100644 --- a/src/modules/config.js +++ b/src/modules/config.js @@ -1,5 +1,5 @@ import { set, delete as del } from 'vue' -import { setPreset, setColors } from '../services/style_setter/style_setter.js' +import { setPreset, applyTheme } from '../services/style_setter/style_setter.js' const browserLocale = (window.navigator.language || 'en').split('-')[0] @@ -57,7 +57,7 @@ const config = { setPreset(value, commit) break case 'customTheme': - setColors(value, commit) + applyTheme(value, commit) } } } -- cgit v1.2.3-70-g09d2 From 7b4e08dd93520e3dc1113d76e097b998d12b0f3c Mon Sep 17 00:00:00 2001 From: ValD Date: Wed, 12 Dec 2018 03:33:53 +0530 Subject: added config for preload and made attachment responsive to it --- src/components/attachment/attachment.js | 3 ++- src/components/attachment/attachment.vue | 3 +-- src/components/settings/settings.js | 4 ++++ src/components/settings/settings.vue | 4 ++++ src/i18n/en.json | 1 + src/modules/config.js | 1 + 6 files changed, 13 insertions(+), 3 deletions(-) (limited to 'src/modules') diff --git a/src/components/attachment/attachment.js b/src/components/attachment/attachment.js index 41730720..71ef2ca4 100644 --- a/src/components/attachment/attachment.js +++ b/src/components/attachment/attachment.js @@ -13,6 +13,7 @@ const Attachment = { return { nsfwImage, hideNsfwLocal: this.$store.state.config.hideNsfw, + preloadNsfwImage: this.$store.state.config.preloadNsfwImage, loopVideo: this.$store.state.config.loopVideo, showHidden: false, loading: false, @@ -27,7 +28,7 @@ const Attachment = { return fileTypeService.fileType(this.attachment.mimetype) }, hidden () { - return this.nsfw && this.hideNsfwLocal && !this.showHidden + return (this.nsfw && this.hideNsfwLocal && !this.showHidden) }, isEmpty () { return (this.type === 'html' && !this.attachment.oembed) || this.type === 'unknown' diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue index 6c8a04ed..1b1956e0 100644 --- a/src/components/attachment/attachment.vue +++ b/src/components/attachment/attachment.vue @@ -9,8 +9,7 @@ - - + diff --git a/src/components/settings/settings.js b/src/components/settings/settings.js index 19bd2e5b..4d8744da 100644 --- a/src/components/settings/settings.js +++ b/src/components/settings/settings.js @@ -14,6 +14,7 @@ const settings = { hideAttachmentsInConvLocal: user.hideAttachmentsInConv, hideNsfwLocal: user.hideNsfw, hideISPLocal: user.hideISP, + preloadNsfwImage: user.preloadNsfwImage, hidePostStatsLocal: typeof user.hidePostStats === 'undefined' ? instance.hidePostStats : user.hidePostStats, @@ -84,6 +85,9 @@ const settings = { hideNsfwLocal (value) { this.$store.dispatch('setOption', { name: 'hideNsfw', value }) }, + preloadNsfwImage(value) { + this.$store.dispatch('setOption', { name: 'preloadNsfwImage', value }) + }, hideISPLocal (value) { this.$store.dispatch('setOption', { name: 'hideISP', value }) }, diff --git a/src/components/settings/settings.vue b/src/components/settings/settings.vue index dec33505..60b70227 100644 --- a/src/components/settings/settings.vue +++ b/src/components/settings/settings.vue @@ -118,6 +118,10 @@ +
  • + + +
  • diff --git a/src/i18n/en.json b/src/i18n/en.json index 97dfcb77..dc47788c 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -125,6 +125,7 @@ "hide_attachments_in_convo": "Hide attachments in conversations", "hide_attachments_in_tl": "Hide attachments in timeline", "hide_isp": "Hide instance-specific panel", + "preload_sensitive": "Preload Sensitive Images", "hide_post_stats": "Hide post statistics (e.g. the number of favorites)", "hide_user_stats": "Hide user statistics (e.g. the number of followers)", "import_followers_from_a_csv_file": "Import follows from a csv file", diff --git a/src/modules/config.js b/src/modules/config.js index 45ac8f65..7b0b2cf4 100644 --- a/src/modules/config.js +++ b/src/modules/config.js @@ -9,6 +9,7 @@ const defaultState = { hideAttachments: false, hideAttachmentsInConv: false, hideNsfw: true, + preloadNsfwImage: true, loopVideo: true, loopVideoSilentOnly: true, autoLoad: true, -- cgit v1.2.3-70-g09d2 From 139659d42ca0a877843a4fa1606435dd6f6442db Mon Sep 17 00:00:00 2001 From: ValD Date: Wed, 12 Dec 2018 03:42:29 +0530 Subject: renamed config to preload images and add ident to config --- src/components/attachment/attachment.js | 2 +- src/components/attachment/attachment.vue | 2 +- src/components/settings/settings.js | 6 +++--- src/components/settings/settings.vue | 10 ++++++---- src/i18n/en.json | 2 +- src/modules/config.js | 2 +- 6 files changed, 13 insertions(+), 11 deletions(-) (limited to 'src/modules') diff --git a/src/components/attachment/attachment.js b/src/components/attachment/attachment.js index 71ef2ca4..fd9a2057 100644 --- a/src/components/attachment/attachment.js +++ b/src/components/attachment/attachment.js @@ -13,7 +13,7 @@ const Attachment = { return { nsfwImage, hideNsfwLocal: this.$store.state.config.hideNsfw, - preloadNsfwImage: this.$store.state.config.preloadNsfwImage, + preloadImage: this.$store.state.config.preloadImage, loopVideo: this.$store.state.config.loopVideo, showHidden: false, loading: false, diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue index 1b1956e0..5eaa0d1d 100644 --- a/src/components/attachment/attachment.vue +++ b/src/components/attachment/attachment.vue @@ -9,7 +9,7 @@ - + diff --git a/src/components/settings/settings.js b/src/components/settings/settings.js index 4d8744da..9a658536 100644 --- a/src/components/settings/settings.js +++ b/src/components/settings/settings.js @@ -14,7 +14,7 @@ const settings = { hideAttachmentsInConvLocal: user.hideAttachmentsInConv, hideNsfwLocal: user.hideNsfw, hideISPLocal: user.hideISP, - preloadNsfwImage: user.preloadNsfwImage, + preloadImage: user.preloadImage, hidePostStatsLocal: typeof user.hidePostStats === 'undefined' ? instance.hidePostStats : user.hidePostStats, @@ -85,8 +85,8 @@ const settings = { hideNsfwLocal (value) { this.$store.dispatch('setOption', { name: 'hideNsfw', value }) }, - preloadNsfwImage(value) { - this.$store.dispatch('setOption', { name: 'preloadNsfwImage', value }) + preloadImage(value) { + this.$store.dispatch('setOption', { name: 'preloadImage', value }) }, hideISPLocal (value) { this.$store.dispatch('setOption', { name: 'hideISP', value }) diff --git a/src/components/settings/settings.vue b/src/components/settings/settings.vue index 60b70227..b98d4c1a 100644 --- a/src/components/settings/settings.vue +++ b/src/components/settings/settings.vue @@ -118,10 +118,12 @@
  • -
  • - - -
  • +
      +
    • + + +
    • +
  • diff --git a/src/i18n/en.json b/src/i18n/en.json index dc47788c..92429e4b 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -125,7 +125,7 @@ "hide_attachments_in_convo": "Hide attachments in conversations", "hide_attachments_in_tl": "Hide attachments in timeline", "hide_isp": "Hide instance-specific panel", - "preload_sensitive": "Preload Sensitive Images", + "preload_images": "Preload images", "hide_post_stats": "Hide post statistics (e.g. the number of favorites)", "hide_user_stats": "Hide user statistics (e.g. the number of followers)", "import_followers_from_a_csv_file": "Import follows from a csv file", diff --git a/src/modules/config.js b/src/modules/config.js index 7b0b2cf4..72839476 100644 --- a/src/modules/config.js +++ b/src/modules/config.js @@ -9,7 +9,7 @@ const defaultState = { hideAttachments: false, hideAttachmentsInConv: false, hideNsfw: true, - preloadNsfwImage: true, + preloadImage: true, loopVideo: true, loopVideoSilentOnly: true, autoLoad: true, -- cgit v1.2.3-70-g09d2 From b3455649c53034e01725977260e69cff59c47e87 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Thu, 13 Dec 2018 18:04:09 +0700 Subject: improve notification subscription --- src/main.js | 35 +++++++++++++++++++++++++++-------- src/modules/interface.js | 11 +++++++++-- src/modules/users.js | 11 +++++++++++ src/services/push/push.js | 25 +++---------------------- 4 files changed, 50 insertions(+), 32 deletions(-) (limited to 'src/modules') diff --git a/src/main.js b/src/main.js index e4621482..23ea854b 100644 --- a/src/main.js +++ b/src/main.js @@ -50,6 +50,32 @@ const persistedStateOptions = { 'oauth' ] } + +const registerPushNotifications = store => { + store.subscribe((mutation, state) => { + const vapidPublicKey = state.instance.vapidPublicKey + 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 isVapidMutation = mutation.type === 'setInstanceOption' && mutation.payload.name === 'vapidPublicKey' + + if (isVapidMutation && user && permission) { + return store.dispatch('registerPushNotifications') + } + + const isPermMutation = mutation.type === 'setNotificationPermission' && mutation.payload === 'granted' + + if (isPermMutation && user && vapidPublicKey) { + return store.dispatch('registerPushNotifications') + } + }) +} + createPersistedState(persistedStateOptions).then((persistedState) => { const store = new Vuex.Store({ modules: { @@ -62,17 +88,10 @@ createPersistedState(persistedStateOptions).then((persistedState) => { chat: chatModule, oauth: oauthModule }, - plugins: [persistedState], + plugins: [persistedState, registerPushNotifications], strict: false // Socket modifies itself, let's ignore this for now. // strict: process.env.NODE_ENV !== 'production' }) - store.subscribe((mutation, state) => { - if ((mutation.type === 'setCurrentUser' && state.instance.vapidPublicKey) || // Login + existing key - (mutation.type === 'setInstanceOption' && mutation.payload.name === 'vapidPublicKey' && state.users.currentUser)) { // Logged in, key arrives late - store.dispatch('registerPushNotifications') - } - }) - afterStoreSetup({ store, i18n }) }) diff --git a/src/modules/interface.js b/src/modules/interface.js index 07489685..5abc2c81 100644 --- a/src/modules/interface.js +++ b/src/modules/interface.js @@ -3,7 +3,8 @@ import { set, delete as del } from 'vue' const defaultState = { settings: { currentSaveStateNotice: null, - noticeClearTimeout: null + noticeClearTimeout: null, + notificationPermission: null } } @@ -17,10 +18,13 @@ const interfaceMod = { } set(state.settings, 'currentSaveStateNotice', { error: false, data: success }) set(state.settings, 'noticeClearTimeout', - setTimeout(() => del(state.settings, 'currentSaveStateNotice'), 2000)) + setTimeout(() => del(state.settings, 'currentSaveStateNotice'), 2000)) } else { set(state.settings, 'currentSaveStateNotice', { error: true, errorData: error }) } + }, + setNotificationPermission (state, permission) { + state.notificationPermission = permission } }, actions: { @@ -29,6 +33,9 @@ const interfaceMod = { }, settingsSaved ({ commit, dispatch }, { success, error }) { commit('settingsSaved', { success, error }) + }, + setNotificationPermission ({ commit }, permission) { + commit('setNotificationPermission', permission) } } } diff --git a/src/modules/users.js b/src/modules/users.js index 5e0c087d..e4fa472d 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -19,6 +19,14 @@ export const mergeOrAdd = (arr, obj, item) => { } } +const getNotificationPermission = () => { + const Notification = window.Notification + + if (!Notification) return Promise.resolve(null) + if (Notification.permission === 'default') return Notification.requestPermission() + return Promise.resolve(Notification.permission) +} + export const mutations = { setMuted (state, { user: {id}, muted }) { const user = state.usersObject[id] @@ -108,6 +116,9 @@ const users = { commit('setCurrentUser', user) commit('addNewUsers', [user]) + getNotificationPermission() + .then(permission => commit('setNotificationPermission', permission)) + // Set our new backend interactor commit('setBackendInteractor', backendInteractorService(accessToken)) diff --git a/src/services/push/push.js b/src/services/push/push.js index 58017ed7..1ac304d1 100644 --- a/src/services/push/push.js +++ b/src/services/push/push.js @@ -19,22 +19,6 @@ function registerServiceWorker () { .catch((err) => console.error('Unable to register service worker.', err)) } -function askPermission () { - return new Promise((resolve, reject) => { - const Notification = window.Notification - - if (!Notification) return reject(new Error('Notifications disabled')) - if (Notification.permission !== 'default') return resolve(Notification.permission) - - const permissionResult = Notification.requestPermission(resolve) - - if (permissionResult) permissionResult.then(resolve, reject) - }).then((permissionResult) => { - if (permissionResult !== 'granted') throw new Error('We weren\'t granted permission.') - return permissionResult - }) -} - 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')) @@ -78,11 +62,8 @@ function sendSubscriptionToBackEnd (subscription, token) { export default function registerPushNotifications (isEnabled, vapidPublicKey, token) { if (isPushSupported()) { registerServiceWorker() - .then((registration) => { - return askPermission() - .then(() => subscribe(registration, isEnabled, vapidPublicKey)) - .then((subscription) => sendSubscriptionToBackEnd(subscription, token)) - .catch((e) => console.warn(`Failed to setup Web Push Notifications: ${e.message}`)) - }) + .then((registration) => subscribe(registration, isEnabled, vapidPublicKey)) + .then((subscription) => sendSubscriptionToBackEnd(subscription, token)) + .catch((e) => console.warn(`Failed to setup Web Push Notifications: ${e.message}`)) } } -- cgit v1.2.3-70-g09d2