diff --git a/bin/build-sass.js b/bin/build-sass.js
index be8dd3eff..a61a00957 100755
--- a/bin/build-sass.js
+++ b/bin/build-sass.js
@@ -11,7 +11,6 @@ const render = promisify(sass.render.bind(sass))
const globalScss = path.join(__dirname, '../src/scss/global.scss')
const defaultThemeScss = path.join(__dirname, '../src/scss/themes/_default.scss')
-const offlineThemeScss = path.join(__dirname, '../src/scss/themes/_offline.scss')
const customScrollbarScss = path.join(__dirname, '../src/scss/custom-scrollbars.scss')
const themesScssDir = path.join(__dirname, '../src/scss/themes')
const assetsDir = path.join(__dirname, '../static')
@@ -22,11 +21,9 @@ async function renderCss (file) {
async function compileGlobalSass () {
let mainStyle = (await Promise.all([defaultThemeScss, globalScss].map(renderCss))).join('')
- let offlineStyle = (await renderCss(offlineThemeScss))
let scrollbarStyle = (await renderCss(customScrollbarScss))
return `\n` +
- `\n` +
`\n`
}
diff --git a/bin/svgs.js b/bin/svgs.js
index 04a78d7cd..933d58fbb 100644
--- a/bin/svgs.js
+++ b/bin/svgs.js
@@ -51,5 +51,6 @@ module.exports = [
{ id: 'fa-bar-chart', src: 'src/thirdparty/font-awesome-svg-png/white/svg/bar-chart.svg' },
{ id: 'fa-clock', src: 'src/thirdparty/font-awesome-svg-png/white/svg/clock-o.svg' },
{ id: 'fa-refresh', src: 'src/thirdparty/font-awesome-svg-png/white/svg/refresh.svg' },
- { id: 'fa-plus', src: 'src/thirdparty/font-awesome-svg-png/white/svg/plus.svg' }
+ { id: 'fa-plus', src: 'src/thirdparty/font-awesome-svg-png/white/svg/plus.svg' },
+ { id: 'fa-info-circle', src: 'src/thirdparty/font-awesome-svg-png/white/svg/info-circle.svg' }
]
diff --git a/src/inline-script/inline-script.js b/src/inline-script/inline-script.js
index 1d084a2bc..2621daf36 100644
--- a/src/inline-script/inline-script.js
+++ b/src/inline-script/inline-script.js
@@ -3,16 +3,21 @@
// To allow CSP to work correctly, we also calculate a sha256 hash during
// the build process and write it to checksum.js.
-import { testHasLocalStorageOnce } from '../routes/_utils/testStorage'
import { INLINE_THEME, DEFAULT_THEME, switchToTheme } from '../routes/_utils/themeEngine'
import { basename } from '../routes/_api/utils'
import { onUserIsLoggedOut } from '../routes/_actions/onUserIsLoggedOut'
+import { storeLite } from '../routes/_store/storeLite'
window.__themeColors = process.env.THEME_COLORS
-const safeParse = str => (typeof str === 'undefined' || str === 'undefined') ? undefined : JSON.parse(str)
-const hasLocalStorage = testHasLocalStorageOnce()
-const currentInstance = hasLocalStorage && safeParse(localStorage.store_currentInstance)
+const {
+ currentInstance,
+ instanceThemes,
+ disableCustomScrollbars,
+ enableGrayscale
+} = storeLite.get()
+
+const theme = (instanceThemes && instanceThemes[currentInstance]) || DEFAULT_THEME
if (currentInstance) {
// Do prefetch if we're logged in, so we can connect faster to the other origin.
@@ -26,24 +31,23 @@ if (currentInstance) {
document.head.appendChild(link)
}
-let theme = (currentInstance &&
- localStorage.store_instanceThemes &&
- safeParse(localStorage.store_instanceThemes)[safeParse(localStorage.store_currentInstance)]) ||
- DEFAULT_THEME
if (theme !== INLINE_THEME) {
// switch theme ASAP to minimize flash of default theme
- switchToTheme(theme)
+ switchToTheme(theme, enableGrayscale)
+}
+
+if (enableGrayscale) {
+ document.body.classList.add('grayscale')
}
-if (!hasLocalStorage || !currentInstance) {
+if (!currentInstance) {
// if not logged in, show all these 'hidden-from-ssr' elements
onUserIsLoggedOut()
}
-if (hasLocalStorage && localStorage.store_disableCustomScrollbars === 'true') {
- // if user has disabled custom scrollbars, remove this style
- let theScrollbarStyle = document.getElementById('theScrollbarStyle')
- theScrollbarStyle.setAttribute('media', 'only x') // disables the style
+if (disableCustomScrollbars) {
+ document.getElementById('theScrollbarStyle')
+ .setAttribute('media', 'only x') // disables the style
}
// hack to make the scrollbars rounded only on macOS
diff --git a/src/routes/_actions/addInstance.js b/src/routes/_actions/addInstance.js
index df808824f..8a2a2a4e0 100644
--- a/src/routes/_actions/addInstance.js
+++ b/src/routes/_actions/addInstance.js
@@ -84,7 +84,8 @@ async function registerNewInstance (code) {
instanceThemes: instanceThemes
})
store.save()
- switchToTheme(DEFAULT_THEME)
+ let { enableGrayscale } = store.get()
+ switchToTheme(DEFAULT_THEME, enableGrayscale)
// fire off these requests so they're cached
/* no await */ updateVerifyCredentialsForInstance(currentRegisteredInstanceName)
/* no await */ updateCustomEmojiForInstance(currentRegisteredInstanceName)
diff --git a/src/routes/_actions/instances.js b/src/routes/_actions/instances.js
index 21d6816d1..92807f5fb 100644
--- a/src/routes/_actions/instances.js
+++ b/src/routes/_actions/instances.js
@@ -1,6 +1,6 @@
import { getVerifyCredentials } from '../_api/user'
import { store } from '../_store/store'
-import { DEFAULT_THEME, switchToTheme } from '../_utils/themeEngine'
+import { switchToTheme } from '../_utils/themeEngine'
import { toast } from '../_components/toast/toast'
import { goto } from '../../../__sapper__/client'
import { cacheFirstUpdateAfter } from '../_utils/sync'
@@ -14,7 +14,8 @@ export function changeTheme (instanceName, newTheme) {
store.save()
let { currentInstance } = store.get()
if (instanceName === currentInstance) {
- switchToTheme(newTheme)
+ let { enableGrayscale } = store.get()
+ switchToTheme(newTheme, enableGrayscale)
}
}
@@ -26,7 +27,8 @@ export function switchToInstance (instanceName) {
queryInSearch: ''
})
store.save()
- switchToTheme(instanceThemes[instanceName])
+ let { enableGrayscale } = store.get()
+ switchToTheme(instanceThemes[instanceName], enableGrayscale)
}
export async function logOutOfInstance (instanceName) {
@@ -55,7 +57,8 @@ export async function logOutOfInstance (instanceName) {
})
store.save()
toast.say(`Logged out of ${instanceName}`)
- switchToTheme(instanceThemes[newInstance] || DEFAULT_THEME)
+ let { enableGrayscale } = store.get()
+ switchToTheme(instanceThemes[newInstance], enableGrayscale)
/* no await */ database.clearDatabaseForInstance(instanceName)
goto('/settings/instances')
}
diff --git a/src/routes/_components/profile/AccountProfileDetails.html b/src/routes/_components/profile/AccountProfileDetails.html
index 51dbe4883..a95b45189 100644
--- a/src/routes/_components/profile/AccountProfileDetails.html
+++ b/src/routes/_components/profile/AccountProfileDetails.html
@@ -127,7 +127,12 @@
Stats and more options
numFollowers: ({ account }) => account.followers_count || 0,
numStatusesDisplay: ({ numStatuses }) => numberFormat.format(numStatuses),
numFollowingDisplay: ({ numFollowing }) => numberFormat.format(numFollowing),
- numFollowersDisplay: ({ numFollowers }) => numberFormat.format(numFollowers),
+ numFollowersDisplay: ({ numFollowers, $disableFollowerCounts }) => {
+ if ($disableFollowerCounts && numFollowers >= 10) {
+ return '10+'
+ }
+ return numberFormat.format(numFollowers)
+ },
followersLabel: ({ numFollowers }) => `Followed by ${numFollowers}`,
followingLabel: ({ numFollowing }) => `Follows ${numFollowing}`
},
diff --git a/src/routes/_components/status/StatusDetails.html b/src/routes/_components/status/StatusDetails.html
index d3c4b2a2e..8c537af75 100644
--- a/src/routes/_components/status/StatusDetails.html
+++ b/src/routes/_components/status/StatusDetails.html
@@ -158,13 +158,19 @@
application: ({ originalStatus }) => originalStatus.application,
applicationName: ({ application }) => (application && application.name),
applicationWebsite: ({ application }) => (application && application.website),
- numReblogs: ({ overrideNumReblogs, originalStatus }) => {
+ numReblogs: ({ $disableReblogCounts, overrideNumReblogs, originalStatus }) => {
+ if ($disableReblogCounts) {
+ return 0
+ }
if (typeof overrideNumReblogs === 'number') {
return overrideNumReblogs
}
return originalStatus.reblogs_count || 0
},
- numFavs: ({ overrideNumFavs, originalStatus }) => {
+ numFavs: ({ $disableFavCounts, overrideNumFavs, originalStatus }) => {
+ if ($disableFavCounts) {
+ return 0
+ }
if (typeof overrideNumFavs === 'number') {
return overrideNumFavs
}
@@ -173,13 +179,19 @@
displayAbsoluteFormattedDate: ({ createdAtDateTS, $isMobileSize }) => (
($isMobileSize ? shortAbsoluteDateFormatter : absoluteDateFormatter).format(createdAtDateTS)
),
- reblogsLabel: ({ numReblogs }) => {
+ reblogsLabel: ({ $disableReblogCounts, numReblogs }) => {
+ if ($disableReblogCounts) {
+ return 'Boost counts hidden'
+ }
// TODO: intl
return numReblogs === 1
? `Boosted ${numReblogs} time`
: `Boosted ${numReblogs} times`
},
- favoritesLabel: ({ numFavs }) => {
+ favoritesLabel: ({ $disableFavCounts, numFavs }) => {
+ if ($disableFavCounts) {
+ return 'Favorite counts hidden'
+ }
// TODO: intl
return numFavs === 1
? `Favorited ${numFavs} time`
diff --git a/src/routes/_pages/settings/index.html b/src/routes/_pages/settings/index.html
index 02a304578..151068665 100644
--- a/src/routes/_pages/settings/index.html
+++ b/src/routes/_pages/settings/index.html
@@ -8,6 +8,9 @@ Settings
+
+
+
diff --git a/src/routes/_pages/settings/wellness.html b/src/routes/_pages/settings/wellness.html
new file mode 100644
index 000000000..077a9655a
--- /dev/null
+++ b/src/routes/_pages/settings/wellness.html
@@ -0,0 +1,154 @@
+
+ Wellness Settings
+
+
+ Wellness settings are designed to reduce the addictive or anxiety-inducing aspects of social media.
+ Choose any options that work well for you.
+
+
+
+
+ Metrics
+
+
+
+ Notifications
+
+
+
+
+
+ UI
+
+
+
+ These settings are partly based on guidelines from the
+ Center for Humane Technology.
+
+
+
+
diff --git a/src/routes/_static/themes.js b/src/routes/_static/themes.js
index f52dd9cc4..7f0b5754a 100644
--- a/src/routes/_static/themes.js
+++ b/src/routes/_static/themes.js
@@ -41,6 +41,12 @@ const themes = [
dark: false,
color: '#4ab92f'
},
+ {
+ name: 'grayscale',
+ label: 'Grayscale',
+ dark: false,
+ color: '#999999'
+ },
{
name: 'ozark',
label: 'Ozark',
@@ -88,6 +94,12 @@ const themes = [
label: 'Pitch Black',
dark: true,
color: '#000'
+ },
+ {
+ name: 'dark-grayscale',
+ label: 'Dark Grayscale',
+ dark: true,
+ color: '#666'
}
]
diff --git a/src/routes/_store/LocalStorageStore.js b/src/routes/_store/LocalStorageStore.js
index 4c1bb8dba..edfa019c4 100644
--- a/src/routes/_store/LocalStorageStore.js
+++ b/src/routes/_store/LocalStorageStore.js
@@ -1,10 +1,7 @@
import { Store } from 'svelte/store'
import { safeLocalStorage as LS } from '../_utils/safeLocalStorage'
import lifecycle from 'page-lifecycle/dist/lifecycle.mjs'
-
-function safeParse (str) {
- return !str ? undefined : (str === 'undefined' ? undefined : JSON.parse(str))
-}
+import { safeParse } from './safeParse'
export class LocalStorageStore extends Store {
constructor (state, keysToWatch) {
diff --git a/src/routes/_store/computations/timelineComputations.js b/src/routes/_store/computations/timelineComputations.js
index 5280151d6..5716929d3 100644
--- a/src/routes/_store/computations/timelineComputations.js
+++ b/src/routes/_store/computations/timelineComputations.js
@@ -174,7 +174,9 @@ export function timelineComputations (store) {
)
store.compute('hasNotifications',
- ['numberOfNotifications', 'currentPage'],
- (numberOfNotifications, currentPage) => currentPage !== 'notifications' && !!numberOfNotifications
+ ['numberOfNotifications', 'currentPage', 'disableNotificationBadge'],
+ (numberOfNotifications, currentPage, $disableNotificationBadge) => (
+ !$disableNotificationBadge && currentPage !== 'notifications' && !!numberOfNotifications
+ )
)
}
diff --git a/src/routes/_store/observers/grayscaleObservers.js b/src/routes/_store/observers/grayscaleObservers.js
new file mode 100644
index 000000000..04d197724
--- /dev/null
+++ b/src/routes/_store/observers/grayscaleObservers.js
@@ -0,0 +1,14 @@
+import { switchToTheme } from '../../_utils/themeEngine'
+
+export function grayscaleObservers (store) {
+ if (!process.browser) {
+ return
+ }
+
+ store.observe('enableGrayscale', enableGrayscale => {
+ const { instanceThemes, currentInstance } = store.get()
+ const theme = instanceThemes && instanceThemes[currentInstance]
+ document.body.classList.toggle('grayscale', enableGrayscale)
+ switchToTheme(theme, enableGrayscale)
+ })
+}
diff --git a/src/routes/_store/observers/observers.js b/src/routes/_store/observers/observers.js
index ff8f6b1fb..527f64414 100644
--- a/src/routes/_store/observers/observers.js
+++ b/src/routes/_store/observers/observers.js
@@ -6,6 +6,7 @@ import { resizeObservers } from './resizeObservers'
import { setupLoggedInObservers } from './setupLoggedInObservers'
import { logOutObservers } from './logOutObservers'
import { touchObservers } from './touchObservers'
+import { grayscaleObservers } from './grayscaleObservers'
export function observers (store) {
onlineObservers(store)
@@ -15,5 +16,6 @@ export function observers (store) {
resizeObservers(store)
touchObservers(store)
logOutObservers(store)
+ grayscaleObservers(store)
setupLoggedInObservers(store)
}
diff --git a/src/routes/_store/observers/onlineObservers.js b/src/routes/_store/observers/onlineObservers.js
index ebe9a156d..a0c730870 100644
--- a/src/routes/_store/observers/onlineObservers.js
+++ b/src/routes/_store/observers/onlineObservers.js
@@ -6,8 +6,6 @@ const NOTIFY_OFFLINE_LIMIT = 1
let notifyCount = 0
-let offlineStyle = process.browser && document.getElementById('theOfflineStyle')
-
// debounce to avoid notifying for a short connection issue
const notifyOffline = debounce(() => {
if (process.browser && !navigator.onLine && ++notifyCount <= NOTIFY_OFFLINE_LIMIT) {
@@ -19,20 +17,9 @@ export function onlineObservers (store) {
if (!process.browser) {
return
}
- let meta = document.getElementById('theThemeColor')
- let oldTheme = meta.content
store.observe('online', online => {
- // "only x" ensures the