diff --git a/app/soapbox/actions/notifications.js b/app/soapbox/actions/notifications.js index ce7e5eca0..66918c698 100644 --- a/app/soapbox/actions/notifications.js +++ b/app/soapbox/actions/notifications.js @@ -98,7 +98,7 @@ export function updateNotificationsQueue(notification, intlMessages, intlLocale, const isOnNotificationsPage = curPath === '/notifications'; - if (notification.type === 'mention') { + if (['mention', 'status'].includes(notification.type)) { const regex = regexFromFilters(filters); const searchIndex = notification.status.spoiler_text + '\n' + unescapeHTML(notification.status.content); filtered = regex && regex.test(searchIndex); @@ -170,7 +170,7 @@ export function dequeueNotifications() { const excludeTypesFromSettings = getState => getSettings(getState()).getIn(['notifications', 'shows']).filter(enabled => !enabled).keySeq().toJS(); const excludeTypesFromFilter = filter => { - const allTypes = ImmutableList(['follow', 'follow_request', 'favourite', 'reblog', 'mention', 'poll', 'move', 'pleroma:emoji_reaction']); + const allTypes = ImmutableList(['follow', 'follow_request', 'favourite', 'reblog', 'mention', 'status', 'poll', 'move', 'pleroma:emoji_reaction']); return allTypes.filterNot(item => item === filter).toJS(); }; diff --git a/app/soapbox/components/account.tsx b/app/soapbox/components/account.tsx index 3288127a1..132b9dfa2 100644 --- a/app/soapbox/components/account.tsx +++ b/app/soapbox/components/account.tsx @@ -35,6 +35,7 @@ interface IAccount { showProfileHoverCard?: boolean, timestamp?: string | Date, timestampUrl?: string, + withDate?: boolean, withRelationship?: boolean, } @@ -51,6 +52,7 @@ const Account = ({ showProfileHoverCard = true, timestamp, timestampUrl, + withDate = false, withRelationship = true, }: IAccount) => { const overflowRef = React.useRef(null); @@ -122,6 +124,8 @@ const Account = ({ ); } + if (withDate) timestamp = account.created_at; + const LinkEl: any = showProfileHoverCard ? Link : 'div'; return ( diff --git a/app/soapbox/components/autosuggest_input.js b/app/soapbox/components/autosuggest_input.js index 1bb2e2edf..d13873c80 100644 --- a/app/soapbox/components/autosuggest_input.js +++ b/app/soapbox/components/autosuggest_input.js @@ -206,8 +206,8 @@ export default class AutosuggestInput extends ImmutablePureComponent { key={key} data-index={i} className={classNames({ - 'px-4 py-2.5 text-sm text-gray-700 cursor-pointer hover:bg-gray-100 group': true, - 'bg-gray-100 hover:bg-gray-100': i === selectedSuggestion, + 'px-4 py-2.5 text-sm text-gray-700 dark:text-gray-400 cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-700 group': true, + 'bg-gray-100 dark:bg-slate-700 hover:bg-gray-100 dark:hover:bg-gray-700': i === selectedSuggestion, })} onMouseDown={this.onSuggestionClick} > @@ -238,7 +238,7 @@ export default class AutosuggestInput extends ImmutablePureComponent { return menu.map((item, i) => (
, 'onCh name?: string, placeholder?: string, value?: string, - onChange?: () => void, + onChange?: (event: React.ChangeEvent) => void, type: 'text' | 'email' | 'tel' | 'password' } diff --git a/app/soapbox/features/admin/components/latest_accounts_panel.js b/app/soapbox/features/admin/components/latest_accounts_panel.js deleted file mode 100644 index 6377b6810..000000000 --- a/app/soapbox/features/admin/components/latest_accounts_panel.js +++ /dev/null @@ -1,87 +0,0 @@ -import { is } from 'immutable'; -import PropTypes from 'prop-types'; -import React from 'react'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import ImmutablePureComponent from 'react-immutable-pure-component'; -import { injectIntl, defineMessages } from 'react-intl'; -import { connect } from 'react-redux'; - -import { fetchUsers } from 'soapbox/actions/admin'; -import compareId from 'soapbox/compare_id'; -import AccountListPanel from 'soapbox/features/ui/components/account_list_panel'; - -const messages = defineMessages({ - title: { id: 'admin.latest_accounts_panel.title', defaultMessage: 'Latest Accounts' }, - expand: { id: 'admin.latest_accounts_panel.expand_message', defaultMessage: 'Click to see {count} more {count, plural, one {account} other {accounts}}' }, -}); - -const mapStateToProps = state => { - const accountIds = state.getIn(['admin', 'latestUsers']); - - // HACK: AdminAPI only recently started sorting new users at the top. - // Try a dirty check to see if the users are sorted properly, or don't show the panel. - // Probably works most of the time. - const sortedIds = accountIds.sort(compareId).reverse(); - const hasDates = accountIds.every(id => state.getIn(['accounts', id, 'created_at'])); - const isSorted = hasDates && is(accountIds, sortedIds); - - return { - isSorted, - accountIds, - }; -}; - -export default @connect(mapStateToProps) -@injectIntl -class LatestAccountsPanel extends ImmutablePureComponent { - - static propTypes = { - accountIds: ImmutablePropTypes.orderedSet.isRequired, - limit: PropTypes.number, - }; - - static defaultProps = { - limit: 5, - } - - state = { - total: 0, - } - - componentDidMount() { - const { dispatch, limit } = this.props; - - dispatch(fetchUsers(['local', 'active'], 1, null, limit)) - .then(({ count }) => { - this.setState({ total: count }); - }) - .catch(() => {}); - } - - render() { - const { intl, accountIds, limit, isSorted, ...props } = this.props; - const { total } = this.state; - - if (!isSorted || !accountIds || accountIds.isEmpty()) { - return null; - } - - const expandCount = total - accountIds.size; - - return ( - - ); - } - -} diff --git a/app/soapbox/features/admin/components/latest_accounts_panel.tsx b/app/soapbox/features/admin/components/latest_accounts_panel.tsx new file mode 100644 index 000000000..b1e9a441b --- /dev/null +++ b/app/soapbox/features/admin/components/latest_accounts_panel.tsx @@ -0,0 +1,63 @@ +import { OrderedSet as ImmutableOrderedSet, is } from 'immutable'; +import React, { useState } from 'react'; +import { useEffect } from 'react'; +import { defineMessages, useIntl } from 'react-intl'; +import { Link } from 'react-router-dom'; + +import { fetchUsers } from 'soapbox/actions/admin'; +import compareId from 'soapbox/compare_id'; +import { Text, Widget } from 'soapbox/components/ui'; +import AccountContainer from 'soapbox/containers/account_container'; +import { useAppSelector } from 'soapbox/hooks'; +import { useAppDispatch } from 'soapbox/hooks'; + +const messages = defineMessages({ + title: { id: 'admin.latest_accounts_panel.title', defaultMessage: 'Latest Accounts' }, + expand: { id: 'admin.latest_accounts_panel.expand_message', defaultMessage: 'Click to see {count} more {count, plural, one {account} other {accounts}}' }, +}); + +interface ILatestAccountsPanel { + limit?: number, +} + +const LatestAccountsPanel: React.FC = ({ limit = 5 }) => { + const dispatch = useAppDispatch(); + const intl = useIntl(); + + const accountIds = useAppSelector>((state) => state.admin.get('latestUsers').take(limit)); + const hasDates = useAppSelector((state) => accountIds.every(id => !!state.accounts.getIn([id, 'created_at']))); + + const [total, setTotal] = useState(accountIds.size); + + useEffect(() => { + dispatch(fetchUsers(['local', 'active'], 1, null, limit)) + .then((value) => { + setTotal((value as { count: number }).count); + }) + .catch(() => {}); + }, []); + + const sortedIds = accountIds.sort(compareId).reverse(); + const isSorted = hasDates && is(accountIds, sortedIds); + + if (!isSorted || !accountIds || accountIds.isEmpty()) { + return null; + } + + const expandCount = total - accountIds.size; + + return ( + + {accountIds.take(limit).map((account) => ( + + ))} + {!!expandCount && ( + + {intl.formatMessage(messages.expand, { count: expandCount })} + + )} + + ); +}; + +export default LatestAccountsPanel; diff --git a/app/soapbox/features/admin/index.js b/app/soapbox/features/admin/index.js index 09f626f0e..93a38c53f 100644 --- a/app/soapbox/features/admin/index.js +++ b/app/soapbox/features/admin/index.js @@ -7,6 +7,7 @@ import { connect } from 'react-redux'; import { Link } from 'react-router-dom'; import { getSubscribersCsv, getUnsubscribersCsv, getCombinedCsv } from 'soapbox/actions/email_list'; +import { Text } from 'soapbox/components/ui'; import sourceCode from 'soapbox/utils/code'; import { parseVersion } from 'soapbox/utils/features'; import { getFeatures } from 'soapbox/utils/features'; @@ -86,56 +87,46 @@ class Dashboard extends ImmutablePureComponent {
{mau &&
-
-
- -
-
- -
-
+ + + + + +
} -
- -
- -
-
- -
- -
+ + + + + + + + {isNumber(retention) && (
-
-
- {retention}% -
-
- -
-
+ + {retention}% + + + +
)} + + + + + + + +
- -
- -
-
- -
- -
-
-
-
- -
-
- -
-
+ + + + + +
{account.admin && } diff --git a/app/soapbox/features/admin/user_index.js b/app/soapbox/features/admin/user_index.js index d93f99d28..dc7691f69 100644 --- a/app/soapbox/features/admin/user_index.js +++ b/app/soapbox/features/admin/user_index.js @@ -116,6 +116,7 @@ class UserIndex extends ImmutablePureComponent { showLoading={showLoading} onLoadMore={this.handleLoadMore} emptyMessage={intl.formatMessage(messages.empty)} + className='mt-4 space-y-4' > {accountIds.map(id => , diff --git a/app/soapbox/features/delete_account/index.js b/app/soapbox/features/delete_account/index.tsx similarity index 94% rename from app/soapbox/features/delete_account/index.js rename to app/soapbox/features/delete_account/index.tsx index f0d902e4a..733b9554c 100644 --- a/app/soapbox/features/delete_account/index.js +++ b/app/soapbox/features/delete_account/index.tsx @@ -1,11 +1,11 @@ import PropTypes from 'prop-types'; import * as React from 'react'; import { defineMessages, useIntl } from 'react-intl'; -import { useDispatch } from 'react-redux'; import { deleteAccount } from 'soapbox/actions/security'; import snackbar from 'soapbox/actions/snackbar'; import { Button, Card, CardBody, CardHeader, CardTitle, Form, FormActions, FormGroup, Input } from 'soapbox/components/ui'; +import { useAppDispatch } from 'soapbox/hooks'; const messages = defineMessages({ passwordFieldLabel: { id: 'security.fields.password.label', defaultMessage: 'Password' }, @@ -18,12 +18,12 @@ const messages = defineMessages({ const DeleteAccount = () => { const intl = useIntl(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const [password, setPassword] = React.useState(''); const [isLoading, setLoading] = React.useState(false); - const handleInputChange = React.useCallback((event) => { + const handleInputChange = React.useCallback((event: React.ChangeEvent) => { event.persist(); setPassword(event.target.value); diff --git a/app/soapbox/features/developers/developers_challenge.js b/app/soapbox/features/developers/developers_challenge.js deleted file mode 100644 index b62191227..000000000 --- a/app/soapbox/features/developers/developers_challenge.js +++ /dev/null @@ -1,92 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import { FormattedMessage, injectIntl, defineMessages } from 'react-intl'; -import { connect } from 'react-redux'; - -import { changeSettingImmediate } from 'soapbox/actions/settings'; -import snackbar from 'soapbox/actions/snackbar'; -import { Button, Form, FormActions, FormGroup, Input, Text } from 'soapbox/components/ui'; - - -import Column from '../ui/components/column'; - -const messages = defineMessages({ - heading: { id: 'column.developers', defaultMessage: 'Developers' }, - answerLabel: { id: 'developers.challenge.answer_label', defaultMessage: 'Answer' }, - answerPlaceholder: { id: 'developers.challenge.answer_placeholder', defaultMessage: 'Your answer' }, - success: { id: 'developers.challenge.success', defaultMessage: 'You are now a developer' }, - fail: { id: 'developers.challenge.fail', defaultMessage: 'Wrong answer' }, -}); - -export default @connect() -@injectIntl -class DevelopersChallenge extends React.Component { - - static propTypes = { - intl: PropTypes.object.isRequired, - dispatch: PropTypes.func.isRequired, - } - - state = { - answer: '', - } - - handleChangeAnswer = e => { - this.setState({ answer: e.target.value }); - } - - handleSubmit = e => { - const { intl, dispatch } = this.props; - const { answer } = this.state; - - if (answer === 'boxsoap') { - dispatch(changeSettingImmediate(['isDeveloper'], true)); - dispatch(snackbar.success(intl.formatMessage(messages.success))); - } else { - dispatch(snackbar.error(intl.formatMessage(messages.fail))); - } - } - - render() { - const { intl } = this.props; - - const challenge = `function soapbox() { - return 'soap|box'.split('|').reverse().join(''); -}`; - - return ( - -
- - soapbox() }} - /> - - - {challenge} - - - - - - - - - -
-
- ); - } - -} diff --git a/app/soapbox/features/developers/developers_challenge.tsx b/app/soapbox/features/developers/developers_challenge.tsx new file mode 100644 index 000000000..d27b46429 --- /dev/null +++ b/app/soapbox/features/developers/developers_challenge.tsx @@ -0,0 +1,80 @@ +import React from 'react'; +import { useState } from 'react'; +import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; +import { useDispatch } from 'react-redux'; + +import { changeSettingImmediate } from 'soapbox/actions/settings'; +import snackbar from 'soapbox/actions/snackbar'; +import { Button, Form, FormActions, FormGroup, Input, Text } from 'soapbox/components/ui'; + + +import Column from '../ui/components/column'; + +const messages = defineMessages({ + heading: { id: 'column.developers', defaultMessage: 'Developers' }, + answerLabel: { id: 'developers.challenge.answer_label', defaultMessage: 'Answer' }, + answerPlaceholder: { id: 'developers.challenge.answer_placeholder', defaultMessage: 'Your answer' }, + success: { id: 'developers.challenge.success', defaultMessage: 'You are now a developer' }, + fail: { id: 'developers.challenge.fail', defaultMessage: 'Wrong answer' }, +}); + +const DevelopersChallenge = () => { + const dispatch = useDispatch(); + const intl = useIntl(); + + const [answer, setAnswer] = useState(''); + + const handleChangeAnswer = (e: React.ChangeEvent) => { + setAnswer(e.target.value); + }; + + const handleSubmit = () => { + if (answer === 'boxsoap') { + dispatch(changeSettingImmediate(['isDeveloper'], true)); + dispatch(snackbar.success(intl.formatMessage(messages.success))); + } else { + dispatch(snackbar.error(intl.formatMessage(messages.fail))); + } + }; + + const challenge = `function soapbox() { + return 'soap|box'.split('|').reverse().join(''); +}`; + + return ( + +
+ + soapbox() }} + /> + + + {challenge} + + + + + + + + + +
+
+ ); +}; + +export default DevelopersChallenge; diff --git a/app/soapbox/features/developers/developers_menu.js b/app/soapbox/features/developers/developers_menu.tsx similarity index 97% rename from app/soapbox/features/developers/developers_menu.js rename to app/soapbox/features/developers/developers_menu.tsx index f4ab7f8ca..545c0a998 100644 --- a/app/soapbox/features/developers/developers_menu.js +++ b/app/soapbox/features/developers/developers_menu.tsx @@ -20,7 +20,7 @@ const Developers = () => { const history = useHistory(); const intl = useIntl(); - const leaveDevelopers = (e) => { + const leaveDevelopers = (e: React.MouseEvent) => { e.preventDefault(); dispatch(changeSettingImmediate(['isDeveloper'], false)); diff --git a/app/soapbox/features/developers/index.js b/app/soapbox/features/developers/index.js deleted file mode 100644 index 3d58c9249..000000000 --- a/app/soapbox/features/developers/index.js +++ /dev/null @@ -1,30 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import { connect } from 'react-redux'; - -import { getSettings } from 'soapbox/actions/settings'; - -import DevelopersChallenge from './developers_challenge'; -import DevelopersMenu from './developers_menu'; - -const mapStateToProps = state => { - const settings = getSettings(state); - - return { - isDeveloper: settings.get('isDeveloper'), - }; -}; - -export default @connect(mapStateToProps) -class Developers extends React.Component { - - static propTypes = { - isDeveloper: PropTypes.bool.isRequired, - } - - render() { - const { isDeveloper } = this.props; - return isDeveloper ? : ; - } - -} diff --git a/app/soapbox/features/developers/index.tsx b/app/soapbox/features/developers/index.tsx new file mode 100644 index 000000000..a192f1683 --- /dev/null +++ b/app/soapbox/features/developers/index.tsx @@ -0,0 +1,15 @@ +import React from 'react'; + +import { getSettings } from 'soapbox/actions/settings'; +import { useAppSelector } from 'soapbox/hooks'; + +import DevelopersChallenge from './developers_challenge'; +import DevelopersMenu from './developers_menu'; + +const Developers: React.FC = () => { + const isDeveloper = useAppSelector((state) => getSettings(state).get('isDeveloper')); + + return isDeveloper ? : ; +}; + +export default Developers; diff --git a/app/soapbox/features/notifications/components/filter_bar.js b/app/soapbox/features/notifications/components/filter_bar.js index 05e182146..d443205a7 100644 --- a/app/soapbox/features/notifications/components/filter_bar.js +++ b/app/soapbox/features/notifications/components/filter_bar.js @@ -15,6 +15,7 @@ const messages = defineMessages({ follows: { id: 'notifications.filter.follows', defaultMessage: 'Follows' }, moves: { id: 'notifications.filter.moves', defaultMessage: 'Moves' }, emoji_reacts: { id: 'notifications.filter.emoji_reacts', defaultMessage: 'Emoji reacts' }, + statuses: { id: 'notifications.filter.statuses', defaultMessage: 'Updates from people you follow' }, }); export default @injectIntl @@ -80,6 +81,12 @@ class NotificationFilterBar extends React.PureComponent { action: this.onClick('poll'), name: 'poll', }); + items.push({ + text: , + title: intl.formatMessage(messages.statuses), + action: this.onClick('status'), + name: 'status', + }); items.push({ text: , title: intl.formatMessage(messages.follows), diff --git a/app/soapbox/features/notifications/components/notification.js b/app/soapbox/features/notifications/components/notification.js index 77fa5762e..00911e68e 100644 --- a/app/soapbox/features/notifications/components/notification.js +++ b/app/soapbox/features/notifications/components/notification.js @@ -36,13 +36,14 @@ const buildLink = (account) => ( ); -export const NOTIFICATION_TYPES = ['follow', 'mention', 'favourite', 'reblog']; +export const NOTIFICATION_TYPES = ['follow', 'mention', 'favourite', 'reblog', 'status']; const icons = { follow: require('@tabler/icons/icons/user-plus.svg'), mention: require('@tabler/icons/icons/at.svg'), favourite: require('@tabler/icons/icons/heart.svg'), reblog: require('@tabler/icons/icons/repeat.svg'), + status: require('@tabler/icons/icons/home.svg'), }; const messages = { @@ -62,6 +63,10 @@ const messages = { id: 'notification.reblog', defaultMessage: '{name} re-TRUTH your TRUTH', }, + status: { + id: 'notification.status', + defaultMessage: '{name} just posted', + }, }; const buildMessage = (type, account) => { @@ -153,6 +158,7 @@ const Notification = (props) => { case 'favourite': case 'mention': case 'reblog': + case 'status': return ( { {({ top }) => (
{status && ( - + -
-

{header}

- -
+ } + secondaryAction={this.onRegister} + secondaryText={} + >
- +
- + - +
{!singleUserMode && ( - <> -

- - - - + + + )} - - -
-
+ ); } diff --git a/app/soapbox/hooks/index.ts b/app/soapbox/hooks/index.ts index d3c5f4700..6ec919a94 100644 --- a/app/soapbox/hooks/index.ts +++ b/app/soapbox/hooks/index.ts @@ -1,3 +1,4 @@ +export { useAppDispatch } from './useAppDispatch'; export { useAppSelector } from './useAppSelector'; export { useFeatures } from './useFeatures'; export { useOnScreen } from './useOnScreen'; diff --git a/app/soapbox/hooks/useAppDispatch.ts b/app/soapbox/hooks/useAppDispatch.ts new file mode 100644 index 000000000..11e7226d5 --- /dev/null +++ b/app/soapbox/hooks/useAppDispatch.ts @@ -0,0 +1,5 @@ +import { useDispatch } from 'react-redux'; + +import { AppDispatch } from 'soapbox/store'; + +export const useAppDispatch = () => useDispatch(); \ No newline at end of file diff --git a/app/soapbox/locales/pl.json b/app/soapbox/locales/pl.json index d4feae868..ca01e879b 100644 --- a/app/soapbox/locales/pl.json +++ b/app/soapbox/locales/pl.json @@ -694,6 +694,7 @@ "notification.pleroma:emoji_reaction": "{name} zareagował(a) na Twój wpis", "notification.poll": "Głosowanie w którym brałeś(-aś) udział zakończyła się", "notification.reblog": "{name} podbił(a) Twój wpis", + "notification.status": "{name} właśnie opublikował(a) wpis", "notifications.clear": "Wyczyść powiadomienia", "notifications.clear_confirmation": "Czy na pewno chcesz bezpowrotnie usunąć wszystkie powiadomienia?", "notifications.clear_heading": "Wyczyść powiadomienia", @@ -725,6 +726,7 @@ "notifications.filter.mentions": "Wspomienia", "notifications.filter.moves": "Przenoszone konta", "notifications.filter.polls": "Wyniki głosowania", + "notifications.filter.statuses": "Nowe wpisy osób, które subskrybujesz", "notifications.group": "{count, number} {count, plural, one {powiadomienie} few {powiadomienia} many {powiadomień} more {powiadomień}}", "notifications.queue_label": "Naciśnij aby zobaczyć {count} {count, plural, one {nowe powiadomienie} few {nowe powiadomienia} many {nowych powiadomień} other {nowe powiadomienia}}", "password_reset.confirmation": "Sprawdź swoją pocztę e-mail, aby potwierdzić.", diff --git a/app/soapbox/reducers/account_notes.js b/app/soapbox/reducers/account_notes.ts similarity index 68% rename from app/soapbox/reducers/account_notes.js rename to app/soapbox/reducers/account_notes.ts index 67cd83e1d..b7f6f2eab 100644 --- a/app/soapbox/reducers/account_notes.js +++ b/app/soapbox/reducers/account_notes.ts @@ -1,4 +1,5 @@ -import { Map as ImmutableMap } from 'immutable'; +import { Record as ImmutableRecord } from 'immutable'; +import { AnyAction } from 'redux'; import { ACCOUNT_NOTE_INIT_MODAL, @@ -8,15 +9,19 @@ import { ACCOUNT_NOTE_SUBMIT_SUCCESS, } from '../actions/account_notes'; -const initialState = ImmutableMap({ - edit: ImmutableMap({ - isSubmitting: false, - account: null, - comment: null, - }), +const EditRecord = ImmutableRecord({ + isSubmitting: false, + account: null, + comment: null, }); -export default function account_notes(state = initialState, action) { +const ReducerRecord = ImmutableRecord({ + edit: EditRecord(), +}); + +type State = ReturnType; + +export default function account_notes(state: State = ReducerRecord(), action: AnyAction) { switch (action.type) { case ACCOUNT_NOTE_INIT_MODAL: return state.withMutations((state) => { diff --git a/app/soapbox/reducers/notifications.js b/app/soapbox/reducers/notifications.js index fad2563c3..faade6a82 100644 --- a/app/soapbox/reducers/notifications.js +++ b/app/soapbox/reducers/notifications.js @@ -73,7 +73,7 @@ const isValid = notification => { } // Mastodon can return status notifications with a null status - if (['mention', 'reblog', 'favourite', 'poll'].includes(notification.type) && !notification.status.id) { + if (['mention', 'reblog', 'favourite', 'poll', 'status'].includes(notification.type) && !notification.status.id) { return false; } diff --git a/app/styles/components/admin.scss b/app/styles/components/admin.scss index 88a34b05c..56e7e9022 100644 --- a/app/styles/components/admin.scss +++ b/app/styles/components/admin.scss @@ -1,65 +1,9 @@ .dashcounters { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); - margin: 0 -5px 0; - padding: 20px; + @apply grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-2 mb-4; } .dashcounter { - box-sizing: border-box; - flex: 0 0 33.333%; - padding: 0 5px; - margin-bottom: 10px; - - > a, - > div { - box-sizing: border-box; - text-decoration: none; - color: inherit; - display: block; - padding: 20px; - background: var(--accent-color--faint); - border-radius: 4px; - transition: 0.2s; - height: 100%; - } - - > a:hover { - background: var(--accent-color--med); - transform: translateY(-2px); - } - - &__num, - &__icon, - &__text { - text-align: center; - font-weight: 500; - font-size: 24px; - line-height: 30px; - color: var(--primary-text-color); - margin-bottom: 10px; - } - - &__icon { - display: flex; - justify-content: center; - - .svg-icon { - width: 48px; - height: 48px; - - svg { - stroke-width: 1px; - } - } - } - - &__label { - font-size: 14px; - color: hsla(var(--primary-text-color_hsl), 0.6); - text-align: center; - font-weight: 500; - } + @apply bg-gray-200 dark:bg-gray-600 p-4 rounded flex flex-col items-center space-y-2 hover:-translate-y-1 transition-transform cursor-pointer; } .dashwidgets { diff --git a/app/styles/components/modal.scss b/app/styles/components/modal.scss index c04089318..728044af3 100644 --- a/app/styles/components/modal.scss +++ b/app/styles/components/modal.scss @@ -341,7 +341,7 @@ .actions-modal { .dropdown-menu__separator { - @apply block m-2 h-[1px] bg-gray-200; + @apply block m-2 h-[1px] bg-gray-200 dark:bg-gray-600; } } @@ -521,7 +521,7 @@ } .actions-modal { - @apply w-full max-h-full max-w-lg mt-auto mb-2 bg-white; + @apply w-full max-h-full max-w-lg mt-auto mb-2 bg-white dark:bg-slate-800; .status { overflow-y: auto; @@ -540,7 +540,7 @@ li:not(:empty) { a, button { - @apply flex items-center px-4 py-3 text-gray-600 no-underline hover:bg-gray-100 hover:text-gray-800; + @apply flex items-center px-4 py-3 text-gray-600 dark:text-gray-300 no-underline hover:bg-gray-100 dark:bg-gray-800 hover:text-gray-800 dark:hover:text-gray-200; &.destructive { @apply text-danger-600; @@ -817,9 +817,7 @@ &__content { display: flex; flex-direction: column; - // align-items: center; row-gap: 10px; - padding: 10px; .unauthorized-modal-content__button { margin: 0 auto; @@ -832,11 +830,8 @@ gap: 10px; width: 100%; - .button { - width: auto; - margin: 0; - text-transform: none; - overflow: unset; + button { + align-self: flex-end; } } @@ -848,9 +843,9 @@ &::before, &::after { + @apply border-b border-gray-300 dark:border-gray-600; content: ""; flex: 1; - border-bottom: 1px solid hsla(var(--primary-text-color_hsl), 0.2); } } diff --git a/app/styles/components/reply-mentions.scss b/app/styles/components/reply-mentions.scss index f94ba5181..5312d0859 100644 --- a/app/styles/components/reply-mentions.scss +++ b/app/styles/components/reply-mentions.scss @@ -1,5 +1,5 @@ .reply-mentions { - @apply text-gray-500 mb-1 text-sm; + @apply text-gray-500 dark:text-gray-400 mb-1 text-sm; &__account { @apply text-primary-600 no-underline; diff --git a/app/styles/components/wtf-panel.scss b/app/styles/components/wtf-panel.scss index 120abe338..113e33e1f 100644 --- a/app/styles/components/wtf-panel.scss +++ b/app/styles/components/wtf-panel.scss @@ -139,13 +139,13 @@ } &__expand-btn { + @apply border-gray-300 dark:border-gray-600; display: block; width: 100%; height: 100%; max-height: 46px; position: relative; border-top: 1px solid; - border-color: var(--brand-color--faint); transition: max-height 150ms ease; overflow: hidden; opacity: 1;