b69ff951 by Adam Heath

WIP

1 parent 2c7e9de3
{
"name": "astro-redux",
"type": "module",
"exports": {
".": "./src/index.mjs",
"./client": "./src/client.mjs",
"./slices": "./src/slices.mjs",
"./react": "./src/react.jsx"
}
}
---
const { locals: { store, session } } = Astro
const getPreloadedState = async () => {
//await getAllAsync(store.dispatch)
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(store.getState())
}, 0)
})
}
---
<script define:vars={{preloadedState: await getPreloadedState()}}>
window.__PRELOADED_STATE__ = preloadedState
console.log('window', window)
</script>
import { configureStore } from '@reduxjs/toolkit'
import { setupListeners } from '@reduxjs/toolkit/query'
export function getBrowserStore() {
const {
astroStore: store
} = window
return store
}
export async function configureAstroStore(userStoreConfig) {
const {
__PRELOADED_STATE__: preloadedState,
} = window
console.log('preloadedState', preloadedState)
if (preloadedState) {
const store = configureStore({
...userStoreConfig,
preloadedState,
})
await setupListeners(store.dispatch)
//await resetApiState(store.dispatch)
console.log('store', store)
window.astroStore = store
}
}
import NodeCache from 'node-cache'
import { configureStore } from '@reduxjs/toolkit'
export const makeLeadingCase = (s) => s.substring(0, 1).toUpperCase() + s.substring(1)
export const fixUnsubscribe = async (store, action) => {
const promise = store.dispatch(action)
try {
return await promise
} finally {
if (promise.unsubscribe) promise.unsubscribe()
}
}
export const DefaultEndpointOptions = Symbol.for('DefaultEndpointOptions')
export const createApiWrappers = (apiSlice, options = {}) => {
const { util: { upsertQueryData } } = apiSlice
const { prefix, endpoints: endpointOptions = {} } = options
const { [ DefaultEndpointOptions ]: defaultEndpointOptions = {} } = endpointOptions
return Object.entries(apiSlice.endpoints).reduce((result, [ endpointName, endpoint ]) => {
const { initiate } = endpoint
const methodName = prefix ? `${prefix}${makeLeadingCase(endpointName)}` : endpointName
const { [ endpointName ]: { forceRefetch = false } = defaultEndpointOptions } = endpointOptions
result.endpoints[ methodName ] = (store, arg) => {
return fixUnsubscribe(store, initiate(arg, { forceRefetch, subscribe: true }))
}
result.upserts[ methodName ] = (store, arg, data) => {
return fixUnsubscribe(store, upsertQueryData(endpointName, arg, data))
}
return result
}, { endpoints: {}, upserts: {} })
}
export const createAstroApiWrappers = (apiWrappers) => Object.entries(apiWrappers).reduce((result, [ apiType, methods ]) => {
result[ apiType ] = Object.entries(methods).reduce((methods, [ methodName, method ]) => {
methods[ methodName ] = (Astro, ...args) => method(Astro.locals.store, ...args)
return methods
}, {})
return result
}, {})
export const sessionStoreCache = new NodeCache({
stdTTL: 600, // in seconds
useClones: false,
})
export const createGetSessionStore = (storeConfig) => (session) => {
const getStore = (sessionId) => {
const currentStore = sessionStoreCache.get(sessionId)
if (currentStore) return currentStore
const newStore = configureStore(storeConfig)
sessionStoreCache.set(sessionId, newStore)
return newStore
}
const { id: sessionId } = session
return getStore(sessionId)
}
export { default as AstroReduxProvider } from './Provider.astro'
import React from 'react'
import { Provider } from 'react-redux'
import { getBrowserStore } from './client.mjs'
export const ReduxAstroProvider = (Component) => (props) => {
const {
ASTRO_STORE: possibleStore = {},
...rest
} = props
const store = possibleStore.dispatch ? possibleStore : getBrowserStore()
return (
<Provider store={store}><Component {...rest}/></Provider>
)
}
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
export const loginPrepareHeaders = (headers, context) => {
const { getState } = context
const state = getState()
const { data: { access_token } = {} } = loginApiSlice.endpoints.getToken.select()(state)
if (access_token) {
headers.set('authorization', `Bearer ${access_token}`)
}
return headers
}
export const loginApiSlice = createApi({
reducerPath: 'login',
tagTypes: ['Token'],
keepUnusedDataFor: 60,
baseQuery: fetchBaseQuery({
baseUrl: '/login',
}),
refetchOnReconnect: true,
endpoints: builder => ({
getToken: builder.query({
query: () => ({
url: '/token?refresh',
method: 'GET',
}),
providesTags(result, error, arg) {
const { expires_at, access_token } = result
return ['Token']
},
}),
}),
})
export const { useGetTokenQuery } = loginApiSlice
export const invalidateToken = (dispatch) => dispatch(loginApiSlice.util.invalidateTags(['Token']))