+
- { profileCardVisible &&
-
- }
{status.get('group') && (
@@ -208,6 +195,11 @@ export default class DetailedStatus extends ImmutablePureComponent {
+ {favicon &&
+
+

+
}
+
{statusTypeIcon}
diff --git a/app/soapbox/features/status/containers/detailed_status_container.js b/app/soapbox/features/status/containers/detailed_status_container.js
index 7c78a2e4e..509587636 100644
--- a/app/soapbox/features/status/containers/detailed_status_container.js
+++ b/app/soapbox/features/status/containers/detailed_status_container.js
@@ -12,6 +12,8 @@ import {
favourite,
unreblog,
unfavourite,
+ bookmark,
+ unbookmark,
pin,
unpin,
} from '../../../actions/interactions';
@@ -88,6 +90,14 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
});
},
+ onBookmark(status) {
+ if (status.get('bookmarked')) {
+ dispatch(unbookmark(status));
+ } else {
+ dispatch(bookmark(status));
+ }
+ },
+
onFavourite(status) {
if (status.get('favourited')) {
dispatch(unfavourite(status));
diff --git a/app/soapbox/features/status/index.js b/app/soapbox/features/status/index.js
index 8eb3530f6..4f1fe6084 100644
--- a/app/soapbox/features/status/index.js
+++ b/app/soapbox/features/status/index.js
@@ -14,6 +14,8 @@ import {
unfavourite,
reblog,
unreblog,
+ bookmark,
+ unbookmark,
pin,
unpin,
} from '../../actions/interactions';
@@ -168,6 +170,14 @@ class Status extends ImmutablePureComponent {
}
}
+ handleBookmark = (status) => {
+ if (status.get('bookmarked')) {
+ this.props.dispatch(unbookmark(status));
+ } else {
+ this.props.dispatch(bookmark(status));
+ }
+ }
+
handleReplyClick = (status) => {
let { askReplyConfirmation, dispatch, intl } = this.props;
if (askReplyConfirmation) {
@@ -408,7 +418,7 @@ class Status extends ImmutablePureComponent {
}
if (prevProps.status && ancestorsIds && ancestorsIds.size > 0) {
- const element = this.node.querySelectorAll('.focusable')[ancestorsIds.size - 1];
+ const element = this.node.querySelector('.detailed-status');
window.requestAnimationFrame(() => {
element.scrollIntoView(true);
@@ -507,6 +517,7 @@ class Status extends ImmutablePureComponent {
onBlock={this.handleBlockClick}
onReport={this.handleReport}
onPin={this.handlePin}
+ onBookmark={this.handleBookmark}
onEmbed={this.handleEmbed}
/>
diff --git a/app/soapbox/features/ui/components/tabs_bar.js b/app/soapbox/features/ui/components/tabs_bar.js
index e85efa40a..1440e2ae6 100644
--- a/app/soapbox/features/ui/components/tabs_bar.js
+++ b/app/soapbox/features/ui/components/tabs_bar.js
@@ -46,6 +46,11 @@ class TabsBar extends React.PureComponent {
this.node = ref;
}
+ isHomeActive = (match, location) => {
+ const { pathname } = location;
+ return pathname === '/' || pathname.startsWith('/timeline/');
+ }
+
getNavLinks() {
const { intl: { formatMessage }, logo, account } = this.props;
let links = [];
@@ -57,7 +62,7 @@ class TabsBar extends React.PureComponent {
);
}
links.push(
-
+
);
diff --git a/app/soapbox/features/ui/components/user_panel.js b/app/soapbox/features/ui/components/user_panel.js
index 1ba4e3efc..411b9b70b 100644
--- a/app/soapbox/features/ui/components/user_panel.js
+++ b/app/soapbox/features/ui/components/user_panel.js
@@ -56,26 +56,26 @@ class UserPanel extends ImmutablePureComponent {
-
+ {account.get('statuses_count') &&
{shortNumberFormat(account.get('statuses_count'))}
-
+
}
-
+ {account.get('followers_count') &&
{shortNumberFormat(account.get('followers_count'))}
-
+
}
-
+ {account.get('following_count') &&
{shortNumberFormat(account.get('following_count'))}
-
+
}
diff --git a/app/soapbox/features/ui/index.js b/app/soapbox/features/ui/index.js
index 59269d577..3f37ffe40 100644
--- a/app/soapbox/features/ui/index.js
+++ b/app/soapbox/features/ui/index.js
@@ -18,6 +18,7 @@ import { expandHomeTimeline } from '../../actions/timelines';
import { expandNotifications } from '../../actions/notifications';
import { fetchReports } from '../../actions/admin';
import { fetchFilters } from '../../actions/filters';
+import { fetchChats } from 'soapbox/actions/chats';
import { clearHeight } from '../../actions/height_cache';
import { openModal } from '../../actions/modal';
import { WrappedRoute } from './util/react_router_helpers';
@@ -37,6 +38,7 @@ import { Redirect } from 'react-router-dom';
import Icon from 'soapbox/components/icon';
import { isStaff } from 'soapbox/utils/accounts';
import ChatPanes from 'soapbox/features/chats/components/chat_panes';
+import ProfileHoverCard from 'soapbox/components/profile_hover_card';
import {
Status,
@@ -433,6 +435,7 @@ class UI extends React.PureComponent {
if (account) {
this.props.dispatch(expandHomeTimeline());
this.props.dispatch(expandNotifications());
+ this.props.dispatch(fetchChats());
// this.props.dispatch(fetchGroups('member'));
if (isStaff(account))
this.props.dispatch(fetchReports({ state: 'open' }));
@@ -648,6 +651,7 @@ class UI extends React.PureComponent {
{me && }
{me && !mobile && }
+
);
diff --git a/app/soapbox/reducers/chat_message_lists.js b/app/soapbox/reducers/chat_message_lists.js
index 0d635070f..3e2d1dbae 100644
--- a/app/soapbox/reducers/chat_message_lists.js
+++ b/app/soapbox/reducers/chat_message_lists.js
@@ -9,9 +9,15 @@ import { Map as ImmutableMap, OrderedSet as ImmutableOrderedSet } from 'immutabl
const initialState = ImmutableMap();
+const idComparator = (a, b) => {
+ if (a < b) return -1;
+ if (a > b) return 1;
+ return 0;
+};
+
const updateList = (state, chatId, messageIds) => {
const ids = state.get(chatId, ImmutableOrderedSet());
- const newIds = ids.union(messageIds);
+ const newIds = ids.union(messageIds).sort(idComparator);
return state.set(chatId, newIds);
};
@@ -31,22 +37,28 @@ const importLastMessages = (state, chats) =>
if (chat.last_message) importMessage(mutable, chat.last_message);
}));
+const replaceMessage = (state, chatId, oldId, newId) => {
+ const ids = state.get(chatId, ImmutableOrderedSet());
+ const newIds = ids.delete(oldId).add(newId).sort(idComparator);
+ return state.set(chatId, newIds);
+};
+
export default function chatMessageLists(state = initialState, action) {
switch(action.type) {
case CHAT_MESSAGE_SEND_REQUEST:
- return updateList(state, action.chatId, [action.uuid]).sort();
+ return updateList(state, action.chatId, [action.uuid]);
case CHATS_FETCH_SUCCESS:
- return importLastMessages(state, action.chats).sort();
+ return importLastMessages(state, action.chats);
case STREAMING_CHAT_UPDATE:
if (action.chat.last_message &&
action.chat.last_message.account_id !== action.me)
- return importMessages(state, [action.chat.last_message]).sort();
+ return importMessages(state, [action.chat.last_message]);
else
return state;
case CHAT_MESSAGES_FETCH_SUCCESS:
- return updateList(state, action.chatId, action.chatMessages.map(chat => chat.id).reverse()).sort();
+ return updateList(state, action.chatId, action.chatMessages.map(chat => chat.id));
case CHAT_MESSAGE_SEND_SUCCESS:
- return updateList(state, action.chatId, [action.chatMessage.id]).sort();
+ return replaceMessage(state, action.chatId, action.uuid, action.chatMessage.id);
default:
return state;
}
diff --git a/app/soapbox/reducers/index.js b/app/soapbox/reducers/index.js
index 2caec469a..6f4abdafe 100644
--- a/app/soapbox/reducers/index.js
+++ b/app/soapbox/reducers/index.js
@@ -46,6 +46,7 @@ import admin from './admin';
import chats from './chats';
import chat_messages from './chat_messages';
import chat_message_lists from './chat_message_lists';
+import profile_hover_card from './profile_hover_card';
const reducers = {
dropdown_menu,
@@ -95,6 +96,7 @@ const reducers = {
chats,
chat_messages,
chat_message_lists,
+ profile_hover_card,
};
export default combineReducers(reducers);
diff --git a/app/soapbox/reducers/notifications.js b/app/soapbox/reducers/notifications.js
index c4c97c73f..e121c4e82 100644
--- a/app/soapbox/reducers/notifications.js
+++ b/app/soapbox/reducers/notifications.js
@@ -61,8 +61,9 @@ const normalizeNotification = (state, notification) => {
const expandNormalizedNotifications = (state, notifications, next) => {
let items = ImmutableList();
- notifications.forEach((n, i) => {
- items = items.set(i, notificationToMap(n));
+ notifications.forEach((n) => {
+ if (!n.account.id) return;
+ items = items.push(notificationToMap(n));
});
return state.withMutations(mutable => {
diff --git a/app/soapbox/reducers/profile_hover_card.js b/app/soapbox/reducers/profile_hover_card.js
new file mode 100644
index 000000000..5776a3e87
--- /dev/null
+++ b/app/soapbox/reducers/profile_hover_card.js
@@ -0,0 +1,27 @@
+import {
+ PROFILE_HOVER_CARD_OPEN,
+ PROFILE_HOVER_CARD_CLOSE,
+ PROFILE_HOVER_CARD_UPDATE,
+} from 'soapbox/actions/profile_hover_card';
+import { Map as ImmutableMap } from 'immutable';
+
+const initialState = ImmutableMap();
+
+export default function profileHoverCard(state = initialState, action) {
+ switch(action.type) {
+ case PROFILE_HOVER_CARD_OPEN:
+ return ImmutableMap({
+ ref: action.ref,
+ accountId: action.accountId,
+ });
+ case PROFILE_HOVER_CARD_UPDATE:
+ return state.set('hovered', true);
+ case PROFILE_HOVER_CARD_CLOSE:
+ if (state.get('hovered') === true && !action.force)
+ return state;
+ else
+ return ImmutableMap();
+ default:
+ return state;
+ }
+}
diff --git a/app/soapbox/reducers/soapbox.js b/app/soapbox/reducers/soapbox.js
index dd7b0bea3..ecf1656f5 100644
--- a/app/soapbox/reducers/soapbox.js
+++ b/app/soapbox/reducers/soapbox.js
@@ -1,10 +1,17 @@
import { ADMIN_CONFIG_UPDATE_SUCCESS } from '../actions/admin';
-import { SOAPBOX_CONFIG_REQUEST_SUCCESS } from '../actions/soapbox';
+import {
+ SOAPBOX_CONFIG_REQUEST_SUCCESS,
+ SOAPBOX_CONFIG_REQUEST_FAIL,
+} from '../actions/soapbox';
import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
import { ConfigDB } from 'soapbox/utils/config_db';
const initialState = ImmutableMap();
+const fallbackState = ImmutableMap({
+ brandColor: '#0482d8', // Azure
+});
+
const updateFromAdmin = (state, config) => {
const configs = config.get('configs', ImmutableList());
@@ -22,6 +29,8 @@ export default function soapbox(state = initialState, action) {
switch(action.type) {
case SOAPBOX_CONFIG_REQUEST_SUCCESS:
return fromJS(action.soapboxConfig);
+ case SOAPBOX_CONFIG_REQUEST_FAIL:
+ return fallbackState.mergeDeep(state);
case ADMIN_CONFIG_UPDATE_SUCCESS:
return updateFromAdmin(state, fromJS(action.config));
default:
diff --git a/app/soapbox/reducers/user_lists.js b/app/soapbox/reducers/user_lists.js
index bb8ce8f1a..9dd079203 100644
--- a/app/soapbox/reducers/user_lists.js
+++ b/app/soapbox/reducers/user_lists.js
@@ -20,7 +20,7 @@ import {
MUTES_FETCH_SUCCESS,
MUTES_EXPAND_SUCCESS,
} from '../actions/mutes';
-import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
+import { Map as ImmutableMap, OrderedSet as ImmutableOrderedSet } from 'immutable';
import {
GROUP_MEMBERS_FETCH_SUCCESS,
GROUP_MEMBERS_EXPAND_SUCCESS,
@@ -44,7 +44,7 @@ const initialState = ImmutableMap({
const normalizeList = (state, type, id, accounts, next) => {
return state.setIn([type, id], ImmutableMap({
next,
- items: ImmutableList(accounts.map(item => item.id)),
+ items: ImmutableOrderedSet(accounts.map(item => item.id)),
}));
};
@@ -65,22 +65,22 @@ export default function userLists(state = initialState, action) {
case FOLLOWING_EXPAND_SUCCESS:
return appendToList(state, 'following', action.id, action.accounts, action.next);
case REBLOGS_FETCH_SUCCESS:
- return state.setIn(['reblogged_by', action.id], ImmutableList(action.accounts.map(item => item.id)));
+ return state.setIn(['reblogged_by', action.id], ImmutableOrderedSet(action.accounts.map(item => item.id)));
case FAVOURITES_FETCH_SUCCESS:
- return state.setIn(['favourited_by', action.id], ImmutableList(action.accounts.map(item => item.id)));
+ return state.setIn(['favourited_by', action.id], ImmutableOrderedSet(action.accounts.map(item => item.id)));
case FOLLOW_REQUESTS_FETCH_SUCCESS:
- return state.setIn(['follow_requests', 'items'], ImmutableList(action.accounts.map(item => item.id))).setIn(['follow_requests', 'next'], action.next);
+ return state.setIn(['follow_requests', 'items'], ImmutableOrderedSet(action.accounts.map(item => item.id))).setIn(['follow_requests', 'next'], action.next);
case FOLLOW_REQUESTS_EXPAND_SUCCESS:
return state.updateIn(['follow_requests', 'items'], list => list.concat(action.accounts.map(item => item.id))).setIn(['follow_requests', 'next'], action.next);
case FOLLOW_REQUEST_AUTHORIZE_SUCCESS:
case FOLLOW_REQUEST_REJECT_SUCCESS:
return state.updateIn(['follow_requests', 'items'], list => list.filterNot(item => item === action.id));
case BLOCKS_FETCH_SUCCESS:
- return state.setIn(['blocks', 'items'], ImmutableList(action.accounts.map(item => item.id))).setIn(['blocks', 'next'], action.next);
+ return state.setIn(['blocks', 'items'], ImmutableOrderedSet(action.accounts.map(item => item.id))).setIn(['blocks', 'next'], action.next);
case BLOCKS_EXPAND_SUCCESS:
return state.updateIn(['blocks', 'items'], list => list.concat(action.accounts.map(item => item.id))).setIn(['blocks', 'next'], action.next);
case MUTES_FETCH_SUCCESS:
- return state.setIn(['mutes', 'items'], ImmutableList(action.accounts.map(item => item.id))).setIn(['mutes', 'next'], action.next);
+ return state.setIn(['mutes', 'items'], ImmutableOrderedSet(action.accounts.map(item => item.id))).setIn(['mutes', 'next'], action.next);
case MUTES_EXPAND_SUCCESS:
return state.updateIn(['mutes', 'items'], list => list.concat(action.accounts.map(item => item.id))).setIn(['mutes', 'next'], action.next);
case GROUP_MEMBERS_FETCH_SUCCESS:
diff --git a/app/soapbox/utils/accounts.js b/app/soapbox/utils/accounts.js
index 3d04a3360..929429b24 100644
--- a/app/soapbox/utils/accounts.js
+++ b/app/soapbox/utils/accounts.js
@@ -1,21 +1,26 @@
import { Map as ImmutableMap } from 'immutable';
import { List as ImmutableList } from 'immutable';
+const guessDomain = account => {
+ try {
+ let re = /https?:\/\/(.*?)\//i;
+ return re.exec(account.get('url'))[1];
+ } catch(e) {
+ return null;
+ }
+};
+
export const getDomain = account => {
- let re = /https?:\/\/(.*?)\//i;
- return re.exec(account.get('url'))[1];
+ let domain = account.get('acct').split('@')[1];
+ if (!domain) domain = guessDomain(account);
+ return domain;
};
// user@domain even for local users
export const acctFull = account => {
- let [user, domain] = account.get('acct').split('@');
- try {
- if (!domain) domain = getDomain(account);
- } catch(e) {
- console.warning('Could not get domain for acctFull. Falling back to acct.');
- return account.get('acct');
- }
- return [user, domain].join('@');
+ const [user, domain] = account.get('acct').split('@');
+ if (!domain) return [user, guessDomain(account)].join('@');
+ return account.get('acct');
};
export const isStaff = (account = ImmutableMap()) => (
diff --git a/app/soapbox/utils/media.js b/app/soapbox/utils/media.js
new file mode 100644
index 000000000..c8266c646
--- /dev/null
+++ b/app/soapbox/utils/media.js
@@ -0,0 +1,10 @@
+export const truncateFilename = (url, maxLength) => {
+ const filename = url.split('/').pop();
+
+ if (filename.length <= maxLength) return filename;
+
+ return [
+ filename.substr(0, maxLength/2),
+ filename.substr(filename.length - maxLength/2),
+ ].join('…');
+};
diff --git a/app/styles/application.scss b/app/styles/application.scss
index 96eb4dcf4..fdce329ad 100644
--- a/app/styles/application.scss
+++ b/app/styles/application.scss
@@ -27,7 +27,6 @@
@import 'dyslexic';
@import 'demetricator';
@import 'pro';
-@import 'overflow_hacks';
@import 'chats';
// COMPONENTS
diff --git a/app/styles/chats.scss b/app/styles/chats.scss
index a7369c53b..5a8752987 100644
--- a/app/styles/chats.scss
+++ b/app/styles/chats.scss
@@ -113,6 +113,8 @@
background-color: var(--background-color);
overflow: hidden;
text-overflow: ellipsis;
+ overflow-wrap: break-word;
+ white-space: break-spaces;
a {
color: var(--brand-color--hicontrast);
@@ -150,6 +152,10 @@
.display-name {
display: flex;
+ .hover-ref-wrapper {
+ display: flex;
+ }
+
bdi {
overflow: hidden;
text-overflow: ellipsis;
@@ -173,10 +179,65 @@
}
.chat-box {
+ .upload-progress {
+ padding: 0 10px;
+ align-items: center;
+ height: 25px;
+
+ .fa {
+ font-size: 22px;
+ }
+
+ &__message {
+ font-size: 13px;
+ flex: 1;
+ align-items: center;
+ }
+
+ &__backdrop {
+ margin-top: 2px;
+ }
+ }
+
+ &__attachment {
+ display: flex;
+ align-items: center;
+ font-size: 13px;
+ padding: 0 10px;
+ height: 25px;
+
+ .chat-box__remove-attachment {
+ margin-left: auto;
+
+ .icon-button > div {
+ display: flex;
+ align-items: center;
+ }
+ }
+ }
+
&__actions {
background: var(--foreground-color);
margin-top: auto;
padding: 6px;
+ position: relative;
+
+ .icon-button {
+ color: var(--primary-text-color--faint);
+ position: absolute;
+ right: 10px;
+ top: calc(50% - 13px);
+ width: auto;
+ height: auto;
+ background: transparent !important;
+ border: 0;
+ padding: 0;
+ margin: 0;
+ }
+
+ .chat-box__send .icon-button {
+ top: calc(50% - 9px);
+ }
textarea {
width: 100%;
@@ -184,11 +245,13 @@
margin: 0;
box-sizing: border-box;
padding: 6px;
+ padding-right: 25px;
background: var(--background-color);
border: 0;
border-radius: 6px;
color: var(--primary-text-color);
font-size: 15px;
+ overflow: hidden;
}
}
}
@@ -215,7 +278,38 @@
border-radius: 0 0 10px 10px;
&__actions textarea {
- padding: 10px;
+ padding: 10px 40px 10px 10px;
+ }
+ }
+ }
+
+ @media(max-width: 630px) {
+ .columns-area__panels__main .columns-area {
+ padding: 0;
+ }
+
+ .columns-area__panels__main {
+ padding: 0;
+ max-width: none;
+ }
+
+ .columns-area--mobile .column {
+ border-radius: 0;
+ }
+
+ .page {
+ .chat-box {
+ border-radius: 0;
+ border: 2px solid var(--foreground-color);
+
+ &__actions {
+ padding: 0;
+
+ textarea {
+ height: 4em;
+ border-radius: 0;
+ }
+ }
}
}
}
@@ -238,6 +332,7 @@
margin-left: auto;
padding-right: 15px;
overflow: hidden;
+ text-decoration: none;
.account__avatar {
margin-right: 7px;
@@ -252,7 +347,7 @@
background: transparent;
border: 0;
padding: 0;
- color: #fff;
+ color: var(--primary-text-color);
font-weight: bold;
text-align: left;
font-size: 14px;
@@ -263,7 +358,6 @@
display: flex;
align-items: center;
background: var(--accent-color--faint);
- border-radius: 10px 10px 0 0;
.column-back-button {
background: transparent;
@@ -276,5 +370,37 @@
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
+
+ a {
+ color: var(--highlight-text-color);
+ }
+ }
+}
+
+.chat-message__media {
+ height: 120px;
+}
+
+.chat-message .media-gallery {
+ height: 100% !important;
+ margin: 4px 0 8px;
+
+ .spoiler-button {
+ display: none;
+ }
+
+ .media-gallery__item:not(.media-gallery__item--image) {
+ max-width: 100%;
+ width: 120px !important;
+ height: 100% !important;
+ }
+
+ &__preview {
+ background-color: transparent;
+ }
+
+ &__item-thumbnail img,
+ &__item-thumbnail .still-image img {
+ object-fit: contain;
}
}
diff --git a/app/styles/components/columns.scss b/app/styles/components/columns.scss
index d8034e497..10e46e441 100644
--- a/app/styles/components/columns.scss
+++ b/app/styles/components/columns.scss
@@ -212,7 +212,6 @@
font-size: 16px;
line-height: inherit;
border: 0;
- border-radius: 10px 10px 0 0;
text-align: unset;
padding: 15px;
margin: 0;
diff --git a/app/styles/components/detailed-status.scss b/app/styles/components/detailed-status.scss
index 6795bba5e..ceab5899c 100644
--- a/app/styles/components/detailed-status.scss
+++ b/app/styles/components/detailed-status.scss
@@ -62,7 +62,6 @@
border-bottom: 1px solid var(--brand-color--faint);
display: flex;
flex-direction: row;
- border-radius: 0 0 10px 10px;
}
.detailed-status__link {
@@ -125,3 +124,8 @@
float: left;
margin-right: 10px;
}
+
+.detailed-status .status__favicon {
+ float: left;
+ margin-right: 5px;
+}
diff --git a/app/styles/components/drawer.scss b/app/styles/components/drawer.scss
index 39e936158..7dc5a1a10 100644
--- a/app/styles/components/drawer.scss
+++ b/app/styles/components/drawer.scss
@@ -20,7 +20,7 @@
.column,
.drawer {
flex: 1 1 100%;
- overflow: visible;
+ overflow: hidden;
}
.drawer__pager {
diff --git a/app/styles/components/media-gallery.scss b/app/styles/components/media-gallery.scss
index 731f361fc..02318c6d8 100644
--- a/app/styles/components/media-gallery.scss
+++ b/app/styles/components/media-gallery.scss
@@ -134,6 +134,7 @@
}
.media-gallery__gifv__label,
+.media-gallery__filename__label,
.media-gallery__file-extension__label {
display: block;
position: absolute;
diff --git a/app/styles/components/profile_hover_card.scss b/app/styles/components/profile_hover_card.scss
index decb68711..b5b9da716 100644
--- a/app/styles/components/profile_hover_card.scss
+++ b/app/styles/components/profile_hover_card.scss
@@ -15,19 +15,14 @@
transition-duration: 0.2s;
width: 320px;
z-index: 200;
- left: -10px;
- padding: 20px;
- margin-bottom: 10px;
+ top: 0;
+ left: 0;
&--visible {
opacity: 1;
pointer-events: all;
}
- @media(min-width: 750px) {
- left: -100px;
- }
-
.profile-hover-card__container {
@include standard-panel;
position: relative;
@@ -110,22 +105,3 @@
}
}
}
-
-.detailed-status {
- .profile-hover-card {
- top: 0;
- left: 60px;
- }
-}
-
-/* Prevent floating avatars from intercepting with current card */
-.status,
-.detailed-status {
- .floating-link {
- display: none;
- }
-
- &:hover .floating-link {
- display: block;
- }
-}
diff --git a/app/styles/components/status.scss b/app/styles/components/status.scss
index 5aa440367..69f1ed24e 100644
--- a/app/styles/components/status.scss
+++ b/app/styles/components/status.scss
@@ -635,3 +635,15 @@ a.status-card.compact:hover {
background-size: cover;
background-position: center center;
}
+
+.status__favicon {
+ width: 16px;
+ height: 16px;
+ float: right;
+ margin-right: 4px;
+
+ img {
+ width: 100%;
+ max-height: 100%;
+ }
+}
diff --git a/app/styles/components/tabs-bar.scss b/app/styles/components/tabs-bar.scss
index 76920534b..a9cf3b1d4 100644
--- a/app/styles/components/tabs-bar.scss
+++ b/app/styles/components/tabs-bar.scss
@@ -143,6 +143,10 @@
.setting-toggle {
margin-left: 10px;
+ .react-toggle-track {
+ background-color: var(--foreground-color);
+ }
+
.react-toggle--checked {
.react-toggle-track {
background-color: var(--accent-color);
diff --git a/app/styles/components/theme-toggle.scss b/app/styles/components/theme-toggle.scss
index 1f2697b89..c0cadc68f 100644
--- a/app/styles/components/theme-toggle.scss
+++ b/app/styles/components/theme-toggle.scss
@@ -9,10 +9,6 @@
.react-toggle {
vertical-align: middle;
- &-track {
- background-color: var(--foreground-color);
- }
-
&-track-check,
&-track-x {
display: flex;
diff --git a/app/styles/loading.scss b/app/styles/loading.scss
index 88705782e..42a3a0c1f 100644
--- a/app/styles/loading.scss
+++ b/app/styles/loading.scss
@@ -188,7 +188,6 @@
align-items: center;
justify-content: center;
padding: 20px;
- border-radius: 0 0 10px 10px;
& > div {
width: 100%;
diff --git a/app/styles/overflow_hacks.scss b/app/styles/overflow_hacks.scss
deleted file mode 100644
index ef72b6863..000000000
--- a/app/styles/overflow_hacks.scss
+++ /dev/null
@@ -1,37 +0,0 @@
-// This is a file dedicated to fixing the css we broke by introducing the hover
-// card and `overflow:visible` on drawer.scss line 23. If we ever figure out how
-// to pop the hover card out while keeping `overflow:hidden`, feel free to delete
-// this entire file.
-
-button.column-header__button.active {
- border-radius: 0 10px 0 0;
-}
-
-.column-back-button.column-back-button--slim-button {
- border-radius: 0 10px 0 0;
-}
-
-.detailed-status__wrapper .detailed-status__action-bar {
- border-radius: 0 0 10px 10px;
-}
-
-.slist .item-list .column-link {
- background-color: transparent;
- border-top: 1px solid var(--brand-color--med);
-}
-
-.focusable {
- &:focus {
- border-radius: 0 0 10px 10px;
- }
-}
-
-.load-more:hover {
- border-radius: 0 0 10px 10px;
-}
-
-// this still looks like shit but at least it's better than it overflowing
-
-.empty-column-indicator {
- border-radius: 0 0 10px 10px;
-}
diff --git a/package.json b/package.json
index e79c4356c..e407f8788 100644
--- a/package.json
+++ b/package.json
@@ -46,6 +46,7 @@
"@babel/preset-react": "^7.0.0",
"@babel/runtime": "^7.3.4",
"@clusterws/cws": "^0.16.0",
+ "@popperjs/core": "^2.4.4",
"array-includes": "^3.0.3",
"autoprefixer": "^9.5.1",
"axios": "^0.19.0",
@@ -114,7 +115,8 @@
"react-motion": "^0.5.2",
"react-notification": "^6.8.4",
"react-overlays": "^0.8.3",
- "react-redux": "^6.0.1",
+ "react-popper": "^2.2.3",
+ "react-redux": "^7.2.1",
"react-redux-loading-bar": "^4.5.0",
"react-router-dom": "^4.1.1",
"react-router-scroll-4": "^1.0.0-beta.1",
@@ -124,7 +126,7 @@
"react-textarea-autosize": "^7.1.0",
"react-toggle": "^4.0.1",
"redis": "^2.7.1",
- "redux": "^4.0.1",
+ "redux": "^4.0.5",
"redux-immutable": "^4.0.0",
"redux-thunk": "^2.2.0",
"rellax": "^1.7.1",
@@ -138,7 +140,7 @@
"substring-trie": "^1.0.2",
"throng": "^4.0.0",
"tiny-queue": "^0.2.1",
- "uglifyjs-webpack-plugin": "^2.1.2",
+ "uglifyjs-webpack-plugin": "^2.2.0",
"uuid": "^3.1.0",
"webpack": "^4.41.2",
"webpack-assets-manifest": "^3.1.1",
diff --git a/webpack/production.js b/webpack/production.js
index a788e7884..9ed66b400 100644
--- a/webpack/production.js
+++ b/webpack/production.js
@@ -40,9 +40,7 @@ module.exports = merge(sharedConfig, {
sourceMap: true,
uglifyOptions: {
- compress: {
- warnings: false,
- },
+ warnings: false,
output: {
comments: false,
diff --git a/yarn.lock b/yarn.lock
index 561257434..3129a175f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -978,7 +978,7 @@
dependencies:
regenerator-runtime "^0.12.0"
-"@babel/runtime@^7.1.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.2":
+"@babel/runtime@^7.1.2", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.2":
version "7.4.5"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.5.tgz#582bb531f5f9dc67d2fcb682979894f75e253f12"
integrity sha512-TuI4qpWZP6lGOGIuGWtp9sPluqYICmbk8T/1vpSysqJxRPkudh/ofFWyqdcMsDf2s7KvDL4/YHgKyvcS3g9CJQ==
@@ -992,6 +992,13 @@
dependencies:
regenerator-runtime "^0.13.2"
+"@babel/runtime@^7.5.5":
+ version "7.11.2"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736"
+ integrity sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==
+ dependencies:
+ regenerator-runtime "^0.13.4"
+
"@babel/runtime@^7.7.2":
version "7.7.2"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.7.2.tgz#111a78002a5c25fc8e3361bedc9529c696b85a6a"
@@ -1482,6 +1489,11 @@
"@types/yargs" "^15.0.0"
chalk "^4.0.0"
+"@popperjs/core@^2.4.4":
+ version "2.4.4"
+ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.4.4.tgz#11d5db19bd178936ec89cd84519c4de439574398"
+ integrity sha512-1oO6+dN5kdIA3sKPZhRGJTfGVP4SWV6KqlMOwry4J3HfyD68sl/3KmG7DeYUzvN+RbhXDnv/D8vNNB8168tAMg==
+
"@sinonjs/commons@^1.7.0":
version "1.8.0"
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.0.tgz#c8d68821a854c555bba172f3b06959a0039b236d"
@@ -3257,11 +3269,6 @@ commander@^4.1.1:
resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==
-commander@~2.17.1:
- version "2.17.1"
- resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
- integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==
-
commondir@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
@@ -9488,6 +9495,11 @@ react-fast-compare@^2.0.4:
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9"
integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==
+react-fast-compare@^3.0.1:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb"
+ integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==
+
react-helmet@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/react-helmet/-/react-helmet-6.0.0.tgz#fcb93ebaca3ba562a686eb2f1f9d46093d83b5f8"
@@ -9563,12 +9575,12 @@ react-intl@^4.6.6:
intl-messageformat-parser "^5.1.1"
shallow-equal "^1.2.1"
-react-is@^16.12.0, react-is@^16.8.6:
+react-is@^16.12.0, react-is@^16.8.6, react-is@^16.9.0:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
-react-is@^16.3.2, react-is@^16.6.1, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.2:
+react-is@^16.3.2, react-is@^16.6.1, react-is@^16.7.0, react-is@^16.8.1:
version "16.8.6"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16"
integrity sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==
@@ -9615,6 +9627,14 @@ react-overlays@^0.8.3:
react-transition-group "^2.2.0"
warning "^3.0.0"
+react-popper@^2.2.3:
+ version "2.2.3"
+ resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-2.2.3.tgz#33d425fa6975d4bd54d9acd64897a89d904b9d97"
+ integrity sha512-mOEiMNT1249js0jJvkrOjyHsGvqcJd3aGW/agkiMoZk3bZ1fXN1wQszIQSjHIai48fE67+zwF8Cs+C4fWqlfjw==
+ dependencies:
+ react-fast-compare "^3.0.1"
+ warning "^4.0.2"
+
react-redux-loading-bar@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/react-redux-loading-bar/-/react-redux-loading-bar-4.5.0.tgz#96538d0ba041463d810e213fb54eadbce9628266"
@@ -9623,17 +9643,16 @@ react-redux-loading-bar@^4.5.0:
prop-types "^15.6.2"
react-lifecycles-compat "^3.0.2"
-react-redux@^6.0.1:
- version "6.0.1"
- resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-6.0.1.tgz#0d423e2c1cb10ada87293d47e7de7c329623ba4d"
- integrity sha512-T52I52Kxhbqy/6TEfBv85rQSDz6+Y28V/pf52vDWs1YRXG19mcFOGfHnY2HsNFHyhP+ST34Aih98fvt6tqwVcQ==
+react-redux@^7.2.1:
+ version "7.2.1"
+ resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.1.tgz#8dedf784901014db2feca1ab633864dee68ad985"
+ integrity sha512-T+VfD/bvgGTUA74iW9d2i5THrDQWbweXP0AVNI8tNd1Rk5ch1rnMiJkDD67ejw7YBKM4+REvcvqRuWJb7BLuEg==
dependencies:
- "@babel/runtime" "^7.3.1"
+ "@babel/runtime" "^7.5.5"
hoist-non-react-statics "^3.3.0"
- invariant "^2.2.4"
loose-envify "^1.4.0"
prop-types "^15.7.2"
- react-is "^16.8.2"
+ react-is "^16.9.0"
react-router-dom@^4.1.1:
version "4.3.1"
@@ -9931,10 +9950,10 @@ redux-thunk@^2.2.0:
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622"
integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==
-redux@^4.0.1:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.1.tgz#436cae6cc40fbe4727689d7c8fae44808f1bfef5"
- integrity sha512-R7bAtSkk7nY6O/OYMVR9RiBI+XghjF9rlbl5806HJbQph0LJVHZrU5oaO4q70eUKiqMRqm4y07KLTlMZ2BlVmg==
+redux@^4.0.5:
+ version "4.0.5"
+ resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f"
+ integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==
dependencies:
loose-envify "^1.4.0"
symbol-observable "^1.2.0"
@@ -9966,6 +9985,11 @@ regenerator-runtime@^0.13.2:
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz#32e59c9a6fb9b1a4aff09b4930ca2d4477343447"
integrity sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA==
+regenerator-runtime@^0.13.4:
+ version "0.13.7"
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"
+ integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==
+
regenerator-transform@^0.13.4:
version "0.13.4"
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.13.4.tgz#18f6763cf1382c69c36df76c6ce122cc694284fb"
@@ -11572,27 +11596,25 @@ ua-parser-js@^0.7.18:
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.19.tgz#94151be4c0a7fb1d001af7022fdaca4642659e4b"
integrity sha512-T3PVJ6uz8i0HzPxOF9SWzWAlfN/DavlpQqepn22xgve/5QecC+XMCAtmUNnY7C9StehaV6exjUCI801lOI7QlQ==
-uglify-js@^3.0.0:
- version "3.4.9"
- resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.9.tgz#af02f180c1207d76432e473ed24a28f4a782bae3"
- integrity sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==
- dependencies:
- commander "~2.17.1"
- source-map "~0.6.1"
+uglify-js@^3.6.0:
+ version "3.10.4"
+ resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.10.4.tgz#dd680f5687bc0d7a93b14a3482d16db6eba2bfbb"
+ integrity sha512-kBFT3U4Dcj4/pJ52vfjCSfyLyvG9VYYuGYPmrPvAxRw/i7xHiT4VvCev+uiEMcEEiu6UNB6KgWmGtSUYIWScbw==
-uglifyjs-webpack-plugin@^2.1.2:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-2.1.2.tgz#70e5c38fb2d35ee887949c2a0adb2656c23296d5"
- integrity sha512-G1fJx2uOAAfvdZ77SVCzmFo6mv8uKaHoZBL9Qq/ciC8r6p0ANOL1uY85fIUiyWXKw5RzAaJYZfNSL58Or2hQ0A==
+uglifyjs-webpack-plugin@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-2.2.0.tgz#e75bc80e7f1937f725954c9b4c5a1e967ea9d0d7"
+ integrity sha512-mHSkufBmBuJ+KHQhv5H0MXijtsoA1lynJt1lXOaotja8/I0pR4L9oGaPIZw+bQBOFittXZg9OC1sXSGO9D9ZYg==
dependencies:
- cacache "^11.2.0"
- find-cache-dir "^2.0.0"
+ cacache "^12.0.2"
+ find-cache-dir "^2.1.0"
+ is-wsl "^1.1.0"
schema-utils "^1.0.0"
- serialize-javascript "^1.4.0"
+ serialize-javascript "^1.7.0"
source-map "^0.6.1"
- uglify-js "^3.0.0"
- webpack-sources "^1.1.0"
- worker-farm "^1.5.2"
+ uglify-js "^3.6.0"
+ webpack-sources "^1.4.0"
+ worker-farm "^1.7.0"
unicode-astral-regex@^1.0.1:
version "1.0.1"
@@ -11874,6 +11896,13 @@ warning@^4.0.1:
dependencies:
loose-envify "^1.0.0"
+warning@^4.0.2:
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"
+ integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==
+ dependencies:
+ loose-envify "^1.0.0"
+
watchpack@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00"
@@ -12139,13 +12168,6 @@ wordwrap@~1.0.0:
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=
-worker-farm@^1.5.2:
- version "1.6.0"
- resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.6.0.tgz#aecc405976fab5a95526180846f0dba288f3a4a0"
- integrity sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ==
- dependencies:
- errno "~0.1.7"
-
worker-farm@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8"