M .eslintrc.js => .eslintrc.js +1 -1
@@ 319,7 319,7 @@ module.exports = {
},
// Internal packages
{
- pattern: '{mastodon/**}',
+ pattern: '{mastodon/**,flavours/glitch-soc/**}',
group: 'internal',
position: 'after',
},
M app/javascript/flavours/glitch/actions/app.ts => app/javascript/flavours/glitch/actions/app.ts +3 -2
@@ 1,8 1,9 @@
import { createAction } from '@reduxjs/toolkit';
+
import type { LayoutType } from '../is_mobile';
-type ChangeLayoutPayload = {
+interface ChangeLayoutPayload {
layout: LayoutType;
-};
+}
export const changeLayout =
createAction<ChangeLayoutPayload>('APP_LAYOUT_CHANGE');
M app/javascript/flavours/glitch/actions/pin_statuses.js => app/javascript/flavours/glitch/actions/pin_statuses.js +2 -2
@@ 1,12 1,12 @@
import api from '../api';
import { importFetchedStatuses } from './importer';
+import { me } from 'flavours/glitch/initial_state';
+
export const PINNED_STATUSES_FETCH_REQUEST = 'PINNED_STATUSES_FETCH_REQUEST';
export const PINNED_STATUSES_FETCH_SUCCESS = 'PINNED_STATUSES_FETCH_SUCCESS';
export const PINNED_STATUSES_FETCH_FAIL = 'PINNED_STATUSES_FETCH_FAIL';
-import { me } from 'flavours/glitch/initial_state';
-
export function fetchPinnedStatuses() {
return (dispatch, getState) => {
dispatch(fetchPinnedStatusesRequest());
M app/javascript/flavours/glitch/components/animated_number.tsx => app/javascript/flavours/glitch/components/animated_number.tsx +11 -4
@@ 1,8 1,11 @@
import React, { useCallback, useState } from 'react';
-import ShortNumber from './short_number';
+
import { TransitionMotion, spring } from 'react-motion';
+
import { reduceMotion } from '../initial_state';
+import ShortNumber from './short_number';
+
const obfuscatedCount = (count: number) => {
if (count < 0) {
return 0;
@@ 13,10 16,10 @@ const obfuscatedCount = (count: number) => {
}
};
-type Props = {
+interface Props {
value: number;
obfuscate?: boolean;
-};
+}
export const AnimatedNumber: React.FC<Props> = ({ value, obfuscate }) => {
const [previousValue, setPreviousValue] = useState(value);
const [direction, setDirection] = useState<1 | -1>(1);
@@ 64,7 67,11 @@ export const AnimatedNumber: React.FC<Props> = ({ value, obfuscate }) => {
transform: `translateY(${style.y * 100}%)`,
}}
>
- {obfuscate ? obfuscatedCount(data) : <ShortNumber value={data} />}
+ {obfuscate ? (
+ obfuscatedCount(data as number)
+ ) : (
+ <ShortNumber value={data as number} />
+ )}
</span>
))}
</span>
M app/javascript/flavours/glitch/components/avatar.tsx => app/javascript/flavours/glitch/components/avatar.tsx +5 -3
@@ 1,16 1,18 @@
import * as React from 'react';
+
import classNames from 'classnames';
-import { autoPlayGif } from 'flavours/glitch/initial_state';
+
import { useHovering } from 'flavours/glitch/hooks/useHovering';
+import { autoPlayGif } from 'flavours/glitch/initial_state';
import type { Account } from 'flavours/glitch/types/resources';
-type Props = {
+interface Props {
account: Account | undefined;
className?: string;
size: number;
style?: React.CSSProperties;
inline?: boolean;
-};
+}
export const Avatar: React.FC<Props> = ({
account,
M app/javascript/flavours/glitch/components/blurhash.tsx => app/javascript/flavours/glitch/components/blurhash.tsx +5 -4
@@ 1,14 1,14 @@
-import { decode } from 'blurhash';
import React, { useRef, useEffect } from 'react';
-type Props = {
+import { decode } from 'blurhash';
+
+interface Props extends React.HTMLAttributes<HTMLCanvasElement> {
hash: string;
width?: number;
height?: number;
dummy?: boolean; // Whether dummy mode is enabled. If enabled, nothing is rendered and canvas left untouched
children?: never;
- [key: string]: any;
-};
+}
const Blurhash: React.FC<Props> = ({
hash,
width = 32,
@@ 21,6 21,7 @@ const Blurhash: React.FC<Props> = ({
useEffect(() => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const canvas = canvasRef.current!;
+
// eslint-disable-next-line no-self-assign
canvas.width = canvas.width; // resets canvas
M app/javascript/flavours/glitch/components/domain.tsx => app/javascript/flavours/glitch/components/domain.tsx +6 -3
@@ 1,6 1,9 @@
import React, { useCallback } from 'react';
+
+import type { InjectedIntl } from 'react-intl';
+import { defineMessages, injectIntl } from 'react-intl';
+
import { IconButton } from './icon_button';
-import { InjectedIntl, defineMessages, injectIntl } from 'react-intl';
const messages = defineMessages({
unblockDomain: {
@@ 9,11 12,11 @@ const messages = defineMessages({
},
});
-type Props = {
+interface Props {
domain: string;
onUnblockDomain: (domain: string) => void;
intl: InjectedIntl;
-};
+}
const _Domain: React.FC<Props> = ({ domain, onUnblockDomain, intl }) => {
const handleDomainUnblock = useCallback(() => {
onUnblockDomain(domain);
M app/javascript/flavours/glitch/components/gifv.tsx => app/javascript/flavours/glitch/components/gifv.tsx +2 -2
@@ 1,6 1,6 @@
import React, { useCallback, useState } from 'react';
-type Props = {
+interface Props {
src: string;
key: string;
alt?: string;
@@ 8,7 8,7 @@ type Props = {
width: number;
height: number;
onClick?: () => void;
-};
+}
export const GIFV: React.FC<Props> = ({
src,
M app/javascript/flavours/glitch/components/icon.tsx => app/javascript/flavours/glitch/components/icon.tsx +4 -3
@@ 1,13 1,14 @@
import React from 'react';
+
import classNames from 'classnames';
-type Props = {
+interface Props extends React.HTMLAttributes<HTMLImageElement> {
id: string;
className?: string;
fixedWidth?: boolean;
children?: never;
- [key: string]: any;
-};
+}
+
export const Icon: React.FC<Props> = ({
id,
className,
M app/javascript/flavours/glitch/components/icon_button.tsx => app/javascript/flavours/glitch/components/icon_button.tsx +7 -5
@@ 1,9 1,11 @@
import React from 'react';
+
import classNames from 'classnames';
-import { Icon } from './icon';
+
import { AnimatedNumber } from './animated_number';
+import { Icon } from './icon';
-type Props = {
+interface Props {
className?: string;
title: string;
icon: string;
@@ 26,11 28,11 @@ type Props = {
obfuscateCount?: boolean;
href?: string;
ariaHidden: boolean;
-};
-type States = {
+}
+interface States {
activate: boolean;
deactivate: boolean;
-};
+}
export class IconButton extends React.PureComponent<Props, States> {
static defaultProps = {
size: 18,
M app/javascript/flavours/glitch/components/icon_with_badge.tsx => app/javascript/flavours/glitch/components/icon_with_badge.tsx +3 -2
@@ 1,14 1,15 @@
import React from 'react';
+
import { Icon } from './icon';
const formatNumber = (num: number): number | string => (num > 40 ? '40+' : num);
-type Props = {
+interface Props {
id: string;
count: number;
issueBadge: boolean;
className: string;
-};
+}
export const IconWithBadge: React.FC<Props> = ({
id,
count,
M app/javascript/flavours/glitch/components/not_signed_in_indicator.tsx => app/javascript/flavours/glitch/components/not_signed_in_indicator.tsx +1 -0
@@ 1,4 1,5 @@
import React from 'react';
+
import { FormattedMessage } from 'react-intl';
export const NotSignedInIndicator: React.FC = () => (
M app/javascript/flavours/glitch/components/radio_button.tsx => app/javascript/flavours/glitch/components/radio_button.tsx +3 -2
@@ 1,13 1,14 @@
import React from 'react';
+
import classNames from 'classnames';
-type Props = {
+interface Props {
value: string;
checked: boolean;
name: string;
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
label: React.ReactNode;
-};
+}
export const RadioButton: React.FC<Props> = ({
name,
M app/javascript/flavours/glitch/components/relative_timestamp.tsx => app/javascript/flavours/glitch/components/relative_timestamp.tsx +7 -5
@@ 1,5 1,7 @@
import React from 'react';
-import { injectIntl, defineMessages, InjectedIntl } from 'react-intl';
+
+import type { InjectedIntl } from 'react-intl';
+import { injectIntl, defineMessages } from 'react-intl';
const messages = defineMessages({
today: { id: 'relative_time.today', defaultMessage: 'today' },
@@ 187,16 189,16 @@ const timeRemainingString = (
return relativeTime;
};
-type Props = {
+interface Props {
intl: InjectedIntl;
timestamp: string;
year: number;
futureDate?: boolean;
short?: boolean;
-};
-type States = {
+}
+interface States {
now: number;
-};
+}
class RelativeTimestamp extends React.Component<Props, States> {
state = {
now: this.props.intl.now(),
M app/javascript/flavours/glitch/components/server_hero_image.tsx => app/javascript/flavours/glitch/components/server_hero_image.tsx +5 -3
@@ 1,13 1,15 @@
import React, { useCallback, useState } from 'react';
-import { Blurhash } from './blurhash';
+
import classNames from 'classnames';
-type Props = {
+import { Blurhash } from './blurhash';
+
+interface Props {
src: string;
srcSet?: string;
blurhash?: string;
className?: string;
-};
+}
export const ServerHeroImage: React.FC<Props> = ({
src,
M app/javascript/flavours/glitch/features/account_timeline/index.jsx => app/javascript/flavours/glitch/features/account_timeline/index.jsx +1 -1
@@ 3,7 3,7 @@ import { connect } from 'react-redux';
import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types';
import { lookupAccount, fetchAccount } from 'flavours/glitch/actions/accounts';
-import { expandAccountFeaturedTimeline, expandAccountTimeline } from 'flavours/glitch/actions/timelines';
+import { expandAccountFeaturedTimeline, expandAccountTimeline } from '../../actions/timelines';
import StatusList from '../../components/status_list';
import LoadingIndicator from '../../components/loading_indicator';
import Column from '../ui/components/column';
M app/javascript/flavours/glitch/features/ui/components/columns_area.jsx => app/javascript/flavours/glitch/features/ui/components/columns_area.jsx +1 -1
@@ 18,7 18,7 @@ import {
BookmarkedStatuses,
ListTimeline,
Directory,
-} from '../../ui/util/async-components';
+} from '../util/async-components';
import ComposePanel from './compose_panel';
import NavigationPanel from './navigation_panel';
M app/javascript/flavours/glitch/features/ui/components/focal_point_modal.jsx => app/javascript/flavours/glitch/features/ui/components/focal_point_modal.jsx +1 -1
@@ 4,7 4,7 @@ import PropTypes from 'prop-types';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux';
import classNames from 'classnames';
-import { changeUploadCompose, uploadThumbnail, onChangeMediaDescription, onChangeMediaFocus } from 'flavours/glitch/actions/compose';
+import { changeUploadCompose, uploadThumbnail, onChangeMediaDescription, onChangeMediaFocus } from '../../../actions/compose';
import Video, { getPointerPosition } from 'flavours/glitch/features/video';
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
import { IconButton } from 'flavours/glitch/components/icon_button';
M app/javascript/flavours/glitch/features/ui/components/upload_area.jsx => app/javascript/flavours/glitch/features/ui/components/upload_area.jsx +1 -1
@@ 1,6 1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
-import Motion from '../../ui/util/optional_motion';
+import Motion from '../util/optional_motion';
import spring from 'react-motion/lib/spring';
import { FormattedMessage } from 'react-intl';
M app/javascript/flavours/glitch/features/ui/index.jsx => app/javascript/flavours/glitch/features/ui/index.jsx +1 -1
@@ 64,7 64,7 @@ import Header from './components/header';
// Dummy import, to make sure that <Status /> ends up in the application bundle.
// Without this it ends up in ~8 very commonly used bundles.
-import '../../../glitch/components/status';
+import "../../components/status";
const messages = defineMessages({
beforeUnload: { id: 'ui.beforeunload', defaultMessage: 'Your draft will be lost if you leave Mastodon.' },
M app/javascript/flavours/glitch/is_mobile.ts => app/javascript/flavours/glitch/is_mobile.ts +1 -0
@@ 1,4 1,5 @@
import { supportsPassiveEvents } from 'detect-passive-events';
+
import { forceSingleColumn } from 'flavours/glitch/initial_state';
const LAYOUT_BREAKPOINT = 630;
M app/javascript/flavours/glitch/packs/common.js => app/javascript/flavours/glitch/packs/common.js +2 -2
@@ 1,9 1,9 @@
import 'packs/public-path';
import { start } from '@rails/ujs';
-start();
-
import 'flavours/glitch/styles/index.scss';
+start();
+
// This ensures that webpack compiles our images.
require.context('../images', true);
M app/javascript/flavours/glitch/polyfills/base_polyfills.ts => app/javascript/flavours/glitch/polyfills/base_polyfills.ts +7 -2
@@ 10,8 10,13 @@ if (!HTMLCanvasElement.prototype.toBlob) {
const BASE64_MARKER = ';base64,';
Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', {
- value(callback: BlobCallback, type = 'image/png', quality: any) {
- const dataURL = this.toDataURL(type, quality);
+ value: function (
+ this: HTMLCanvasElement,
+ callback: BlobCallback,
+ type = 'image/png',
+ quality: unknown
+ ) {
+ const dataURL: string = this.toDataURL(type, quality);
let data;
if (dataURL.indexOf(BASE64_MARKER) >= 0) {
M app/javascript/flavours/glitch/reducers/index.ts => app/javascript/flavours/glitch/reducers/index.ts +35 -34
@@ 1,48 1,49 @@
-import { combineReducers } from 'redux-immutable';
-import dropdown_menu from './dropdown_menu';
-import timelines from './timelines';
-import meta from './meta';
-import alerts from './alerts';
import { loadingBarReducer } from 'react-redux-loading-bar';
-import modal from './modal';
-import user_lists from './user_lists';
-import domain_lists from './domain_lists';
+import { combineReducers } from 'redux-immutable';
+
+import account_notes from './account_notes';
import accounts from './accounts';
import accounts_counters from './accounts_counters';
-import statuses from './statuses';
-import relationships from './relationships';
-import settings from './settings';
-import local_settings from './local_settings';
-import push_notifications from './push_notifications';
-import status_lists from './status_lists';
-import mutes from './mutes';
+import accounts_map from './accounts_map';
+import alerts from './alerts';
+import announcements from './announcements';
import blocks from './blocks';
-import server from './server';
import boosts from './boosts';
-import contexts from './contexts';
import compose from './compose';
-import search from './search';
-import media_attachments from './media_attachments';
-import notifications from './notifications';
-import height_cache from './height_cache';
+import contexts from './contexts';
+import conversations from './conversations';
import custom_emojis from './custom_emojis';
-import lists from './lists';
-import listEditor from './list_editor';
-import listAdder from './list_adder';
+import domain_lists from './domain_lists';
+import dropdown_menu from './dropdown_menu';
import filters from './filters';
-import conversations from './conversations';
-import suggestions from './suggestions';
-import pinnedAccountsEditor from './pinned_accounts_editor';
-import polls from './polls';
-import trends from './trends';
-import announcements from './announcements';
+import followed_tags from './followed_tags';
+import height_cache from './height_cache';
+import history from './history';
+import listAdder from './list_adder';
+import listEditor from './list_editor';
+import lists from './lists';
+import local_settings from './local_settings';
import markers from './markers';
-import account_notes from './account_notes';
+import media_attachments from './media_attachments';
+import meta from './meta';
+import modal from './modal';
+import mutes from './mutes';
+import notifications from './notifications';
import picture_in_picture from './picture_in_picture';
-import accounts_map from './accounts_map';
-import history from './history';
+import pinnedAccountsEditor from './pinned_accounts_editor';
+import polls from './polls';
+import push_notifications from './push_notifications';
+import relationships from './relationships';
+import search from './search';
+import server from './server';
+import settings from './settings';
+import status_lists from './status_lists';
+import statuses from './statuses';
+import suggestions from './suggestions';
import tags from './tags';
-import followed_tags from './followed_tags';
+import timelines from './timelines';
+import trends from './trends';
+import user_lists from './user_lists';
const reducers = {
announcements,
M app/javascript/flavours/glitch/reducers/markers.js => app/javascript/flavours/glitch/reducers/markers.js +2 -2
@@ 2,13 2,13 @@ import {
MARKERS_SUBMIT_SUCCESS,
} from '../actions/markers';
+import { Map as ImmutableMap } from 'immutable';
+
const initialState = ImmutableMap({
home: '0',
notifications: '0',
});
-import { Map as ImmutableMap } from 'immutable';
-
export default function markers(state = initialState, action) {
switch(action.type) {
case MARKERS_SUBMIT_SUCCESS:
M app/javascript/flavours/glitch/store/index.ts => app/javascript/flavours/glitch/store/index.ts +6 -2
@@ 1,9 1,13 @@
+import type { TypedUseSelectorHook } from 'react-redux';
+import { useDispatch, useSelector } from 'react-redux';
+
import { configureStore } from '@reduxjs/toolkit';
+
import { rootReducer } from '../reducers';
-import { loadingBarMiddleware } from './middlewares/loading_bar';
+
import { errorsMiddleware } from './middlewares/errors';
+import { loadingBarMiddleware } from './middlewares/loading_bar';
import { soundsMiddleware } from './middlewares/sounds';
-import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
export const store = configureStore({
reducer: rootReducer,
M app/javascript/flavours/glitch/store/middlewares/errors.ts => app/javascript/flavours/glitch/store/middlewares/errors.ts +6 -4
@@ 1,17 1,19 @@
-import { Middleware } from 'redux';
+import type { AnyAction, Middleware } from 'redux';
+
import { showAlertForError } from 'flavours/glitch/actions/alerts';
-import { RootState } from '..';
+
+import type { RootState } from '..';
const defaultFailSuffix = 'FAIL';
export const errorsMiddleware: Middleware<Record<string, never>, RootState> =
({ dispatch }) =>
(next) =>
- (action) => {
+ (action: AnyAction & { skipAlert?: boolean; skipNotFound?: boolean }) => {
if (action.type && !action.skipAlert) {
const isFail = new RegExp(`${defaultFailSuffix}$`, 'g');
- if (action.type.match(isFail)) {
+ if (typeof action.type === 'string' && action.type.match(isFail)) {
dispatch(showAlertForError(action.error, action.skipNotFound));
}
}
M app/javascript/flavours/glitch/store/middlewares/loading_bar.ts => app/javascript/flavours/glitch/store/middlewares/loading_bar.ts +13 -10
@@ 1,6 1,7 @@
import { showLoading, hideLoading } from 'react-redux-loading-bar';
-import { Middleware } from 'redux';
-import { RootState } from '..';
+import type { AnyAction, Middleware } from 'redux';
+
+import type { RootState } from '..';
interface Config {
promiseTypeSuffixes?: string[];
@@ 19,7 20,7 @@ export const loadingBarMiddleware = (
return ({ dispatch }) =>
(next) =>
- (action) => {
+ (action: AnyAction) => {
if (action.type && !action.skipLoading) {
const [PENDING, FULFILLED, REJECTED] = promiseTypeSuffixes;
@@ 27,13 28,15 @@ export const loadingBarMiddleware = (
const isFulfilled = new RegExp(`${FULFILLED}$`, 'g');
const isRejected = new RegExp(`${REJECTED}$`, 'g');
- if (action.type.match(isPending)) {
- dispatch(showLoading());
- } else if (
- action.type.match(isFulfilled) ||
- action.type.match(isRejected)
- ) {
- dispatch(hideLoading());
+ if (typeof action.type === 'string') {
+ if (action.type.match(isPending)) {
+ dispatch(showLoading());
+ } else if (
+ action.type.match(isFulfilled) ||
+ action.type.match(isRejected)
+ ) {
+ dispatch(hideLoading());
+ }
}
}
M app/javascript/flavours/glitch/store/middlewares/sounds.ts => app/javascript/flavours/glitch/store/middlewares/sounds.ts +13 -10
@@ 1,5 1,6 @@
-import { Middleware, AnyAction } from 'redux';
-import { RootState } from '..';
+import type { Middleware, AnyAction } from 'redux';
+
+import type { RootState } from '..';
interface AudioSource {
src: string;
@@ 27,7 28,7 @@ const play = (audio: HTMLAudioElement) => {
}
}
- audio.play();
+ void audio.play();
};
export const soundsMiddleware = (): Middleware<
@@ 47,13 48,15 @@ export const soundsMiddleware = (): Middleware<
]),
};
- return () => (next) => (action: AnyAction) => {
- const sound = action?.meta?.sound;
+ return () =>
+ (next) =>
+ (action: AnyAction & { meta?: { sound?: string } }) => {
+ const sound = action?.meta?.sound;
- if (sound && soundCache[sound]) {
- play(soundCache[sound]);
- }
+ if (sound && soundCache[sound]) {
+ play(soundCache[sound]);
+ }
- return next(action);
- };
+ return next(action);
+ };
};
M app/javascript/flavours/glitch/types/resources.ts => app/javascript/flavours/glitch/types/resources.ts +4 -4
@@ 12,7 12,7 @@ type AccountField = Record<{
verified_at: string | null;
}>;
-type AccountApiResponseValues = {
+interface AccountApiResponseValues {
acct: string;
avatar: string;
avatar_static: string;
@@ 34,7 34,7 @@ type AccountApiResponseValues = {
statuses_count: number;
url: string;
username: string;
-};
+}
type NormalizedAccountField = Record<{
name_emojified: string;
@@ 42,12 42,12 @@ type NormalizedAccountField = Record<{
value_plain: string;
}>;
-type NormalizedAccountValues = {
+interface NormalizedAccountValues {
display_name_html: string;
fields: NormalizedAccountField[];
note_emojified: string;
note_plain: string;
-};
+}
export type Account = Record<
AccountApiResponseValues & NormalizedAccountValues
M app/javascript/flavours/glitch/utils/resize_image.js => app/javascript/flavours/glitch/utils/resize_image.js +3 -1
@@ 170,7 170,7 @@ const resizeImage = (img, type = 'image/png') => new Promise((resolve, reject) =
.catch(reject);
});
-export default inputFile => new Promise((resolve) => {
+const resizeFile = (inputFile) => new Promise((resolve) => {
if (!inputFile.type.match(/image.*/) || inputFile.type === 'image/gif') {
resolve(inputFile);
return;
@@ 187,3 187,5 @@ export default inputFile => new Promise((resolve) => {
.catch(() => resolve(inputFile));
}).catch(() => resolve(inputFile));
});
+
+export default resizeFile;
M app/javascript/flavours/glitch/uuid.ts => app/javascript/flavours/glitch/uuid.ts +4 -3
@@ 1,8 1,9 @@
export function uuid(a?: string): string {
return a
? (
- (a as any as number) ^
- ((Math.random() * 16) >> ((a as any as number) / 4))
+ (a as unknown as number) ^
+ ((Math.random() * 16) >> ((a as unknown as number) / 4))
).toString(16)
- : ('' + 1e7 + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, uuid);
+ : // eslint-disable-next-line @typescript-eslint/restrict-plus-operands
+ ('' + 1e7 + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, uuid);
}
M tsconfig.json => tsconfig.json +2 -0
@@ 12,6 12,8 @@
"baseUrl": "./",
"paths": {
"locales": ["app/javascript/locales"],
+ "styles/*": ["app/javascript/styles/*"],
+ "packs/public-path": ["app/javascript/packs/public-path"],
"flavours/glitch": ["app/javascript/flavours/glitch"],
"flavours/glitch/*": ["app/javascript/flavours/glitch/*"],
"mastodon": ["app/javascript/mastodon"],