import { useStore } from 'effector-react'
import * as effector from 'effector'
import { combine } from 'effector'
import { pathOr } from 'ramda'
import React, { useLayoutEffect, useRef } from 'react'
import ReactDOM from 'react-dom'
import { chromeDark, ObjectInspector, ObjectLabel } from 'react-inspector'
import { throttle } from 'lodash'
import './effector-addon.css'
const trackCreateStore = effector.createEvent('trackCreateStore')
const trackCreateEvent = effector.createEvent('trackCreateEvent')
const trackCreateEffect = effector.createEvent('trackCreateEffect')
export function createStore(...params) {
const store = effector.createStore(...params)
process.env.NODE_ENV === 'development' && trackCreateStore({
store,
params,
})
return store
}
export function createEvent(...params) {
// console.log('createEvent', params)
const event = effector.createEvent(...params)
const name = params[0]
const file = params[1].loc.file
const module = file.split('/').slice(-1)[0].split('.')[0]
process.env.NODE_ENV === 'development' && trackCreateEvent({
event,
name,
module,
})
return event
}
export function createEffect(...params) {
// console.log('createEffect', params)
const effect = effector.createEffect(...params)
process.env.NODE_ENV === 'development' && trackCreateEffect({
effect,
params,
})
return effect
}
const toggleVisibility = effector.createEvent('toggleVisibility')
const visibility = effector.createStore(true)
.on(toggleVisibility, state => !state)
const $winParams = effector.createStoreObject({
visibility,
})
const getNewPromise = () => new Promise((resolve) => resolver = resolve)
let resolver = null
let sync = Promise.resolve()
const $eventMap = effector.createStore({})
.on(trackCreateEvent, (state, { event, name, module }) => {
event.watch(async () => {
await sync
sync = getNewPromise()
debouncedTimeSlice()
addEvent({ type: 'event', module, name, fullname: `${module}.${name}` })
resolver && resolver()
})
return {
...state,
[module]: [
...pathOr([], [module], state),
`${module}.${name}`,
],
}
})
const $storeMap = effector.createStore({})
.on(trackCreateStore, (state, { store, params }) => {
store.updates.watch(async () => {
await sync
sync = getNewPromise()
debouncedTimeSlice()
addEvent({ type: 'store', name: store.shortName })
resolver && resolver()
})
return {
...state,
[store.shortName]: store,
}
})
const addEvent = effector.createEvent()
const eventCall = addEvent.filter({
fn: (params) => params.type === 'event',
})
const timeSlice = effector.createEvent()
const debouncedTimeSlice = throttle(timeSlice, 1000, { leading: true, trailing: false })
let eventCounter = 0
const clearStack = effector.createEvent()
const $eventCallStack = effector.createStore([])
.reset(clearStack)
.on(addEvent, (state, event) => {
if (event.type === 'store') {
const lastEvent = state.slice(-1)[0]
if (lastEvent) {
return [...state.slice(0, -1), { ...lastEvent, store: event.name }]
}
}
return [
...state.slice(-100),
{ ...event, index: event.type === 'event' ? ++eventCounter : 0 },
]
})
.on(timeSlice, (state) => [
...state,
{ type: 'time', time: Date.now() },
])
const $eventCalls = effector.createStore({})
.reset(clearStack)
.on(eventCall, (state, { module, name }) => {
return ({
...state,
[`${module}.${name}`]: pathOr(0, [`${module}.${name}`], state) + 1,
})
})
const toggleEventFilter = effector.createEvent()
const toggleEventGroupFilter = effector.createEvent()
const $eventFilter = effector.createStore({})
.reset(clearStack)
.on(trackCreateEvent, (state, { event, name, module }) => ({
...state,
[module]: {
checked: pathOr(true, [module, 'checked'], state),
data: {
...pathOr({}, [module, 'data'], state),
[`${module}.${name}`]: true,
},
},
}))
.on(toggleEventFilter, (state, name) => {
const module = name.split('.')[0]
const checked = !pathOr(true, [module, 'data', name], state)
const newState = {
...state,
[module]: {
// checked: moduleChecked,
data: {
...pathOr({}, [module, 'data'], state),
[name]: checked,
},
},
}
const moduleChecked = checked
? Object.values(pathOr({}, [module, 'data'], newState)).every(item => item)
: false
newState[module].checked = moduleChecked
return newState
})
.on(toggleEventGroupFilter, (state, module) => {
const checked = !pathOr(true, [module, 'checked'], state)
return ({
...state,
[module]: {
checked,
data: Object.keys(pathOr({}, [module, 'data'], state))
.reduce((acc, item) => Object.assign(acc, { [item]: checked }), {}),
},
})
})
const $filteredCallStack = combine($eventCallStack, $eventFilter, (eventCallStack, eventFilter) => {
return eventCallStack.filter(event => {
if (event.type !== 'event') return event
return eventFilter[event.module].data[event.fullname]
})
})
function formatDate(date) {
let diff = new Date() - date // the difference in milliseconds
if (diff < 1000) { // less than 1 second
return 'right now'
}
let sec = Math.floor(diff / 1000) // convert diff to seconds
if (sec < 60) {
return sec + ' sec. ago'
}
let min = Math.floor(diff / 60000) // convert diff to minutes
if (min < 60) {
return min + ' min. ago'
}
// format the date
// add leading zeroes to single-digit day/month/hours/minutes
let d = date
d = [
'0' + d.getDate(),
'0' + (d.getMonth() + 1),
'' + d.getFullYear(),
'0' + d.getHours(),
'0' + d.getMinutes(),
].map(component => component.slice(-2)) // take last 2 digits of every component
// join the components into date
return d.slice(0, 3).join('.') + ' ' + d.slice(3).join(':')
}
const storeNodeRenderer = ({ depth, name, data, isNonenumerable, expanded }) => {
switch (depth) {
case 0:
return Store list
case 1:
return typeof data === 'object' || Array.isArray(data)
? {name}
: