All Downloads are FREE. Search and download functionalities are using the official Maven repository.

sagas.meta.ts Maven / Gradle / Ivy

The newest version!
import {
    put,
    select,
    takeEvery,
    cancel,
    delay,
} from 'redux-saga/effects'
import { push } from 'connected-react-router'
import isArray from 'lodash/isArray'
import get from 'lodash/get'
import isEmpty from 'lodash/isEmpty'

import { insertDialog, destroyOverlays } from '../ducks/overlays/store'
import { id } from '../utils/id'
import { CALL_ALERT_META } from '../constants/meta'
import { dataProviderResolver } from '../core/dataProviderResolver'
import { addAlert, addMultiAlerts } from '../ducks/alerts/store'
import { CLOSE_BUTTON_PATH, DEFAULT_CLOSE_BUTTON, GLOBAL_KEY, STORE_KEY_PATH, PLACEMENT } from '../ducks/alerts/constants'
import { removeAllModel } from '../ducks/models/store'
import { register } from '../ducks/datasource/store'
import { requestConfigSuccess } from '../ducks/global/store'
import { State } from '../ducks/State'
import { Alert } from '../ducks/alerts/Alerts'

type MessagesType = Alert[]
const mapMessage = (message: Alert) => ({ ...message, id: message?.id || id() })

type AccType = Record | Record
const separateMessagesByPlacement = (
    messages: MessagesType,
): Record => messages.reduce((acc: AccType, message) => {
    if (!acc[message.placement]) {
        acc[message.placement] = []
    }

    acc[message.placement].unshift(message)

    return acc
}, {})

interface AlertEffect {
    meta: {
        alert: {
            messages: MessagesType
        }
    }
}

/* TODO избавиться от alertEffect
    для этого бэку нужно присылать
    структуру как в alert action
    { payload: { key: 'key', alerts: [...] }, type: 'type'}
    alertEffect - обрабатывает alert.meta server responses
    маппит id и выполняет redux action */
export function* alertEffect(action: AlertEffect) {
    // FIXME костыльная задержка для автотестов, которые смотрят на алерт инвока как на то что последующее инвоку обновление виджета было выполнено
    yield delay(300)
    try {
        const { messages } = action.meta.alert

        const alerts = isArray(messages)
            ? messages.map(mapMessage)
            : [mapMessage(messages)]

        if (!alerts?.length) { return }

        if (alerts.length === 1) {
            const [alert] = alerts
            const placement = get(alert, STORE_KEY_PATH, GLOBAL_KEY)
            const closeButton = get(alert, CLOSE_BUTTON_PATH, DEFAULT_CLOSE_BUTTON)

            yield put(addAlert(placement, { ...alert, placement, closeButton }))

            yield cancel()
        }

        const separatedAlerts = separateMessagesByPlacement(alerts)
        // @ts-ignore FIXME не знаю как поправить
        const placements: PLACEMENT[] = Object.keys(separatedAlerts)

        for (const placement of placements) {
            const alerts: MessagesType = separatedAlerts[placement]

            yield put(addMultiAlerts(placement, alerts))
        }
    } catch (e) {
        // eslint-disable-next-line no-console
        console.error(e)
    }
}

interface ClearOnSuccessEffect {
    meta: {
        clear?: string
    }
}

export function* clearOnSuccessEffect(action: ClearOnSuccessEffect) {
    const { meta } = action
    const datasourceId = get(meta, 'clear', null)

    if (!datasourceId) { return }

    yield put(removeAllModel(datasourceId))
}

interface Redirect {
    path: string
    pathMapping: Record
    queryMapping: Record
    target: string
}
interface RedirectEffect {
    meta: {
        clear?: string,
        modalsToClose?: number
        redirect: Redirect
    }
}

export function* redirectEffect(action: RedirectEffect) {
    try {
        const { path, pathMapping, queryMapping, target } = action.meta.redirect

        const state: State = yield select()

        // @ts-ignore import from js file
        const { url: newUrl } = dataProviderResolver(state, { url: path, pathMapping, queryMapping })

        if (target === 'application') {
            yield clearOnSuccessEffect(action)
            yield put(push(newUrl))
            yield put(destroyOverlays(action.meta.modalsToClose))
        } else if (target === 'self') {
            // @ts-ignore import from js file
            window.location = newUrl
        } else {
            window.open(newUrl)
        }
    } catch (e) {
        // eslint-disable-next-line no-console
        console.error(e)
    }
}

interface UserDialogEffect {
    dialog: {
        title: string
        description: string
        toolbar: Record
    }
}

export function* userDialogEffect({ meta }: { meta: UserDialogEffect }) {
    const { title, description, toolbar, ...rest } = meta.dialog

    yield put(
        insertDialog('dialog', true, 'dialog', {
            title,
            description,
            toolbar,
            ...rest,
        }),
    )
}

interface ClearEffect {
    meta: {
        clear: string
        redirect: Redirect
    }
}
export function* clearEffect(action: ClearEffect) {
    const { meta } = action

    const redirect = get(meta, 'redirect', null)

    if (redirect) {
        return
    }

    yield clearOnSuccessEffect(action)
}

interface DataSourcesRegister {
    payload: {
        menu: {
            datasources: string[]
        }
    }
}
function* dataSourcesRegister(action: DataSourcesRegister) {
    const datasources = get(action, 'payload.menu.datasources')

    if (isEmpty(datasources)) {
        yield cancel()
    }

    const entries = Object.entries(datasources)

    for (const [id, config] of entries) {
        // @ts-ignore FIXME не знаю как поправить
        yield put(register(id, config))
    }
}

export const metaSagas = [
    // @ts-ignore проблемы с типизацией saga
    takeEvery([action => action?.meta && action.meta.alert, CALL_ALERT_META], alertEffect),
    // @ts-ignore проблемы с типизацией saga
    takeEvery((action: { meta: { redirect: IRedirect } }) => action?.meta && action.meta.redirect, redirectEffect),
    // @ts-ignore проблемы с типизацией saga
    takeEvery((action: { meta: IUserDialogEffect }) => action?.meta && action.meta.dialog, userDialogEffect),
    takeEvery(requestConfigSuccess, dataSourcesRegister),
    // @ts-ignore проблемы с типизацией saga
    takeEvery((action: { meta: { clear: string } }) => action.meta?.clear, clearEffect),
]




© 2015 - 2024 Weber Informatics LLC | Privacy Policy