diff --git a/app/soapbox/actions/settings.js b/app/soapbox/actions/settings.js
index 3062e669e..55ca6b0b5 100644
--- a/app/soapbox/actions/settings.js
+++ b/app/soapbox/actions/settings.js
@@ -38,6 +38,8 @@ export const defaultSettings = ImmutableMap({
dyslexicFont: false,
demetricator: false,
+ isDeveloper: false,
+
chats: ImmutableMap({
panes: ImmutableList(),
mainWindow: 'minimized',
diff --git a/app/soapbox/components/primary_navigation.js b/app/soapbox/components/primary_navigation.js
index 15bc811f6..9a11e5969 100644
--- a/app/soapbox/components/primary_navigation.js
+++ b/app/soapbox/components/primary_navigation.js
@@ -9,6 +9,7 @@ import { NavLink, withRouter } from 'react-router-dom';
import Icon from 'soapbox/components/icon';
import IconWithCounter from 'soapbox/components/icon_with_counter';
import classNames from 'classnames';
+import { getSettings } from 'soapbox/actions/settings';
import { getFeatures } from 'soapbox/utils/features';
import { getSoapboxConfig } from 'soapbox/actions/soapbox';
import { isStaff, getBaseURL } from 'soapbox/utils/accounts';
@@ -27,6 +28,7 @@ const mapStateToProps = state => {
chatsCount: state.get('chats').reduce((acc, curr) => acc + Math.min(curr.get('unread', 0), 1), 0),
dashboardCount: reportsCount + approvalCount,
baseURL: getBaseURL(account),
+ settings: getSettings(state),
features: getFeatures(instance),
instance,
};
@@ -47,13 +49,14 @@ class PrimaryNavigation extends React.PureComponent {
notificationCount: PropTypes.number,
chatsCount: PropTypes.number,
baseURL: PropTypes.string,
+ settings: PropTypes.object.isRequired,
features: PropTypes.object.isRequired,
location: PropTypes.object,
instance: ImmutablePropTypes.map.isRequired,
};
render() {
- const { account, features, notificationCount, chatsCount, dashboardCount, location, instance, baseURL } = this.props;
+ const { account, settings, features, notificationCount, chatsCount, dashboardCount, location, instance, baseURL } = this.props;
return (
@@ -127,6 +130,16 @@ class PrimaryNavigation extends React.PureComponent {
)}
+ {(settings.get('isDeveloper')) && (
+
+
+
+
+ )}
+
{features.federating ? (
diff --git a/app/soapbox/components/sidebar_menu.js b/app/soapbox/components/sidebar_menu.js
index bfd5b9b99..4ee6fc9cd 100644
--- a/app/soapbox/components/sidebar_menu.js
+++ b/app/soapbox/components/sidebar_menu.js
@@ -18,6 +18,7 @@ import { logOut, switchAccount } from 'soapbox/actions/auth';
import ThemeToggle from '../features/ui/components/theme_toggle_container';
import { fetchOwnAccounts } from 'soapbox/actions/auth';
import { is as ImmutableIs } from 'immutable';
+import { getSettings } from 'soapbox/actions/settings';
import { getSoapboxConfig } from 'soapbox/actions/soapbox';
import { getFeatures } from 'soapbox/utils/features';
@@ -45,6 +46,7 @@ const messages = defineMessages({
donate: { id: 'donate', defaultMessage: 'Donate' },
donate_crypto: { id: 'donate_crypto', defaultMessage: 'Donate cryptocurrency' },
info: { id: 'column.info', defaultMessage: 'Server information' },
+ developers: { id: 'navigation.developers', defaultMessage: 'Developers' },
add_account: { id: 'profile_dropdown.add_account', defaultMessage: 'Add an existing account' },
});
@@ -67,6 +69,7 @@ const makeMapStateToProps = () => {
hasCrypto: typeof soapbox.getIn(['cryptoAddresses', 0, 'ticker']) === 'string',
otherAccounts: getOtherAccounts(state),
features,
+ settings: getSettings(state),
siteTitle: instance.get('title'),
baseURL: getBaseURL(account),
};
@@ -101,6 +104,7 @@ class SidebarMenu extends ImmutablePureComponent {
otherAccounts: ImmutablePropTypes.list,
sidebarOpen: PropTypes.bool,
onClose: PropTypes.func.isRequired,
+ settings: PropTypes.object.isRequired,
features: PropTypes.object.isRequired,
baseURL: PropTypes.string,
};
@@ -159,7 +163,7 @@ class SidebarMenu extends ImmutablePureComponent {
}
render() {
- const { sidebarOpen, intl, account, onClickLogOut, donateUrl, otherAccounts, hasCrypto, features, siteTitle, baseURL } = this.props;
+ const { sidebarOpen, intl, account, onClickLogOut, donateUrl, otherAccounts, hasCrypto, settings, features, siteTitle, baseURL } = this.props;
const { switcher } = this.state;
if (!account) return null;
const acct = account.get('acct');
@@ -319,6 +323,13 @@ class SidebarMenu extends ImmutablePureComponent {
+ {(settings.get('isDeveloper')) && (
+
+
+ {intl.formatMessage(messages.developers)}
+
+ )}
+
diff --git a/app/soapbox/features/developers/index.js b/app/soapbox/features/developers/index.js
new file mode 100644
index 000000000..cc74e5c14
--- /dev/null
+++ b/app/soapbox/features/developers/index.js
@@ -0,0 +1,29 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { injectIntl, defineMessages } from 'react-intl';
+import Column from '../ui/components/column';
+
+const messages = defineMessages({
+ heading: { id: 'column.developers', defaultMessage: 'Developers' },
+});
+
+export default @injectIntl
+class Developers extends React.Component {
+
+ static propTypes = {
+ intl: PropTypes.object.isRequired,
+ }
+
+ render() {
+ const { intl } = this.props;
+
+ return (
+
+
+ WIP: Developers page
+
+
+ );
+ }
+
+}
diff --git a/app/soapbox/features/preferences/index.js b/app/soapbox/features/preferences/index.js
index e95a79eda..14e41fbfe 100644
--- a/app/soapbox/features/preferences/index.js
+++ b/app/soapbox/features/preferences/index.js
@@ -262,6 +262,10 @@ class Preferences extends ImmutablePureComponent {
hint={
}
path={['demetricator']}
/>
+
}
+ path={['isDeveloper']}
+ />
diff --git a/app/soapbox/features/ui/index.js b/app/soapbox/features/ui/index.js
index a16b12bf3..924c5a757 100644
--- a/app/soapbox/features/ui/index.js
+++ b/app/soapbox/features/ui/index.js
@@ -115,6 +115,7 @@ import {
Share,
NewStatus,
IntentionalError,
+ Developers,
} from './util/async-components';
// Dummy import, to make sure that
ends up in the application bundle.
@@ -318,7 +319,9 @@ class SwitchingColumnsArea extends React.PureComponent {
+
+
diff --git a/app/soapbox/features/ui/util/async-components.js b/app/soapbox/features/ui/util/async-components.js
index 2223eeb92..9bd0418b6 100644
--- a/app/soapbox/features/ui/util/async-components.js
+++ b/app/soapbox/features/ui/util/async-components.js
@@ -425,3 +425,7 @@ export function NewStatus() {
export function IntentionalError() {
return import(/* webpackChunkName: "error" */'../../intentional_error');
}
+
+export function Developers() {
+ return import(/* webpackChunkName: "features/developers" */'../../developers');
+}
diff --git a/app/styles/components/icon.scss b/app/styles/components/icon.scss
index 3514ab03a..b49c5412e 100644
--- a/app/styles/components/icon.scss
+++ b/app/styles/components/icon.scss
@@ -18,7 +18,8 @@
fill: currentColor;
}
- svg.icon-tabler-search {
+ svg.icon-tabler-search,
+ svg.icon-tabler-code {
stroke-width: 2.3px;
}
diff --git a/app/styles/navigation.scss b/app/styles/navigation.scss
index c14a3cf33..0dcb07a4c 100644
--- a/app/styles/navigation.scss
+++ b/app/styles/navigation.scss
@@ -18,7 +18,8 @@
}
&--active {
- svg.icon-tabler-search {
+ svg.icon-tabler-search,
+ svg.icon-tabler-code {
stroke-width: 2.5px;
}
}