Merge remote-tracking branch 'soapbox/next' into next_
This commit is contained in:
commit
5a81c4672a
|
@ -0,0 +1,108 @@
|
||||||
|
{
|
||||||
|
"account": {
|
||||||
|
"acct": "alex",
|
||||||
|
"avatar": "https://freespeechextremist.com/images/avi.png",
|
||||||
|
"avatar_static": "https://freespeechextremist.com/images/avi.png",
|
||||||
|
"bot": false,
|
||||||
|
"created_at": "2022-02-28T01:55:05.000Z",
|
||||||
|
"display_name": "Alex Gleason",
|
||||||
|
"emojis": [],
|
||||||
|
"fields": [],
|
||||||
|
"followers_count": 1,
|
||||||
|
"following_count": 0,
|
||||||
|
"header": "https://freespeechextremist.com/images/banner.png",
|
||||||
|
"header_static": "https://freespeechextremist.com/images/banner.png",
|
||||||
|
"id": "AGv8wCadU7DqWgMqNk",
|
||||||
|
"locked": false,
|
||||||
|
"note": "I'm testing out compatibility with an older Pleroma version",
|
||||||
|
"pleroma": {
|
||||||
|
"accepts_chat_messages": true,
|
||||||
|
"ap_id": "https://freespeechextremist.com/users/alex",
|
||||||
|
"background_image": null,
|
||||||
|
"confirmation_pending": false,
|
||||||
|
"favicon": null,
|
||||||
|
"hide_favorites": true,
|
||||||
|
"hide_followers": false,
|
||||||
|
"hide_followers_count": false,
|
||||||
|
"hide_follows": false,
|
||||||
|
"hide_follows_count": false,
|
||||||
|
"is_admin": false,
|
||||||
|
"is_moderator": false,
|
||||||
|
"relationship": {},
|
||||||
|
"skip_thread_containment": false,
|
||||||
|
"tags": []
|
||||||
|
},
|
||||||
|
"source": {
|
||||||
|
"fields": [],
|
||||||
|
"note": "I'm testing out compatibility with an older Pleroma version",
|
||||||
|
"pleroma": {
|
||||||
|
"actor_type": "Person",
|
||||||
|
"discoverable": true
|
||||||
|
},
|
||||||
|
"sensitive": false
|
||||||
|
},
|
||||||
|
"statuses_count": 1,
|
||||||
|
"url": "https://freespeechextremist.com/users/alex",
|
||||||
|
"username": "alex"
|
||||||
|
},
|
||||||
|
"application": {
|
||||||
|
"name": "Web",
|
||||||
|
"website": null
|
||||||
|
},
|
||||||
|
"bookmarked": false,
|
||||||
|
"card": null,
|
||||||
|
"content": "<br/><a href=\"https://freespeechextremist.com/media/3e34b808-1c84-4ef3-ba56-67cc86b7911a/0f66e92f339705ccc03079b8f647048e15730adf2cc9eaa1071c7c7cf6884b1b.webm?name=0f66e92f339705ccc03079b8f647048e15730adf2cc9eaa1071c7c7cf6884b1b.webm\">0f66e92f339705ccc03079b8f647048e15730adf2cc9eaa1071c7c7cf6884b1b.webm</a>",
|
||||||
|
"created_at": "2022-04-14T19:42:48.000Z",
|
||||||
|
"emojis": [],
|
||||||
|
"favourited": false,
|
||||||
|
"favourites_count": 0,
|
||||||
|
"id": "AIRxLeIzncpCtsr2hs",
|
||||||
|
"in_reply_to_account_id": null,
|
||||||
|
"in_reply_to_id": null,
|
||||||
|
"language": null,
|
||||||
|
"media_attachments": [
|
||||||
|
{
|
||||||
|
"description": "0f66e92f339705ccc03079b8f647048e15730adf2cc9eaa1071c7c7cf6884b1b.webm",
|
||||||
|
"id": "1142674091",
|
||||||
|
"pleroma": {
|
||||||
|
"mime_type": "video/webm"
|
||||||
|
},
|
||||||
|
"preview_url": "https://freespeechextremist.com/media/3e34b808-1c84-4ef3-ba56-67cc86b7911a/0f66e92f339705ccc03079b8f647048e15730adf2cc9eaa1071c7c7cf6884b1b.webm?name=0f66e92f339705ccc03079b8f647048e15730adf2cc9eaa1071c7c7cf6884b1b.webm",
|
||||||
|
"remote_url": "https://freespeechextremist.com/media/3e34b808-1c84-4ef3-ba56-67cc86b7911a/0f66e92f339705ccc03079b8f647048e15730adf2cc9eaa1071c7c7cf6884b1b.webm?name=0f66e92f339705ccc03079b8f647048e15730adf2cc9eaa1071c7c7cf6884b1b.webm",
|
||||||
|
"text_url": "https://freespeechextremist.com/media/3e34b808-1c84-4ef3-ba56-67cc86b7911a/0f66e92f339705ccc03079b8f647048e15730adf2cc9eaa1071c7c7cf6884b1b.webm?name=0f66e92f339705ccc03079b8f647048e15730adf2cc9eaa1071c7c7cf6884b1b.webm",
|
||||||
|
"type": "video",
|
||||||
|
"url": "https://freespeechextremist.com/media/3e34b808-1c84-4ef3-ba56-67cc86b7911a/0f66e92f339705ccc03079b8f647048e15730adf2cc9eaa1071c7c7cf6884b1b.webm?name=0f66e92f339705ccc03079b8f647048e15730adf2cc9eaa1071c7c7cf6884b1b.webm"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"mentions": [],
|
||||||
|
"muted": false,
|
||||||
|
"pinned": false,
|
||||||
|
"pleroma": {
|
||||||
|
"content": {
|
||||||
|
"text/plain": "0f66e92f339705ccc03079b8f647048e15730adf2cc9eaa1071c7c7cf6884b1b.webm"
|
||||||
|
},
|
||||||
|
"conversation_id": 97191096,
|
||||||
|
"direct_conversation_id": null,
|
||||||
|
"emoji_reactions": [],
|
||||||
|
"expires_at": null,
|
||||||
|
"in_reply_to_account_acct": null,
|
||||||
|
"local": true,
|
||||||
|
"parent_visible": false,
|
||||||
|
"spoiler_text": {
|
||||||
|
"text/plain": ""
|
||||||
|
},
|
||||||
|
"thread_muted": false
|
||||||
|
},
|
||||||
|
"poll": null,
|
||||||
|
"reblog": null,
|
||||||
|
"reblogged": false,
|
||||||
|
"reblogs_count": 0,
|
||||||
|
"replies_count": 0,
|
||||||
|
"sensitive": false,
|
||||||
|
"spoiler_text": "",
|
||||||
|
"tags": [],
|
||||||
|
"text": null,
|
||||||
|
"uri": "https://freespeechextremist.com/objects/419b2cad-656a-4dbc-b2b5-94bb75e0afc8",
|
||||||
|
"url": "https://freespeechextremist.com/notice/AIRxLeIzncpCtsr2hs",
|
||||||
|
"visibility": "public"
|
||||||
|
}
|
|
@ -18,7 +18,7 @@ let id = 0;
|
||||||
export interface MenuItem {
|
export interface MenuItem {
|
||||||
action?: React.EventHandler<React.KeyboardEvent | React.MouseEvent>,
|
action?: React.EventHandler<React.KeyboardEvent | React.MouseEvent>,
|
||||||
middleClick?: React.EventHandler<React.MouseEvent>,
|
middleClick?: React.EventHandler<React.MouseEvent>,
|
||||||
text: string,
|
text: string | JSX.Element,
|
||||||
href?: string,
|
href?: string,
|
||||||
to?: string,
|
to?: string,
|
||||||
newTab?: boolean,
|
newTab?: boolean,
|
||||||
|
|
|
@ -8,17 +8,29 @@ interface ISidebarNavigationLink {
|
||||||
count?: number,
|
count?: number,
|
||||||
icon: string,
|
icon: string,
|
||||||
text: string | React.ReactElement,
|
text: string | React.ReactElement,
|
||||||
to: string,
|
to?: string,
|
||||||
|
onClick?: React.EventHandler<React.MouseEvent>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const SidebarNavigationLink = ({ icon, text, to, count }: ISidebarNavigationLink) => {
|
const SidebarNavigationLink = React.forwardRef((props: ISidebarNavigationLink, ref: React.ForwardedRef<HTMLAnchorElement>): JSX.Element => {
|
||||||
|
const { icon, text, to = '', count, onClick } = props;
|
||||||
const isActive = location.pathname === to;
|
const isActive = location.pathname === to;
|
||||||
const withCounter = typeof count !== 'undefined';
|
const withCounter = typeof count !== 'undefined';
|
||||||
|
|
||||||
|
const handleClick: React.EventHandler<React.MouseEvent> = (e) => {
|
||||||
|
if (onClick) {
|
||||||
|
onClick(e);
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NavLink
|
<NavLink
|
||||||
exact
|
exact
|
||||||
to={to}
|
to={to}
|
||||||
|
ref={ref}
|
||||||
|
onClick={handleClick}
|
||||||
className={classNames({
|
className={classNames({
|
||||||
'flex items-center py-2 text-sm font-semibold space-x-4': true,
|
'flex items-center py-2 text-sm font-semibold space-x-4': true,
|
||||||
'text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200': !isActive,
|
'text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200': !isActive,
|
||||||
|
@ -50,6 +62,6 @@ const SidebarNavigationLink = ({ icon, text, to, count }: ISidebarNavigationLink
|
||||||
<Text weight='semibold' theme='inherit'>{text}</Text>
|
<Text weight='semibold' theme='inherit'>{text}</Text>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
export default SidebarNavigationLink;
|
export default SidebarNavigationLink;
|
||||||
|
|
|
@ -1,27 +1,110 @@
|
||||||
import { Map as ImmutableMap } from 'immutable';
|
import { Map as ImmutableMap, OrderedSet as ImmutableOrderedSet } from 'immutable';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { getSettings } from 'soapbox/actions/settings';
|
import { getSettings } from 'soapbox/actions/settings';
|
||||||
|
import DropdownMenu from 'soapbox/containers/dropdown_menu_container';
|
||||||
import ComposeButton from 'soapbox/features/ui/components/compose-button';
|
import ComposeButton from 'soapbox/features/ui/components/compose-button';
|
||||||
import { useAppSelector } from 'soapbox/hooks';
|
import { useAppSelector, useOwnAccount } from 'soapbox/hooks';
|
||||||
import { getBaseURL } from 'soapbox/utils/accounts';
|
import { getBaseURL } from 'soapbox/utils/accounts';
|
||||||
import { getFeatures } from 'soapbox/utils/features';
|
import { getFeatures } from 'soapbox/utils/features';
|
||||||
|
|
||||||
import SidebarNavigationLink from './sidebar-navigation-link';
|
import SidebarNavigationLink from './sidebar-navigation-link';
|
||||||
|
|
||||||
|
import type { Menu } from 'soapbox/components/dropdown_menu';
|
||||||
|
|
||||||
const SidebarNavigation = () => {
|
const SidebarNavigation = () => {
|
||||||
const me = useAppSelector((state) => state.me);
|
|
||||||
const instance = useAppSelector((state) => state.instance);
|
const instance = useAppSelector((state) => state.instance);
|
||||||
const settings = useAppSelector((state) => getSettings(state));
|
const settings = useAppSelector((state) => getSettings(state));
|
||||||
const account = useAppSelector((state) => state.accounts.get(me));
|
const account = useOwnAccount();
|
||||||
const notificationCount = useAppSelector((state) => state.notifications.get('unread'));
|
const notificationCount = useAppSelector((state) => state.notifications.get('unread'));
|
||||||
const chatsCount = useAppSelector((state) => state.chats.get('items').reduce((acc: any, curr: any) => acc + Math.min(curr.get('unread', 0), 1), 0));
|
const chatsCount = useAppSelector((state) => state.chats.get('items').reduce((acc: any, curr: any) => acc + Math.min(curr.get('unread', 0), 1), 0));
|
||||||
const dashboardCount = useAppSelector((state) => state.admin.openReports.count() + state.admin.awaitingApproval.count());
|
const followRequestsCount = useAppSelector((state) => state.user_lists.getIn(['follow_requests', 'items'], ImmutableOrderedSet()).count());
|
||||||
|
// const dashboardCount = useAppSelector((state) => state.admin.openReports.count() + state.admin.awaitingApproval.count());
|
||||||
|
|
||||||
const baseURL = getBaseURL(ImmutableMap(account));
|
const baseURL = account ? getBaseURL(ImmutableMap(account)) : '';
|
||||||
const features = getFeatures(instance);
|
const features = getFeatures(instance);
|
||||||
|
|
||||||
|
const makeMenu = (): Menu => {
|
||||||
|
const menu: Menu = [];
|
||||||
|
|
||||||
|
if (account?.locked || followRequestsCount > 0) {
|
||||||
|
menu.push({
|
||||||
|
to: '/follow_requests',
|
||||||
|
text: <FormattedMessage id='navigation_bar.follow_requests' defaultMessage='Follow requests' />,
|
||||||
|
icon: require('@tabler/icons/icons/user-plus.svg'),
|
||||||
|
// TODO: let menu items have a counter
|
||||||
|
// count: followRequestsCount,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (features.bookmarks) {
|
||||||
|
menu.push({
|
||||||
|
to: '/bookmarks',
|
||||||
|
text: <FormattedMessage id='column.bookmarks' defaultMessage='Bookmarks' />,
|
||||||
|
icon: require('@tabler/icons/icons/bookmark.svg'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (features.lists) {
|
||||||
|
menu.push({
|
||||||
|
to: '/lists',
|
||||||
|
text: <FormattedMessage id='column.lists' defaultMessage='Lists' />,
|
||||||
|
icon: require('@tabler/icons/icons/list.svg'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (account && instance.invites_enabled) {
|
||||||
|
menu.push({
|
||||||
|
to: `${baseURL}/invites`,
|
||||||
|
icon: require('@tabler/icons/icons/mailbox.svg'),
|
||||||
|
text: <FormattedMessage id='navigation.invites' defaultMessage='Invites' />,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.get('isDeveloper')) {
|
||||||
|
menu.push({
|
||||||
|
to: '/developers',
|
||||||
|
icon: require('@tabler/icons/icons/code.svg'),
|
||||||
|
text: <FormattedMessage id='navigation.developers' defaultMessage='Developers' />,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (account && account.staff) {
|
||||||
|
menu.push({
|
||||||
|
to: '/admin',
|
||||||
|
icon: require('@tabler/icons/icons/dashboard.svg'),
|
||||||
|
text: <FormattedMessage id='tabs_bar.dashboard' defaultMessage='Dashboard' />,
|
||||||
|
// TODO: let menu items have a counter
|
||||||
|
// count: dashboardCount,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (features.localTimeline || features.publicTimeline) {
|
||||||
|
menu.push(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (features.localTimeline) {
|
||||||
|
menu.push({
|
||||||
|
to: '/timeline/local',
|
||||||
|
icon: features.federating ? require('@tabler/icons/icons/users.svg') : require('@tabler/icons/icons/world.svg'),
|
||||||
|
text: features.federating ? instance.title : <FormattedMessage id='tabs_bar.all' defaultMessage='All' />,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (features.localTimeline && features.federating) {
|
||||||
|
menu.push({
|
||||||
|
to: '/timeline/fediverse',
|
||||||
|
icon: require('icons/fediverse.svg'),
|
||||||
|
text: <FormattedMessage id='tabs_bar.fediverse' defaultMessage='Fediverse' />,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return menu;
|
||||||
|
};
|
||||||
|
|
||||||
|
const menu = makeMenu();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className='flex flex-col space-y-2'>
|
<div className='flex flex-col space-y-2'>
|
||||||
|
@ -71,49 +154,13 @@ const SidebarNavigation = () => {
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{(account && account.staff) && (
|
{menu.length > 0 && (
|
||||||
|
<DropdownMenu items={menu}>
|
||||||
<SidebarNavigationLink
|
<SidebarNavigationLink
|
||||||
to='/admin'
|
icon={require('@tabler/icons/icons/dots-circle-horizontal.svg')}
|
||||||
icon={require('@tabler/icons/icons/dashboard.svg')}
|
text={<FormattedMessage id='tabs_bar.more' defaultMessage='More' />}
|
||||||
text={<FormattedMessage id='tabs_bar.dashboard' defaultMessage='Dashboard' />}
|
|
||||||
count={dashboardCount}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{(account && instance.invites_enabled) && (
|
|
||||||
<SidebarNavigationLink
|
|
||||||
to={`${baseURL}/invites`}
|
|
||||||
icon={require('@tabler/icons/icons/mailbox.svg')}
|
|
||||||
text={<FormattedMessage id='navigation.invites' defaultMessage='Invites' />}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{(settings.get('isDeveloper')) && (
|
|
||||||
<SidebarNavigationLink
|
|
||||||
to='/developers'
|
|
||||||
icon={require('@tabler/icons/icons/code.svg')}
|
|
||||||
text={<FormattedMessage id='navigation.developers' defaultMessage='Developers' />}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{(features.localTimeline || features.publicTimeline) && (
|
|
||||||
<hr className='dark:border-slate-700' />
|
|
||||||
)}
|
|
||||||
|
|
||||||
{features.localTimeline && (
|
|
||||||
<SidebarNavigationLink
|
|
||||||
to='/timeline/local'
|
|
||||||
icon={features.federating ? require('@tabler/icons/icons/users.svg') : require('@tabler/icons/icons/world.svg')}
|
|
||||||
text={features.federating ? instance.title : <FormattedMessage id='tabs_bar.all' defaultMessage='All' />}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{(features.publicTimeline && features.federating) && (
|
|
||||||
<SidebarNavigationLink
|
|
||||||
to='/timeline/fediverse'
|
|
||||||
icon={require('icons/fediverse.svg')}
|
|
||||||
text={<FormattedMessage id='tabs_bar.fediverse' defaultMessage='Fediverse' />}
|
|
||||||
/>
|
/>
|
||||||
|
</DropdownMenu>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -86,6 +86,7 @@ const mapStateToProps = (state) => {
|
||||||
locale: validLocale(locale) ? locale : 'en',
|
locale: validLocale(locale) ? locale : 'en',
|
||||||
themeCss: generateThemeCss(soapboxConfig),
|
themeCss: generateThemeCss(soapboxConfig),
|
||||||
brandColor: soapboxConfig.get('brandColor'),
|
brandColor: soapboxConfig.get('brandColor'),
|
||||||
|
appleAppId: soapboxConfig.get('appleAppId'),
|
||||||
themeMode: settings.get('themeMode'),
|
themeMode: settings.get('themeMode'),
|
||||||
singleUserMode,
|
singleUserMode,
|
||||||
};
|
};
|
||||||
|
@ -108,6 +109,7 @@ class SoapboxMount extends React.PureComponent {
|
||||||
themeCss: PropTypes.string,
|
themeCss: PropTypes.string,
|
||||||
themeMode: PropTypes.string,
|
themeMode: PropTypes.string,
|
||||||
brandColor: PropTypes.string,
|
brandColor: PropTypes.string,
|
||||||
|
appleAppId: PropTypes.string,
|
||||||
dispatch: PropTypes.func,
|
dispatch: PropTypes.func,
|
||||||
singleUserMode: PropTypes.bool,
|
singleUserMode: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
@ -171,6 +173,10 @@ class SoapboxMount extends React.PureComponent {
|
||||||
<body className={bodyClass} />
|
<body className={bodyClass} />
|
||||||
{themeCss && <style id='theme' type='text/css'>{`:root{${themeCss}}`}</style>}
|
{themeCss && <style id='theme' type='text/css'>{`:root{${themeCss}}`}</style>}
|
||||||
<meta name='theme-color' content={this.props.brandColor} />
|
<meta name='theme-color' content={this.props.brandColor} />
|
||||||
|
|
||||||
|
{this.props.appleAppId && (
|
||||||
|
<meta name='apple-itunes-app' content={`app-id=${this.props.appleAppId}`} />
|
||||||
|
)}
|
||||||
</Helmet>
|
</Helmet>
|
||||||
|
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
|
|
|
@ -47,6 +47,14 @@ const Developers = () => {
|
||||||
</Text>
|
</Text>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
<Link to='/developers/timeline' className='bg-gray-200 dark:bg-gray-600 p-4 rounded flex flex-col items-center justify-center space-y-2 hover:-translate-y-1 transition-transform'>
|
||||||
|
<SvgIcon src={require('icons/feed.svg')} className='dark:text-gray-100' />
|
||||||
|
|
||||||
|
<Text>
|
||||||
|
<FormattedMessage id='developers.navigation.test_timeline_label' defaultMessage='Test timeline' />
|
||||||
|
</Text>
|
||||||
|
</Link>
|
||||||
|
|
||||||
<Link to='/error' className='bg-gray-200 dark:bg-gray-600 p-4 rounded flex flex-col items-center justify-center space-y-2 hover:-translate-y-1 transition-transform'>
|
<Link to='/error' className='bg-gray-200 dark:bg-gray-600 p-4 rounded flex flex-col items-center justify-center space-y-2 hover:-translate-y-1 transition-transform'>
|
||||||
<SvgIcon src={require('@tabler/icons/icons/mood-sad.svg')} className='dark:text-gray-100' />
|
<SvgIcon src={require('@tabler/icons/icons/mood-sad.svg')} className='dark:text-gray-100' />
|
||||||
|
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
import * as React from 'react';
|
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
|
|
||||||
import { IconButton } from 'soapbox/components/ui';
|
|
||||||
import { useAppSelector } from 'soapbox/hooks';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
close: { id: 'pre_header.close', defaultMessage: 'Close' },
|
|
||||||
});
|
|
||||||
|
|
||||||
export default () => {
|
|
||||||
const intl = useIntl();
|
|
||||||
|
|
||||||
const [hidden, setHidden] = React.useState(false);
|
|
||||||
const siteTitle = useAppSelector((state) => state.instance.title);
|
|
||||||
|
|
||||||
const handleClose = () => {
|
|
||||||
localStorage.setItem('soapbox:welcome-banner', '0');
|
|
||||||
setHidden(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
const shouldBeHidden = localStorage.getItem('soapbox:welcome-banner') === '0';
|
|
||||||
|
|
||||||
setHidden(shouldBeHidden);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
if (hidden) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='bg-primary-900 z-10'>
|
|
||||||
<div className='max-w-7xl flex justify-between mx-auto px-2 sm:px-6 lg:px-8'>
|
|
||||||
<div className='h-14 flex items-center space-x-3'>
|
|
||||||
<p className='text-white font-semibold'>
|
|
||||||
<span>Welcome to {siteTitle}</span>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<Link className='text-sea-blue text-sm lowercase hover:underline' to='/beta'>
|
|
||||||
Learn More
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='flex items-center'>
|
|
||||||
<IconButton
|
|
||||||
transparent
|
|
||||||
src={require('@tabler/icons/icons/x.svg')}
|
|
||||||
onClick={handleClose}
|
|
||||||
title={intl.formatMessage(messages.close)}
|
|
||||||
className='bg-transparent text-white text-opacity-75'
|
|
||||||
iconClassName='w-5 h-5'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
|
||||||
|
import { useDispatch } from 'react-redux';
|
||||||
|
|
||||||
|
import { importFetchedStatuses } from 'soapbox/actions/importer';
|
||||||
|
import { expandTimelineSuccess } from 'soapbox/actions/timelines';
|
||||||
|
import SubNavigation from 'soapbox/components/sub_navigation';
|
||||||
|
|
||||||
|
import { Column } from '../../components/ui';
|
||||||
|
import StatusListContainer from '../ui/containers/status_list_container';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
title: { id: 'column.test', defaultMessage: 'Test timeline' },
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of mock statuses to display in the timeline.
|
||||||
|
* These get embedded into the build, but only in this chunk, so it's okay.
|
||||||
|
*/
|
||||||
|
const MOCK_STATUSES: any[] = [
|
||||||
|
require('soapbox/__fixtures__/pleroma-status-with-poll.json'),
|
||||||
|
require('soapbox/__fixtures__/pleroma-status-vertical-video-without-metadata.json'),
|
||||||
|
require('soapbox/__fixtures__/pleroma-status-with-poll-with-emojis.json'),
|
||||||
|
require('soapbox/__fixtures__/pleroma-quote-of-quote-post.json'),
|
||||||
|
require('soapbox/__fixtures__/truthsocial-status-with-external-video.json'),
|
||||||
|
];
|
||||||
|
|
||||||
|
const timelineId = 'test';
|
||||||
|
const onlyMedia = false;
|
||||||
|
|
||||||
|
const TestTimeline: React.FC = () => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
dispatch(importFetchedStatuses(MOCK_STATUSES));
|
||||||
|
dispatch(expandTimelineSuccess(timelineId, MOCK_STATUSES, null, false, false, false));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Column label={intl.formatMessage(messages.title)} transparent>
|
||||||
|
<SubNavigation message={intl.formatMessage(messages.title)} />
|
||||||
|
<StatusListContainer
|
||||||
|
scrollKey={`${timelineId}_timeline`}
|
||||||
|
timelineId={`${timelineId}${onlyMedia ? ':media' : ''}`}
|
||||||
|
emptyMessage={<FormattedMessage id='empty_column.test' defaultMessage='The test timeline is empty.' />}
|
||||||
|
divideType='space'
|
||||||
|
/>
|
||||||
|
</Column>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TestTimeline;
|
|
@ -1,74 +0,0 @@
|
||||||
import { OrderedSet as ImmutableOrderedSet } from 'immutable';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import { injectIntl, defineMessages } from 'react-intl';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { NavLink } from 'react-router-dom';
|
|
||||||
|
|
||||||
import Icon from 'soapbox/components/icon';
|
|
||||||
import IconWithCounter from 'soapbox/components/icon_with_counter';
|
|
||||||
import { getFeatures } from 'soapbox/utils/features';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit Profile' },
|
|
||||||
preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
|
|
||||||
security: { id: 'navigation_bar.security', defaultMessage: 'Security' },
|
|
||||||
lists: { id: 'column.lists', defaultMessage: 'Lists' },
|
|
||||||
bookmarks: { id: 'column.bookmarks', defaultMessage: 'Bookmarks' },
|
|
||||||
follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
|
|
||||||
});
|
|
||||||
|
|
||||||
const mapStateToProps = state => {
|
|
||||||
const me = state.get('me');
|
|
||||||
|
|
||||||
const instance = state.get('instance');
|
|
||||||
const features = getFeatures(instance);
|
|
||||||
|
|
||||||
return {
|
|
||||||
isLocked: state.getIn(['accounts', me, 'locked']),
|
|
||||||
followRequestsCount: state.getIn(['user_lists', 'follow_requests', 'items'], ImmutableOrderedSet()).count(),
|
|
||||||
features,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default @connect(mapStateToProps)
|
|
||||||
@injectIntl
|
|
||||||
class FeaturesPanel extends React.PureComponent {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
intl: PropTypes.object.isRequired,
|
|
||||||
isLocked: PropTypes.bool,
|
|
||||||
followRequestsCount: PropTypes.number,
|
|
||||||
features: PropTypes.object.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { intl, isLocked, followRequestsCount, features } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='wtf-panel promo-panel panel'>
|
|
||||||
<div className='promo-panel__container'>
|
|
||||||
{(isLocked || followRequestsCount > 0) && <NavLink className='promo-panel-item' to='/follow_requests'>
|
|
||||||
<IconWithCounter src={require('@tabler/icons/icons/user-plus.svg')} count={followRequestsCount} className='promo-panel-item__icon' />
|
|
||||||
{intl.formatMessage(messages.follow_requests)}
|
|
||||||
</NavLink>}
|
|
||||||
|
|
||||||
{features.bookmarks && (
|
|
||||||
<NavLink className='promo-panel-item' to='/bookmarks'>
|
|
||||||
<Icon src={require('@tabler/icons/icons/bookmark.svg')} className='promo-panel-item__icon' />
|
|
||||||
{intl.formatMessage(messages.bookmarks)}
|
|
||||||
</NavLink>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{features.lists && (
|
|
||||||
<NavLink className='promo-panel-item' to='/lists'>
|
|
||||||
<Icon src={require('@tabler/icons/icons/list.svg')} className='promo-panel-item__icon' />
|
|
||||||
{intl.formatMessage(messages.lists)}
|
|
||||||
</NavLink>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -41,7 +41,6 @@ import { expandNotifications } from '../../actions/notifications';
|
||||||
import { fetchScheduledStatuses } from '../../actions/scheduled_statuses';
|
import { fetchScheduledStatuses } from '../../actions/scheduled_statuses';
|
||||||
import { connectUserStream } from '../../actions/streaming';
|
import { connectUserStream } from '../../actions/streaming';
|
||||||
import { expandHomeTimeline } from '../../actions/timelines';
|
import { expandHomeTimeline } from '../../actions/timelines';
|
||||||
// import PreHeader from '../../features/public_layout/components/pre_header';
|
|
||||||
// import GroupSidebarPanel from '../groups/sidebar_panel';
|
// import GroupSidebarPanel from '../groups/sidebar_panel';
|
||||||
|
|
||||||
import BackgroundShapes from './components/background_shapes';
|
import BackgroundShapes from './components/background_shapes';
|
||||||
|
@ -120,6 +119,7 @@ import {
|
||||||
Developers,
|
Developers,
|
||||||
CreateApp,
|
CreateApp,
|
||||||
SettingsStore,
|
SettingsStore,
|
||||||
|
TestTimeline,
|
||||||
} from './util/async-components';
|
} from './util/async-components';
|
||||||
import { WrappedRoute } from './util/react_router_helpers';
|
import { WrappedRoute } from './util/react_router_helpers';
|
||||||
|
|
||||||
|
@ -341,6 +341,7 @@ class SwitchingColumnsArea extends React.PureComponent {
|
||||||
|
|
||||||
<WrappedRoute path='/developers/apps/create' developerOnly page={DefaultPage} component={CreateApp} content={children} />
|
<WrappedRoute path='/developers/apps/create' developerOnly page={DefaultPage} component={CreateApp} content={children} />
|
||||||
<WrappedRoute path='/developers/settings_store' developerOnly page={DefaultPage} component={SettingsStore} content={children} />
|
<WrappedRoute path='/developers/settings_store' developerOnly page={DefaultPage} component={SettingsStore} content={children} />
|
||||||
|
<WrappedRoute path='/developers/timeline' developerOnly page={DefaultPage} component={TestTimeline} content={children} />
|
||||||
<WrappedRoute path='/developers' page={DefaultPage} component={Developers} content={children} />
|
<WrappedRoute path='/developers' page={DefaultPage} component={Developers} content={children} />
|
||||||
<WrappedRoute path='/error' page={EmptyPage} component={IntentionalError} content={children} />
|
<WrappedRoute path='/error' page={EmptyPage} component={IntentionalError} content={children} />
|
||||||
|
|
||||||
|
@ -727,7 +728,6 @@ class UI extends React.PureComponent {
|
||||||
<BackgroundShapes />
|
<BackgroundShapes />
|
||||||
|
|
||||||
<div className='z-10 flex flex-col'>
|
<div className='z-10 flex flex-col'>
|
||||||
{/* <PreHeader /> */}
|
|
||||||
<Navbar />
|
<Navbar />
|
||||||
|
|
||||||
<SwitchingColumnsArea location={location} onLayoutChange={this.handleLayoutChange} soapbox={soapbox} features={features}>
|
<SwitchingColumnsArea location={location} onLayoutChange={this.handleLayoutChange} soapbox={soapbox} features={features}>
|
||||||
|
|
|
@ -338,10 +338,6 @@ export function UserPanel() {
|
||||||
return import(/* webpackChunkName: "features/ui" */'../components/user_panel');
|
return import(/* webpackChunkName: "features/ui" */'../components/user_panel');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FeaturesPanel() {
|
|
||||||
return import(/* webpackChunkName: "features/ui" */'../components/features_panel');
|
|
||||||
}
|
|
||||||
|
|
||||||
export function PromoPanel() {
|
export function PromoPanel() {
|
||||||
return import(/* webpackChunkName: "features/ui" */'../components/promo_panel');
|
return import(/* webpackChunkName: "features/ui" */'../components/promo_panel');
|
||||||
}
|
}
|
||||||
|
@ -486,6 +482,10 @@ export function SettingsStore() {
|
||||||
return import(/* webpackChunkName: "features/developers" */'../../developers/settings_store');
|
return import(/* webpackChunkName: "features/developers" */'../../developers/settings_store');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function TestTimeline() {
|
||||||
|
return import(/* webpackChunkName: "features/test_timeline" */'../../test_timeline');
|
||||||
|
}
|
||||||
|
|
||||||
export function DatePicker() {
|
export function DatePicker() {
|
||||||
return import(/* webpackChunkName: "date_picker" */'../../birthdays/date_picker');
|
return import(/* webpackChunkName: "date_picker" */'../../birthdays/date_picker');
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,7 @@ export const CryptoAddressRecord = ImmutableRecord({
|
||||||
});
|
});
|
||||||
|
|
||||||
export const SoapboxConfigRecord = ImmutableRecord({
|
export const SoapboxConfigRecord = ImmutableRecord({
|
||||||
|
appleAppId: null,
|
||||||
logo: '',
|
logo: '',
|
||||||
banner: '',
|
banner: '',
|
||||||
brandColor: '', // Empty
|
brandColor: '', // Empty
|
||||||
|
|
|
@ -7,7 +7,6 @@ import LinkFooter from 'soapbox/features/ui/components/link_footer';
|
||||||
import BundleContainer from 'soapbox/features/ui/containers/bundle_container';
|
import BundleContainer from 'soapbox/features/ui/containers/bundle_container';
|
||||||
import {
|
import {
|
||||||
PromoPanel,
|
PromoPanel,
|
||||||
FeaturesPanel,
|
|
||||||
InstanceInfoPanel,
|
InstanceInfoPanel,
|
||||||
InstanceModerationPanel,
|
InstanceModerationPanel,
|
||||||
} from 'soapbox/features/ui/util/async-components';
|
} from 'soapbox/features/ui/util/async-components';
|
||||||
|
@ -30,7 +29,7 @@ export default @connect(mapStateToProps)
|
||||||
class RemoteInstancePage extends ImmutablePureComponent {
|
class RemoteInstancePage extends ImmutablePureComponent {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { me, children, params: { instance: host }, disclosed, isAdmin } = this.props;
|
const { children, params: { instance: host }, disclosed, isAdmin } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
|
@ -43,11 +42,6 @@ class RemoteInstancePage extends ImmutablePureComponent {
|
||||||
</Layout.Main>
|
</Layout.Main>
|
||||||
|
|
||||||
<Layout.Aside>
|
<Layout.Aside>
|
||||||
{me && (
|
|
||||||
<BundleContainer fetchComponent={FeaturesPanel}>
|
|
||||||
{Component => <Component key='features-panel' />}
|
|
||||||
</BundleContainer>
|
|
||||||
)}
|
|
||||||
<BundleContainer fetchComponent={PromoPanel}>
|
<BundleContainer fetchComponent={PromoPanel}>
|
||||||
{Component => <Component key='promo-panel' />}
|
{Component => <Component key='promo-panel' />}
|
||||||
</BundleContainer>
|
</BundleContainer>
|
||||||
|
|
Loading…
Reference in New Issue