Migrated all service providers to typescript

This commit is contained in:
Alejandro Celaya 2020-08-23 09:03:44 +02:00
parent 2eba607874
commit e193a692e8
10 changed files with 32 additions and 21 deletions

View file

@ -1,3 +1,4 @@
import Bottle, { Decorator } from 'bottlejs';
import ScrollToTop from '../ScrollToTop'; import ScrollToTop from '../ScrollToTop';
import MainHeader from '../MainHeader'; import MainHeader from '../MainHeader';
import Home from '../Home'; import Home from '../Home';
@ -5,9 +6,10 @@ import MenuLayout from '../MenuLayout';
import AsideMenu from '../AsideMenu'; import AsideMenu from '../AsideMenu';
import ErrorHandler from '../ErrorHandler'; import ErrorHandler from '../ErrorHandler';
import ShlinkVersions from '../ShlinkVersions'; import ShlinkVersions from '../ShlinkVersions';
import { ConnectDecorator } from '../../container/types';
const provideServices = (bottle, connect, withRouter) => { const provideServices = (bottle: Bottle, connect: ConnectDecorator, withRouter: Decorator) => {
bottle.constant('window', global.window); bottle.constant('window', (global as any).window);
bottle.constant('console', global.console); bottle.constant('console', global.console);
bottle.serviceFactory('ScrollToTop', ScrollToTop, 'window'); bottle.serviceFactory('ScrollToTop', ScrollToTop, 'window');

View file

@ -11,6 +11,7 @@ import provideTagsServices from '../tags/services/provideServices';
import provideUtilsServices from '../utils/services/provideServices'; import provideUtilsServices from '../utils/services/provideServices';
import provideMercureServices from '../mercure/services/provideServices'; import provideMercureServices from '../mercure/services/provideServices';
import provideSettingsServices from '../settings/services/provideServices'; import provideSettingsServices from '../settings/services/provideServices';
import { ConnectDecorator } from './types';
type ActionMap = Record<string, any>; type ActionMap = Record<string, any>;
@ -20,11 +21,10 @@ const { container } = bottle;
const lazyService = (container: IContainer, serviceName: string) => (...args: any[]) => container[serviceName](...args); const lazyService = (container: IContainer, serviceName: string) => (...args: any[]) => container[serviceName](...args);
const mapActionService = (map: ActionMap, actionName: string): ActionMap => ({ const mapActionService = (map: ActionMap, actionName: string): ActionMap => ({
...map, ...map,
// Wrap actual action service in a function so that it is lazily created the first time it is called // Wrap actual action service in a function so that it is lazily created the first time it is called
[actionName]: lazyService(container, actionName), [actionName]: lazyService(container, actionName),
}); });
const connect = (propsFromState: string[], actionServiceNames: string[] = []) => const connect: ConnectDecorator = (propsFromState: string[], actionServiceNames: string[] = []) =>
reduxConnect( reduxConnect(
propsFromState ? pick(propsFromState) : null, propsFromState ? pick(propsFromState) : null,
actionServiceNames.reduce(mapActionService, {}), actionServiceNames.reduce(mapActionService, {}),

1
src/container/types.ts Normal file
View file

@ -0,0 +1 @@
export type ConnectDecorator = (props: string[], actions?: string[]) => any;

View file

@ -1,6 +1,7 @@
import Bottle from 'bottlejs';
import { loadMercureInfo } from '../reducers/mercureInfo'; import { loadMercureInfo } from '../reducers/mercureInfo';
const provideServices = (bottle) => { const provideServices = (bottle: Bottle) => {
// Actions // Actions
bottle.serviceFactory('loadMercureInfo', loadMercureInfo, 'buildShlinkApiClient'); bottle.serviceFactory('loadMercureInfo', loadMercureInfo, 'buildShlinkApiClient');
}; };

View file

@ -1,11 +1,14 @@
import Bottle from 'bottlejs';
import RealTimeUpdates from '../RealTimeUpdates'; import RealTimeUpdates from '../RealTimeUpdates';
import Settings from '../Settings'; import Settings from '../Settings';
import { setRealTimeUpdates } from '../reducers/settings'; import { setRealTimeUpdates } from '../reducers/settings';
import { ConnectDecorator } from '../../container/types';
const provideServices = (bottle, connect) => { const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
// Components // Components
bottle.serviceFactory('Settings', Settings, 'RealTimeUpdates'); bottle.serviceFactory('Settings', Settings, 'RealTimeUpdates');
// Services
bottle.serviceFactory('RealTimeUpdates', () => RealTimeUpdates); bottle.serviceFactory('RealTimeUpdates', () => RealTimeUpdates);
bottle.decorator('RealTimeUpdates', connect([ 'settings' ], [ 'setRealTimeUpdates' ])); bottle.decorator('RealTimeUpdates', connect([ 'settings' ], [ 'setRealTimeUpdates' ]));

View file

@ -1,5 +1,6 @@
import { connect as reduxConnect } from 'react-redux'; import { connect as reduxConnect } from 'react-redux';
import { assoc } from 'ramda'; import { assoc } from 'ramda';
import Bottle from 'bottlejs';
import ShortUrls from '../ShortUrls'; import ShortUrls from '../ShortUrls';
import SearchBar from '../SearchBar'; import SearchBar from '../SearchBar';
import ShortUrlsList from '../ShortUrlsList'; import ShortUrlsList from '../ShortUrlsList';
@ -18,14 +19,16 @@ import { editShortUrlTags, resetShortUrlsTags } from '../reducers/shortUrlTags';
import { editShortUrlMeta, resetShortUrlMeta } from '../reducers/shortUrlMeta'; import { editShortUrlMeta, resetShortUrlMeta } from '../reducers/shortUrlMeta';
import { resetShortUrlParams } from '../reducers/shortUrlsListParams'; import { resetShortUrlParams } from '../reducers/shortUrlsListParams';
import { editShortUrl } from '../reducers/shortUrlEdition'; import { editShortUrl } from '../reducers/shortUrlEdition';
import { ConnectDecorator } from '../../container/types';
const provideServices = (bottle, connect) => { const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
// Components // Components
bottle.serviceFactory('ShortUrls', ShortUrls, 'SearchBar', 'ShortUrlsList'); bottle.serviceFactory('ShortUrls', ShortUrls, 'SearchBar', 'ShortUrlsList');
bottle.decorator('ShortUrls', reduxConnect( bottle.decorator('ShortUrls', reduxConnect(
(state) => assoc('shortUrlsList', state.shortUrlsList.shortUrls, state.shortUrlsList), (state: any) => assoc('shortUrlsList', state.shortUrlsList.shortUrls, state.shortUrlsList),
)); ));
// Services
bottle.serviceFactory('SearchBar', SearchBar, 'ColorGenerator', 'ForServerVersion'); bottle.serviceFactory('SearchBar', SearchBar, 'ColorGenerator', 'ForServerVersion');
bottle.decorator('SearchBar', connect([ 'shortUrlsListParams' ], [ 'listShortUrls' ])); bottle.decorator('SearchBar', connect([ 'shortUrlsListParams' ], [ 'listShortUrls' ]));

View file

@ -1,3 +1,4 @@
import Bottle, { IContainer } from 'bottlejs';
import TagsSelector from '../helpers/TagsSelector'; import TagsSelector from '../helpers/TagsSelector';
import TagCard from '../TagCard'; import TagCard from '../TagCard';
import DeleteTagConfirmModal from '../helpers/DeleteTagConfirmModal'; import DeleteTagConfirmModal from '../helpers/DeleteTagConfirmModal';
@ -6,8 +7,9 @@ import TagsList from '../TagsList';
import { filterTags, listTags } from '../reducers/tagsList'; import { filterTags, listTags } from '../reducers/tagsList';
import { deleteTag, tagDeleted } from '../reducers/tagDelete'; import { deleteTag, tagDeleted } from '../reducers/tagDelete';
import { editTag, tagEdited } from '../reducers/tagEdit'; import { editTag, tagEdited } from '../reducers/tagEdit';
import { ConnectDecorator } from '../../container/types';
const provideServices = (bottle, connect) => { const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
// Components // Components
bottle.serviceFactory('TagsSelector', TagsSelector, 'ColorGenerator'); bottle.serviceFactory('TagsSelector', TagsSelector, 'ColorGenerator');
bottle.decorator('TagsSelector', connect([ 'tagsList' ], [ 'listTags' ])); bottle.decorator('TagsSelector', connect([ 'tagsList' ], [ 'listTags' ]));
@ -34,7 +36,8 @@ const provideServices = (bottle, connect) => {
)); ));
// Actions // Actions
const listTagsActionFactory = (force) => ({ buildShlinkApiClient }) => listTags(buildShlinkApiClient, force); const listTagsActionFactory = (force: boolean) =>
({ buildShlinkApiClient }: IContainer) => listTags(buildShlinkApiClient, force);
bottle.factory('listTags', listTagsActionFactory(false)); bottle.factory('listTags', listTagsActionFactory(false));
bottle.factory('forceListTags', listTagsActionFactory(true)); bottle.factory('forceListTags', listTagsActionFactory(true));

View file

@ -1,11 +1,12 @@
import axios from 'axios'; import axios from 'axios';
import Bottle from 'bottlejs';
import { useStateFlagTimeout } from '../helpers/hooks'; import { useStateFlagTimeout } from '../helpers/hooks';
import Storage from './Storage'; import Storage from './Storage';
import ColorGenerator from './ColorGenerator'; import ColorGenerator from './ColorGenerator';
import buildShlinkApiClient from './ShlinkApiClientBuilder'; import buildShlinkApiClient from './ShlinkApiClientBuilder';
const provideServices = (bottle) => { const provideServices = (bottle: Bottle) => {
bottle.constant('localStorage', global.localStorage); bottle.constant('localStorage', (global as any).localStorage);
bottle.service('Storage', Storage, 'localStorage'); bottle.service('Storage', Storage, 'localStorage');
bottle.service('ColorGenerator', ColorGenerator, 'Storage'); bottle.service('ColorGenerator', ColorGenerator, 'Storage');

View file

@ -1,3 +1,4 @@
import Bottle from 'bottlejs';
import ShortUrlVisits from '../ShortUrlVisits'; import ShortUrlVisits from '../ShortUrlVisits';
import { cancelGetShortUrlVisits, getShortUrlVisits } from '../reducers/shortUrlVisits'; import { cancelGetShortUrlVisits, getShortUrlVisits } from '../reducers/shortUrlVisits';
import { getShortUrlDetail } from '../reducers/shortUrlDetail'; import { getShortUrlDetail } from '../reducers/shortUrlDetail';
@ -7,9 +8,10 @@ import VisitsStats from '../VisitsStats';
import { createNewVisit } from '../reducers/visitCreation'; import { createNewVisit } from '../reducers/visitCreation';
import { cancelGetTagVisits, getTagVisits } from '../reducers/tagVisits'; import { cancelGetTagVisits, getTagVisits } from '../reducers/tagVisits';
import TagVisits from '../TagVisits'; import TagVisits from '../TagVisits';
import { ConnectDecorator } from '../../container/types';
import * as visitsParser from './VisitsParser'; import * as visitsParser from './VisitsParser';
const provideServices = (bottle, connect) => { const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
// Components // Components
bottle.serviceFactory('OpenMapModalBtn', OpenMapModalBtn, 'MapModal'); bottle.serviceFactory('OpenMapModalBtn', OpenMapModalBtn, 'MapModal');
bottle.serviceFactory('MapModal', () => MapModal); bottle.serviceFactory('MapModal', () => MapModal);

View file

@ -17,12 +17,10 @@ describe('<Checkbox />', () => {
it('includes extra class names when provided', () => { it('includes extra class names when provided', () => {
const classNames = [ 'foo', 'bar', 'baz' ]; const classNames = [ 'foo', 'bar', 'baz' ];
const checked = false;
const onChange = () => {};
expect.assertions(classNames.length); expect.assertions(classNames.length);
classNames.forEach((className) => { classNames.forEach((className) => {
const wrapped = createComponent({ className, checked, onChange }); const wrapped = createComponent({ className });
expect(wrapped.prop('className')).toContain(className); expect(wrapped.prop('className')).toContain(className);
}); });
@ -30,11 +28,10 @@ describe('<Checkbox />', () => {
it('marks input as checked if defined', () => { it('marks input as checked if defined', () => {
const checkeds = [ true, false ]; const checkeds = [ true, false ];
const onChange = () => {};
expect.assertions(checkeds.length); expect.assertions(checkeds.length);
checkeds.forEach((checked) => { checkeds.forEach((checked) => {
const wrapped = createComponent({ checked, onChange }); const wrapped = createComponent({ checked });
const input = wrapped.find('input'); const input = wrapped.find('input');
expect(input.prop('checked')).toEqual(checked); expect(input.prop('checked')).toEqual(checked);
@ -43,12 +40,10 @@ describe('<Checkbox />', () => {
it('renders provided children inside the label', () => { it('renders provided children inside the label', () => {
const labels = [ 'foo', 'bar', 'baz' ]; const labels = [ 'foo', 'bar', 'baz' ];
const checked = false;
const onChange = () => {};
expect.assertions(labels.length); expect.assertions(labels.length);
labels.forEach((children) => { labels.forEach((children) => {
const wrapped = createComponent({ children, checked, onChange }); const wrapped = createComponent({ children });
const label = wrapped.find('label'); const label = wrapped.find('label');
expect(label.text()).toEqual(children); expect(label.text()).toEqual(children);