From f1867a73881444dfed9e093425435681ee764922 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 4 Apr 2018 21:47:39 +0200 Subject: Adjust privacy policy (#6666) * Adjust privacy policy to be more specific to Mastodon Fix #6613 * Change data retention of IP addresses from 5 years to 1 year * Add even more information * Remove all (now invalid) translations of the privacy policy * Add information about archive takeout, remove pointless consent section * Emphasis on DM privacy * Improve wording * Add line about data use for moderation purposes --- app/javascript/styles/mastodon/about.scss | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'app/javascript') diff --git a/app/javascript/styles/mastodon/about.scss b/app/javascript/styles/mastodon/about.scss index 03211036c..034c35e8a 100644 --- a/app/javascript/styles/mastodon/about.scss +++ b/app/javascript/styles/mastodon/about.scss @@ -322,6 +322,11 @@ $small-breakpoint: 960px; border: 0; border-bottom: 1px solid rgba($ui-base-lighter-color, .6); margin: 20px 0; + + &.spacer { + height: 1px; + border: 0; + } } .container-alt { -- cgit From b08ab329f4d149fd414e0539574f49062c571a8a Mon Sep 17 00:00:00 2001 From: Isatis <515462+Reverite@users.noreply.github.com> Date: Wed, 4 Apr 2018 13:25:34 -0700 Subject: retrieve custom emoji list via API instead of before page load (#7047) --- app/javascript/mastodon/actions/custom_emojis.js | 37 +++++++++++++++++++++++ app/javascript/mastodon/containers/mastodon.js | 4 +++ app/javascript/mastodon/reducers/custom_emojis.js | 17 +++++------ app/serializers/initial_state_serializer.rb | 6 ---- 4 files changed, 49 insertions(+), 15 deletions(-) create mode 100644 app/javascript/mastodon/actions/custom_emojis.js (limited to 'app/javascript') diff --git a/app/javascript/mastodon/actions/custom_emojis.js b/app/javascript/mastodon/actions/custom_emojis.js new file mode 100644 index 000000000..aa37bc423 --- /dev/null +++ b/app/javascript/mastodon/actions/custom_emojis.js @@ -0,0 +1,37 @@ +import api from '../api'; + +export const CUSTOM_EMOJIS_FETCH_REQUEST = 'CUSTOM_EMOJIS_FETCH_REQUEST'; +export const CUSTOM_EMOJIS_FETCH_SUCCESS = 'CUSTOM_EMOJIS_FETCH_SUCCESS'; +export const CUSTOM_EMOJIS_FETCH_FAIL = 'CUSTOM_EMOJIS_FETCH_FAIL'; + +export function fetchCustomEmojis() { + return (dispatch, getState) => { + dispatch(fetchCustomEmojisRequest()); + + api(getState).get('/api/v1/custom_emojis').then(response => { + dispatch(fetchCustomEmojisSuccess(response.data)); + }).catch(error => { + dispatch(fetchCustomEmojisFail(error)); + }); + }; +}; + +export function fetchCustomEmojisRequest() { + return { + type: CUSTOM_EMOJIS_FETCH_REQUEST, + }; +}; + +export function fetchCustomEmojisSuccess(custom_emojis) { + return { + type: CUSTOM_EMOJIS_FETCH_SUCCESS, + custom_emojis, + }; +}; + +export function fetchCustomEmojisFail(error) { + return { + type: CUSTOM_EMOJIS_FETCH_FAIL, + error, + }; +}; diff --git a/app/javascript/mastodon/containers/mastodon.js b/app/javascript/mastodon/containers/mastodon.js index d1710445b..b29898d3b 100644 --- a/app/javascript/mastodon/containers/mastodon.js +++ b/app/javascript/mastodon/containers/mastodon.js @@ -6,6 +6,7 @@ import { showOnboardingOnce } from '../actions/onboarding'; import { BrowserRouter, Route } from 'react-router-dom'; import { ScrollContext } from 'react-router-scroll-4'; import UI from '../features/ui'; +import { fetchCustomEmojis } from '../actions/custom_emojis'; import { hydrateStore } from '../actions/store'; import { connectUserStream } from '../actions/streaming'; import { IntlProvider, addLocaleData } from 'react-intl'; @@ -19,6 +20,9 @@ export const store = configureStore(); const hydrateAction = hydrateStore(initialState); store.dispatch(hydrateAction); +// load custom emojis +store.dispatch(fetchCustomEmojis()); + export default class Mastodon extends React.PureComponent { static propTypes = { diff --git a/app/javascript/mastodon/reducers/custom_emojis.js b/app/javascript/mastodon/reducers/custom_emojis.js index 307bcc7dc..d2c801ade 100644 --- a/app/javascript/mastodon/reducers/custom_emojis.js +++ b/app/javascript/mastodon/reducers/custom_emojis.js @@ -1,16 +1,15 @@ -import { List as ImmutableList } from 'immutable'; -import { STORE_HYDRATE } from '../actions/store'; +import { List as ImmutableList, fromJS as ConvertToImmutable } from 'immutable'; +import { CUSTOM_EMOJIS_FETCH_SUCCESS } from '../actions/custom_emojis'; import { search as emojiSearch } from '../features/emoji/emoji_mart_search_light'; import { buildCustomEmojis } from '../features/emoji/emoji'; -const initialState = ImmutableList(); +const initialState = ImmutableList([]); export default function custom_emojis(state = initialState, action) { - switch(action.type) { - case STORE_HYDRATE: - emojiSearch('', { custom: buildCustomEmojis(action.state.get('custom_emojis', [])) }); - return action.state.get('custom_emojis'); - default: - return state; + if(action.type === CUSTOM_EMOJIS_FETCH_SUCCESS) { + state = ConvertToImmutable(action.custom_emojis); + emojiSearch('', { custom: buildCustomEmojis(state) }); } + + return state; }; diff --git a/app/serializers/initial_state_serializer.rb b/app/serializers/initial_state_serializer.rb index 216cf5446..3b908e224 100644 --- a/app/serializers/initial_state_serializer.rb +++ b/app/serializers/initial_state_serializer.rb @@ -4,12 +4,6 @@ class InitialStateSerializer < ActiveModel::Serializer attributes :meta, :compose, :accounts, :media_attachments, :settings, :push_subscription - has_many :custom_emojis, serializer: REST::CustomEmojiSerializer - - def custom_emojis - CustomEmoji.local.where(disabled: false) - end - def meta store = { streaming_api_base_url: Rails.configuration.x.streaming_api_base_url, -- cgit From 98146281e1beaf994710b13ef70f6224e8588cba Mon Sep 17 00:00:00 2001 From: Harmon Date: Sat, 7 Apr 2018 07:53:11 -0500 Subject: Remove duplicate frequently used emojis (#7064) --- .../features/compose/containers/emoji_picker_dropdown_container.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/features/compose/containers/emoji_picker_dropdown_container.js b/app/javascript/mastodon/features/compose/containers/emoji_picker_dropdown_container.js index e6a535a5d..5ec937a39 100644 --- a/app/javascript/mastodon/features/compose/containers/emoji_picker_dropdown_container.js +++ b/app/javascript/mastodon/features/compose/containers/emoji_picker_dropdown_container.js @@ -38,7 +38,8 @@ const getFrequentlyUsedEmojis = createSelector([ .toArray(); if (emojis.length < DEFAULTS.length) { - emojis = emojis.concat(DEFAULTS.slice(0, DEFAULTS.length - emojis.length)); + let uniqueDefaults = DEFAULTS.filter(emoji => !emojis.includes(emoji)); + emojis = emojis.concat(uniqueDefaults.slice(0, DEFAULTS.length - emojis.length)); } return emojis; -- cgit From 4a9becfca2d7399acb422da646c48bdd9f39c989 Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Sat, 7 Apr 2018 16:36:03 -0300 Subject: i18n: improve "Welcome" translation in Portuguese (#7068) * i18n: update gender-neutral language for pt and pt-BR Instead of using "bem-vindo(a)" (a masculine form of "Welcome" with a "(a)" in the end to mean "bem-vinda" for the feminine form), use "boas-vindas", which is a gender-neutral form of "Welcome"). There is already precedent for using "boas-vindas" in the Brazilian Portuguese localization, in `config/locales/pt-BR.yml`. European Portuguese dictionary Priberam also registers it as a valid form: https://www.priberam.pt/dlpo/boas-vindas * i18n: pt-BR minor orthography fix The form "a bordo" does not take an accent. http://oredator.com.br/curso-de-redacao/uncategorized/a-bordo-ou-a-bordo --- app/javascript/mastodon/locales/pt-BR.json | 2 +- app/javascript/mastodon/locales/pt.json | 2 +- config/locales/pt-BR.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json index b056ec8bd..4cd2e0643 100644 --- a/app/javascript/mastodon/locales/pt-BR.json +++ b/app/javascript/mastodon/locales/pt-BR.json @@ -189,7 +189,7 @@ "onboarding.page_one.federation": "Mastodon é uma rede de servidores independentes que se juntam para fazer uma grande rede social. Nós chamamos estes servidores de instâncias.", "onboarding.page_one.full_handle": "Seu nome de usuário completo", "onboarding.page_one.handle_hint": "Isso é o que você diz aos seus amigos para que eles possam te mandar mensagens ou te seguir a partir de outra instância.", - "onboarding.page_one.welcome": "Seja bem-vindo(a) ao Mastodon!", + "onboarding.page_one.welcome": "Boas-vindas ao Mastodon!", "onboarding.page_six.admin": "O administrador de sua instância é {admin}.", "onboarding.page_six.almost_done": "Quase acabando...", "onboarding.page_six.appetoot": "Bom Apetoot!", diff --git a/app/javascript/mastodon/locales/pt.json b/app/javascript/mastodon/locales/pt.json index 65983000c..7a404eaba 100644 --- a/app/javascript/mastodon/locales/pt.json +++ b/app/javascript/mastodon/locales/pt.json @@ -189,7 +189,7 @@ "onboarding.page_one.federation": "Mastodon é uma rede de servidores independentes ligados entre si para fazer uma grande rede social. Nós chamamos instâncias a estes servidores.", "onboarding.page_one.full_handle": "O teu nome de utilizador completo", "onboarding.page_one.handle_hint": "Isto é o que dizes aos teus amigos para pesquisar.", - "onboarding.page_one.welcome": "Bem-vindo(a) ao Mastodon!", + "onboarding.page_one.welcome": "Boas-vindas ao Mastodon!", "onboarding.page_six.admin": "O administrador da tua instância é {admin}.", "onboarding.page_six.almost_done": "Quase pronto...", "onboarding.page_six.appetoot": "Bon Appetoot!", diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index c1225d356..d6f463a19 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -709,7 +709,7 @@ pt-BR: tip_local_timeline: A timeline local é uma visão contínua das pessoas que estão em %{instance}. Esses são seus vizinhos próximos! tip_mobile_webapp: Se o seu navegador móvel oferecer a opção de adicionar Mastodon à tela inicial, você pode receber notificações push. Vai funcionar quase como um aplicativo nativo! tips: Dicas - title: Boas-vindas à bordo, %{name}! + title: Boas-vindas a bordo, %{name}! users: invalid_email: O endereço de e-mail é inválido invalid_otp_token: Código de autenticação inválido -- cgit From 1ed1014546bcfef0d2441702673deab586f6bca0 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 8 Apr 2018 20:32:39 +0900 Subject: Free stroage if it is exceeding disk quota (#7061) --- app/javascript/mastodon/actions/accounts.js | 9 ++- app/javascript/mastodon/actions/importer/index.js | 3 +- app/javascript/mastodon/actions/statuses.js | 11 +++- app/javascript/mastodon/service_worker/entry.js | 13 +++- app/javascript/mastodon/storage/db.js | 9 ++- app/javascript/mastodon/storage/modifier.js | 80 +++++++++++++++++------ 6 files changed, 89 insertions(+), 36 deletions(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/actions/accounts.js b/app/javascript/mastodon/actions/accounts.js index 28ae56763..c9e4afcfc 100644 --- a/app/javascript/mastodon/actions/accounts.js +++ b/app/javascript/mastodon/actions/accounts.js @@ -1,5 +1,5 @@ import api, { getLinks } from '../api'; -import asyncDB from '../storage/db'; +import openDB from '../storage/db'; import { importAccount, importFetchedAccount, importFetchedAccounts } from './importer'; export const ACCOUNT_FETCH_REQUEST = 'ACCOUNT_FETCH_REQUEST'; @@ -94,12 +94,15 @@ export function fetchAccount(id) { dispatch(fetchAccountRequest(id)); - asyncDB.then(db => getFromDB( + openDB().then(db => getFromDB( dispatch, getState, db.transaction('accounts', 'read').objectStore('accounts').index('id'), id - )).catch(() => api(getState).get(`/api/v1/accounts/${id}`).then(response => { + ).then(() => db.close(), error => { + db.close(); + throw error; + })).catch(() => api(getState).get(`/api/v1/accounts/${id}`).then(response => { dispatch(importFetchedAccount(response.data)); })).then(() => { dispatch(fetchAccountSuccess()); diff --git a/app/javascript/mastodon/actions/importer/index.js b/app/javascript/mastodon/actions/importer/index.js index e671d417c..5b18cbc1d 100644 --- a/app/javascript/mastodon/actions/importer/index.js +++ b/app/javascript/mastodon/actions/importer/index.js @@ -1,3 +1,4 @@ +import { autoPlayGif } from '../../initial_state'; import { putAccounts, putStatuses } from '../../storage/modifier'; import { normalizeAccount, normalizeStatus } from './normalizer'; @@ -44,7 +45,7 @@ export function importFetchedAccounts(accounts) { } accounts.forEach(processAccount); - putAccounts(normalAccounts); + putAccounts(normalAccounts, !autoPlayGif); return importAccounts(normalAccounts); } diff --git a/app/javascript/mastodon/actions/statuses.js b/app/javascript/mastodon/actions/statuses.js index d28aef880..849cb4f5a 100644 --- a/app/javascript/mastodon/actions/statuses.js +++ b/app/javascript/mastodon/actions/statuses.js @@ -1,5 +1,5 @@ import api from '../api'; -import asyncDB from '../storage/db'; +import openDB from '../storage/db'; import { evictStatus } from '../storage/modifier'; import { deleteFromTimelines } from './timelines'; @@ -92,12 +92,17 @@ export function fetchStatus(id) { dispatch(fetchStatusRequest(id, skipLoading)); - asyncDB.then(db => { + openDB().then(db => { const transaction = db.transaction(['accounts', 'statuses'], 'read'); const accountIndex = transaction.objectStore('accounts').index('id'); const index = transaction.objectStore('statuses').index('id'); - return getFromDB(dispatch, getState, accountIndex, index, id); + return getFromDB(dispatch, getState, accountIndex, index, id).then(() => { + db.close(); + }, error => { + db.close(); + throw error; + }); }).then(() => { dispatch(fetchStatusSuccess(skipLoading)); }, () => api(getState).get(`/api/v1/statuses/${id}`).then(response => { diff --git a/app/javascript/mastodon/service_worker/entry.js b/app/javascript/mastodon/service_worker/entry.js index 160c3fbf2..ba54ae996 100644 --- a/app/javascript/mastodon/service_worker/entry.js +++ b/app/javascript/mastodon/service_worker/entry.js @@ -1,3 +1,4 @@ +import { freeStorage } from '../storage/modifier'; import './web_push_notifications'; function openSystemCache() { @@ -42,8 +43,10 @@ self.addEventListener('fetch', function(event) { event.respondWith(asyncResponse.then(async response => { if (response.ok || response.type === 'opaqueredirect') { - const cache = await asyncCache; - await cache.delete('/'); + await Promise.all([ + asyncCache.then(cache => cache.delete('/')), + indexedDB.deleteDatabase('mastodon'), + ]); } return response; @@ -56,7 +59,11 @@ self.addEventListener('fetch', function(event) { const fetched = await fetch(event.request); if (fetched.ok) { - await cache.put(event.request.url, fetched.clone()); + try { + await cache.put(event.request.url, fetched.clone()); + } finally { + freeStorage(); + } } return fetched; diff --git a/app/javascript/mastodon/storage/db.js b/app/javascript/mastodon/storage/db.js index e08fc3f3d..377a792a7 100644 --- a/app/javascript/mastodon/storage/db.js +++ b/app/javascript/mastodon/storage/db.js @@ -1,15 +1,14 @@ -import { me } from '../initial_state'; - -export default new Promise((resolve, reject) => { +export default () => new Promise((resolve, reject) => { + // ServiceWorker is required to synchronize the login state. // Microsoft Edge 17 does not support getAll according to: // Catalog of standard and vendor APIs across browsers - Microsoft Edge Development // https://developer.microsoft.com/en-us/microsoft-edge/platform/catalog/?q=specName%3Aindexeddb - if (!me || !('getAll' in IDBObjectStore.prototype)) { + if (!('caches' in self && 'getAll' in IDBObjectStore.prototype)) { reject(); return; } - const request = indexedDB.open('mastodon:' + me); + const request = indexedDB.open('mastodon'); request.onerror = reject; request.onsuccess = ({ target }) => resolve(target.result); diff --git a/app/javascript/mastodon/storage/modifier.js b/app/javascript/mastodon/storage/modifier.js index 4773d07a9..c2ed6f807 100644 --- a/app/javascript/mastodon/storage/modifier.js +++ b/app/javascript/mastodon/storage/modifier.js @@ -1,13 +1,14 @@ -import asyncDB from './db'; -import { autoPlayGif } from '../initial_state'; +import openDB from './db'; const accountAssetKeys = ['avatar', 'avatar_static', 'header', 'header_static']; -const avatarKey = autoPlayGif ? 'avatar' : 'avatar_static'; -const limit = 1024; +const storageMargin = 8388608; +const storeLimit = 1024; -// ServiceWorker and Cache API is not available on iOS 11 -// https://webkit.org/status/#specification-service-workers -const asyncCache = window.caches ? caches.open('mastodon-system') : Promise.reject(); +function openCache() { + // ServiceWorker and Cache API is not available on iOS 11 + // https://webkit.org/status/#specification-service-workers + return self.caches ? caches.open('mastodon-system') : Promise.reject(); +} function printErrorIfAvailable(error) { if (error) { @@ -16,7 +17,7 @@ function printErrorIfAvailable(error) { } function put(name, objects, onupdate, oncreate) { - return asyncDB.then(db => new Promise((resolve, reject) => { + return openDB().then(db => (new Promise((resolve, reject) => { const putTransaction = db.transaction(name, 'readwrite'); const putStore = putTransaction.objectStore(name); const putIndex = putStore.index('id'); @@ -53,7 +54,7 @@ function put(name, objects, onupdate, oncreate) { const count = readStore.count(); count.onsuccess = () => { - const excess = count.result - limit; + const excess = count.result - storeLimit; if (excess > 0) { const retrieval = readStore.getAll(null, excess); @@ -69,11 +70,17 @@ function put(name, objects, onupdate, oncreate) { }; putTransaction.onerror = reject; + })).then(resolved => { + db.close(); + return resolved; + }, error => { + db.close(); + throw error; })); } function evictAccountsByRecords(records) { - asyncDB.then(db => { + return openDB().then(db => { const transaction = db.transaction(['accounts', 'statuses'], 'readwrite'); const accounts = transaction.objectStore('accounts'); const accountsIdIndex = accounts.index('id'); @@ -83,7 +90,7 @@ function evictAccountsByRecords(records) { function evict(toEvict) { toEvict.forEach(record => { - asyncCache + openCache() .then(cache => accountAssetKeys.forEach(key => cache.delete(records[key]))) .catch(printErrorIfAvailable); @@ -98,6 +105,8 @@ function evictAccountsByRecords(records) { } evict(records); + + db.close(); }).catch(printErrorIfAvailable); } @@ -106,8 +115,9 @@ export function evictStatus(id) { } export function evictStatuses(ids) { - asyncDB.then(db => { - const store = db.transaction('statuses', 'readwrite').objectStore('statuses'); + return openDB().then(db => { + const transaction = db.transaction('statuses', 'readwrite'); + const store = transaction.objectStore('statuses'); const idIndex = store.index('id'); const reblogIndex = store.index('reblog'); @@ -118,14 +128,17 @@ export function evictStatuses(ids) { idIndex.getKey(id).onsuccess = ({ target }) => target.result && store.delete(target.result); }); + + db.close(); }).catch(printErrorIfAvailable); } function evictStatusesByRecords(records) { - evictStatuses(records.map(({ id }) => id)); + return evictStatuses(records.map(({ id }) => id)); } -export function putAccounts(records) { +export function putAccounts(records, avatarStatic) { + const avatarKey = avatarStatic ? 'avatar_static' : 'avatar'; const newURLs = []; put('accounts', records, (newRecord, oldKey, store, oncomplete) => { @@ -135,7 +148,7 @@ export function putAccounts(records) { const oldURL = target.result[key]; if (newURL !== oldURL) { - asyncCache + openCache() .then(cache => cache.delete(oldURL)) .catch(printErrorIfAvailable); } @@ -153,11 +166,12 @@ export function putAccounts(records) { }, (newRecord, oncomplete) => { newURLs.push(newRecord[avatarKey]); oncomplete(); - }).then(records => { - evictAccountsByRecords(records); - asyncCache - .then(cache => cache.addAll(newURLs)) - .catch(printErrorIfAvailable); + }).then(records => Promise.all([ + evictAccountsByRecords(records), + openCache().then(cache => cache.addAll(newURLs)), + ])).then(freeStorage, error => { + freeStorage(); + throw error; }).catch(printErrorIfAvailable); } @@ -166,3 +180,27 @@ export function putStatuses(records) { .then(evictStatusesByRecords) .catch(printErrorIfAvailable); } + +export function freeStorage() { + return navigator.storage.estimate().then(({ quota, usage }) => { + if (usage + storageMargin < quota) { + return null; + } + + return openDB().then(db => new Promise((resolve, reject) => { + const retrieval = db.transaction('accounts', 'readonly').objectStore('accounts').getAll(null, 1); + + retrieval.onsuccess = () => { + if (retrieval.result.length > 0) { + resolve(evictAccountsByRecords(retrieval.result).then(freeStorage)); + } else { + resolve(caches.delete('mastodon-system')); + } + }; + + retrieval.onerror = reject; + + db.close(); + })); + }); +} -- cgit From c9cbb8de703e321c0d152813a2e22471ffe5eef7 Mon Sep 17 00:00:00 2001 From: Emelia Smith Date: Sun, 8 Apr 2018 14:26:58 +0200 Subject: Add search item to tab bar for mobile devices (#7072) * Add search item to tab bar for mobile devices * Fix missing prop validation --- app/javascript/mastodon/features/compose/index.js | 5 +++-- app/javascript/mastodon/features/ui/components/tabs_bar.js | 1 + app/javascript/mastodon/features/ui/index.js | 2 ++ app/javascript/mastodon/locales/defaultMessages.json | 4 ++++ app/javascript/mastodon/locales/en.json | 1 + 5 files changed, 11 insertions(+), 2 deletions(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/features/compose/index.js b/app/javascript/mastodon/features/compose/index.js index d5cd854db..da5bf7cff 100644 --- a/app/javascript/mastodon/features/compose/index.js +++ b/app/javascript/mastodon/features/compose/index.js @@ -38,6 +38,7 @@ export default class Compose extends React.PureComponent { columns: ImmutablePropTypes.list.isRequired, multiColumn: PropTypes.bool, showSearch: PropTypes.bool, + isSearchPage: PropTypes.bool, intl: PropTypes.object.isRequired, }; @@ -58,7 +59,7 @@ export default class Compose extends React.PureComponent { } render () { - const { multiColumn, showSearch, intl } = this.props; + const { multiColumn, showSearch, isSearchPage, intl } = this.props; let header = ''; @@ -102,7 +103,7 @@ export default class Compose extends React.PureComponent { )} - + {({ x }) => (
diff --git a/app/javascript/mastodon/features/ui/components/tabs_bar.js b/app/javascript/mastodon/features/ui/components/tabs_bar.js index dba3be98b..ed6de6f39 100644 --- a/app/javascript/mastodon/features/ui/components/tabs_bar.js +++ b/app/javascript/mastodon/features/ui/components/tabs_bar.js @@ -8,6 +8,7 @@ import { isUserTouching } from '../../../is_mobile'; export const links = [ , , + , , , diff --git a/app/javascript/mastodon/features/ui/index.js b/app/javascript/mastodon/features/ui/index.js index 8894eb4e6..8b905fa1d 100644 --- a/app/javascript/mastodon/features/ui/index.js +++ b/app/javascript/mastodon/features/ui/index.js @@ -146,6 +146,8 @@ class SwitchingColumnsArea extends React.PureComponent { + + diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json index 5059fc67b..52b9db9ac 100644 --- a/app/javascript/mastodon/locales/defaultMessages.json +++ b/app/javascript/mastodon/locales/defaultMessages.json @@ -1717,6 +1717,10 @@ "defaultMessage": "Notifications", "id": "tabs_bar.notifications" }, + { + "defaultMessage": "Search", + "id": "tabs_bar.search" + }, { "defaultMessage": "Local", "id": "tabs_bar.local_timeline" diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 23040f7e4..2286d2e83 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -269,6 +269,7 @@ "tabs_bar.home": "Home", "tabs_bar.local_timeline": "Local", "tabs_bar.notifications": "Notifications", + "tabs_bar.search": "Search", "ui.beforeunload": "Your draft will be lost if you leave Mastodon.", "upload_area.title": "Drag & drop to upload", "upload_button.label": "Add media", -- cgit From 0893b1669548858daee79ab1260fb98646a0b3fa Mon Sep 17 00:00:00 2001 From: Emelia Smith Date: Sun, 8 Apr 2018 18:25:08 +0200 Subject: Hide search from Compose on mobile devices (#7077) * Hide search from Compose on mobile devices We're presently seeing large numbers of users accidentally tooting what they're trying to search for. This PR hides the search form from the Compose view, now that we have a dedicated "search" tab on mobile. * Don't "showSearch" on mobile if we're not currently searching (isSearchPage) --- app/javascript/mastodon/features/compose/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/features/compose/index.js b/app/javascript/mastodon/features/compose/index.js index da5bf7cff..67f0e7981 100644 --- a/app/javascript/mastodon/features/compose/index.js +++ b/app/javascript/mastodon/features/compose/index.js @@ -24,9 +24,9 @@ const messages = defineMessages({ logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' }, }); -const mapStateToProps = state => ({ +const mapStateToProps = (state, ownProps) => ({ columns: state.getIn(['settings', 'columns']), - showSearch: state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']), + showSearch: ownProps.multiColumn ? state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']) : ownProps.isSearchPage, }); @connect(mapStateToProps) @@ -90,7 +90,7 @@ export default class Compose extends React.PureComponent {
{header} - + {(multiColumn || isSearchPage) && }
-- cgit