Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement wellness settings #1256

Merged
merged 3 commits into from Jun 1, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 0 additions & 3 deletions bin/build-sass.js
Expand Up @@ -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')
Expand All @@ -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 `<style>\n${mainStyle}</style>\n` +
`<style media="only x" id="theOfflineStyle">\n${offlineStyle}</style>\n` +
`<style media="all" id="theScrollbarStyle">\n${scrollbarStyle}</style>\n`
}

Expand Down
3 changes: 2 additions & 1 deletion bin/svgs.js
Expand Up @@ -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' }
]
32 changes: 18 additions & 14 deletions src/inline-script/inline-script.js
Expand Up @@ -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.
Expand All @@ -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
Expand Down
3 changes: 2 additions & 1 deletion src/routes/_actions/addInstance.js
Expand Up @@ -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)
Expand Down
11 changes: 7 additions & 4 deletions 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'
Expand All @@ -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)
}
}

Expand All @@ -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) {
Expand Down Expand Up @@ -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')
}
Expand Down
7 changes: 6 additions & 1 deletion src/routes/_components/profile/AccountProfileDetails.html
Expand Up @@ -127,7 +127,12 @@ <h2 class="sr-only">Stats and more options</h2>
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}`
},
Expand Down
20 changes: 16 additions & 4 deletions src/routes/_components/status/StatusDetails.html
Expand Up @@ -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
}
Expand All @@ -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`
Expand Down
3 changes: 3 additions & 0 deletions src/routes/_pages/settings/index.html
Expand Up @@ -8,6 +8,9 @@ <h1>Settings</h1>
<SettingsListRow>
<SettingsListButton href="/settings/instances" label="Instances"/>
</SettingsListRow>
<SettingsListRow>
<SettingsListButton href="/settings/wellness" label="Wellness"/>
</SettingsListRow>
<SettingsListRow>
<SettingsListButton href="/settings/hotkeys" label="Hotkeys"/>
</SettingsListRow>
Expand Down
154 changes: 154 additions & 0 deletions src/routes/_pages/settings/wellness.html
@@ -0,0 +1,154 @@
<SettingsLayout page='settings/general' label="General">
<h1>Wellness Settings</h1>

<p>
Wellness settings are designed to reduce the addictive or anxiety-inducing aspects of social media.
Choose any options that work well for you.
</p>

<form class="ui-settings">
<div class="setting-group">
<input type="checkbox" id="choice-check-all"
on:change="onCheckAllChange(event)">
<label for="choice-check-all">Enable all</label>
</div>
</form>

<h2>Metrics</h2>

<form class="ui-settings">
<div class="setting-group">
<input type="checkbox" id="choice-disable-follower-counts"
bind:checked="$disableFollowerCounts" on:change="onChange(event)">
<label for="choice-disable-follower-counts">
Hide follower counts (capped at 10)
</label>
</div>
<div class="setting-group">
<input type="checkbox" id="choice-disable-reblog-counts"
bind:checked="$disableReblogCounts" on:change="onChange(event)">
<label for="choice-disable-reblog-counts">Hide boost counts</label>
</div>
<div class="setting-group">
<input type="checkbox" id="choice-disable-fav-counts"
bind:checked="$disableFavCounts" on:change="onChange(event)">
<label for="choice-disable-fav-counts">Hide favorite counts</label>
</div>
</form>

<h2>Notifications</h2>

<form class="ui-settings">
<div class="setting-group">
<input type="checkbox" id="choice-disable-unread-notification-counts"
bind:checked="$disableNotificationBadge" on:change="onChange(event)">
<label for="choice-disable-unread-notification-counts">
Hide unread notifications count (i.e. the red dot)
</label>
</div>
</form>

<aside>
<SvgIcon href="#fa-info-circle" className="aside-icon" />
<span>
You can filter or disable notifications in the
<a rel="prefetch" href="/settings/instances{$currentInstance ? '/' + $currentInstance : ''}">instance settings</a>.
</span>
</aside>

<h2>UI</h2>

<form class="ui-settings">
<div class="setting-group">
<input type="checkbox" id="choice-grayscale"
bind:checked="$enableGrayscale" on:change="onChange(event)">
<label for="choice-grayscale">Grayscale mode</label>
</div>
</form>
<p>
These settings are partly based on guidelines from the
<ExternalLink href="https://humanetech.com">Center for Humane Technology</ExternalLink>.
</p>
</SettingsLayout>
<style>
.ui-settings {
background: var(--form-bg);
border: 1px solid var(--main-border);
border-radius: 4px;
padding: 20px;
line-height: 2em;
}
.setting-group {
padding: 5px 0;
}
aside {
font-size: 1.2em;
margin: 20px 10px 0px 10px;
color: var(--deemphasized-text-color);
display: flex;
align-items: center;
}
aside a {
text-decoration: underline;
color: var(--deemphasized-text-color);
}
aside span {
flex: 1;
}
:global(.aside-icon) {
fill: var(--deemphasized-text-color);
width: 18px;
height: 18px;
margin: 0 10px 0 5px;
min-width: 18px;
}
</style>
<script>
import SettingsLayout from '../../_components/settings/SettingsLayout.html'
import { store } from '../../_store/store'
import ExternalLink from '../../_components/ExternalLink.html'
import SvgIcon from '../../_components/SvgIcon.html'

export default {
oncreate () {
this.flushChangesToCheckAll()
},
components: {
SettingsLayout,
ExternalLink,
SvgIcon
},
methods: {
flushChangesToCheckAll () {
const {
disableFollowerCounts,
disableReblogCounts,
disableFavCounts,
disableNotificationBadge,
enableGrayscale
} = this.store.get()
document.querySelector('#choice-check-all').checked = disableFollowerCounts &&
disableReblogCounts &&
disableFavCounts &&
disableNotificationBadge &&
enableGrayscale
},
onCheckAllChange (e) {
let { checked } = e.target
this.store.set({
disableFollowerCounts: checked,
disableReblogCounts: checked,
disableFavCounts: checked,
disableNotificationBadge: checked,
enableGrayscale: checked
})
this.store.save()
},
onChange () {
this.flushChangesToCheckAll()
this.store.save()
}
},
store: () => store
}
</script>
12 changes: 12 additions & 0 deletions src/routes/_static/themes.js
Expand Up @@ -41,6 +41,12 @@ const themes = [
dark: false,
color: '#4ab92f'
},
{
name: 'grayscale',
label: 'Grayscale',
dark: false,
color: '#999999'
},
{
name: 'ozark',
label: 'Ozark',
Expand Down Expand Up @@ -88,6 +94,12 @@ const themes = [
label: 'Pitch Black',
dark: true,
color: '#000'
},
{
name: 'dark-grayscale',
label: 'Dark Grayscale',
dark: true,
color: '#666'
}
]

Expand Down
5 changes: 1 addition & 4 deletions 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) {
Expand Down