Skip to content

Commit

Permalink
Add typescript scaffold generators
Browse files Browse the repository at this point in the history
This commit exposes superglue's types and makes them
available to import. The generators are the first to
use these types, and using `rails g superglue:scaffold --typescript`
will generate a typescript version of the scaffold.
  • Loading branch information
jho406 committed Dec 11, 2024
1 parent dab6375 commit 4e8812c
Show file tree
Hide file tree
Showing 36 changed files with 1,474 additions and 314 deletions.
22 changes: 13 additions & 9 deletions superglue/lib/action_creators/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
RemoteCreator,
VisitCreator,
NavigationAction,
VisitMeta,
} from '../types'

function handleFetchErr(
Expand Down Expand Up @@ -205,17 +206,20 @@ to the same page.

const meta = buildMeta(pageKey, json, superglue, rsp, fetchArgs)

meta.navigationAction = calculateNavAction(
meta,
rsp,
isGet,
pageKey,
currentPageKey,
revisit
)
const visitMeta: VisitMeta = {
...meta,
navigationAction: calculateNavAction(
meta,
rsp,
isGet,
pageKey,
currentPageKey,
revisit
),
}

const page = beforeSave(pages[pageKey], json)
return dispatch(saveAndProcessPage(pageKey, page)).then(() => meta)
return dispatch(saveAndProcessPage(pageKey, page)).then(() => visitMeta)
})
.catch((e) => handleFetchErr(e, fetchArgs, dispatch))
}
Expand Down
3 changes: 2 additions & 1 deletion superglue/lib/components/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import React, {
useState,
forwardRef,
useImperativeHandle,
ForwardedRef,
} from 'react'
import { urlToPageKey, pathWithoutBZParams } from '../utils'
import { removePage, historyChange } from '../actions'
Expand Down Expand Up @@ -44,7 +45,7 @@ const notFound = (identifier: string | undefined): never => {

const NavigationProvider = forwardRef(function NavigationProvider(
{ history, visit, remote, initialPageKey, mapping }: NavigationProviderProps,
ref
ref: ForwardedRef<{ navigateTo: NavigateTo }>
) {
const [activePage, setActivePage] = useState({
pageKey: initialPageKey,
Expand Down
15 changes: 12 additions & 3 deletions superglue/lib/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useSelector } from 'react-redux'
import { Page, RootState, SuperglueState } from '../types'
import { JSONMappable, Page, RootState, SuperglueState } from '../types'

/**
* A lightweight hook that grabs the superglue state from the store.
Expand All @@ -11,9 +11,18 @@ export function useSuperglue() {
/**
* A lightweight hook that grabs the current page from the store.
*/
export function usePage() {
export function usePage<T = JSONMappable>() {
const superglueState = useSuperglue()
const currentPageKey = superglueState.currentPageKey

return useSelector<RootState, Page>((state) => state.pages[currentPageKey])
return useSelector<RootState<T>, Page<T>>(
(state) => state.pages[currentPageKey]
)
}

/**
* A lightweight hook that grabs the current page from the store.
*/
export function useContent<T = JSONMappable>() {
return usePage<T>().data
}
156 changes: 75 additions & 81 deletions superglue/lib/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import React, { useEffect, forwardRef, useImperativeHandle } from 'react'
import React, {
useEffect,
forwardRef,
useRef,
useImperativeHandle,
} from 'react'
import parse from 'url-parse'
import { rootReducer } from './reducers'
import { config } from './config'
import { urlToPageKey, ujsHandlers, argsForHistory } from './utils'
import { saveAndProcessPage } from './action_creators'
Expand All @@ -12,7 +16,6 @@ import { History, createBrowserHistory, createMemoryHistory } from 'history'
import { NavigationProvider } from './components/Navigation'
export { NavigationProvider, NavigationContext } from './components/Navigation'
export { saveAndProcessPage } from './action_creators'

export {
beforeFetch,
beforeVisit,
Expand All @@ -24,74 +27,24 @@ export {
GRAFTING_ERROR,
GRAFTING_SUCCESS,
} from './actions'
export * from './types'

import { mapStateToProps, mapDispatchToProps } from './utils/react'
import {
SuperglueStore,
VisitResponse,
Page,
buildVisitAndRemote,
buildStore,
BuildVisitAndRemote,
ConnectedMapping,
ApplicationProps,
NavigateTo,
SuperglueStore,
} from './types'
export { superglueReducer, pageReducer, rootReducer } from './reducers'
export { getIn } from './utils/immutability'
export { urlToPageKey }
export { usePage, useSuperglue } from './hooks'
export * from './hooks'

const hasWindow = typeof window !== 'undefined'

function pageToInitialState(key: string, page: VisitResponse) {
const slices = page.slices || {}
const nextPage: Page = {
...page,
pageKey: key, //TODO remove this
savedAt: Date.now(),
}

return {
pages: { [key]: nextPage },
...slices,
}
}

function populateStore({
initialPage,
baseUrl = config.baseUrl,
maxPages = config.maxPages,
path,
}: {
initialPage: VisitResponse
baseUrl: string
maxPages?: number
path: string
}) {
const initialPageKey = urlToPageKey(parse(path).href)
const { csrfToken } = initialPage
const location = parse(path)

config.baseUrl = baseUrl
config.maxPages = maxPages

return {
reducer: rootReducer,
prepareStore: function (store: SuperglueStore) {
store.dispatch(
historyChange({
pathname: location.pathname,
search: location.query,
hash: location.hash,
})
)
store.dispatch(saveAndProcessPage(initialPageKey, initialPage))
store.dispatch(setCSRFToken({ csrfToken }))
},
initialState: pageToInitialState(initialPageKey, initialPage),
initialPageKey,
}
}

const createHistory = () => {
if (hasWindow) {
// This is used for client side rendering
Expand All @@ -102,31 +55,71 @@ const createHistory = () => {
}
}

export const prepareStore = (
store: SuperglueStore,
initialPage: VisitResponse
) => {
const location = window.location
const initialPageKey = urlToPageKey(location.href) // pass this
const { csrfToken } = initialPage

store.dispatch(
historyChange({
pathname: location.pathname,
search: location.search,
hash: location.hash,
})
)
store.dispatch(saveAndProcessPage(initialPageKey, initialPage))
store.dispatch(setCSRFToken({ csrfToken }))
}

export function start(
initialPage: VisitResponse,
// initialPage: VisitResponse,
baseUrl: string,
path: string,
buildStore: buildStore,
store: SuperglueStore,
mapping: Record<string, React.ComponentType>,
buildVisitAndRemote: buildVisitAndRemote,
navigatorRef: React.RefObject<typeof NavigationProvider>,
buildVisitAndRemote: BuildVisitAndRemote,
navigatorRef: React.RefObject<{ navigateTo: NavigateTo }>,
history: History
) {
const { prepareStore, initialState, initialPageKey, reducer } = populateStore(
{
initialPage,
baseUrl,
path,
// The max number of pages to keep in the store. Default is 20
// maxPages: 20
}
)
const initialPageKey = urlToPageKey(parse(path).href)
// const { csrfToken } = initialPage
// const location = parse(path)

const store = buildStore(initialState, reducer)
// Fire initial events and populate the store
prepareStore(store)
config.baseUrl = baseUrl
// const reducer = rootReducer

// const pathname = location.pathname
// const search = location.query
// const hash = location.hash
// const currentPageKey = urlToPageKey(pathname + search)
// const initialSuperglueState = {
// pathname,
// currentPageKey,
// search,
// hash,
// assets: initialPage.assets,
// csrfToken
// }

// const initialState : InitialState = {
// pages: {
// [initialPageKey]: {
// ...initialPage,
// pageKey: initialPageKey,
// savedAt: Date.now()
// }
// },
// superglue: initialSuperglueState,
// ...initialPage.slices
// }

// Fire initial events
// const store = buildStore(initialState, reducer)
// store.dispatch(saveAndProcessPage(initialPageKey, initialPage))

// const history = createHistory()
history.replace(...argsForHistory(path))

const unconnectedMapping = mapping
Expand All @@ -140,7 +133,7 @@ export function start(
const { visit, remote } = buildVisitAndRemote(navigatorRef, store)

return {
store,
// store,
visit,
remote,
connectedMapping,
Expand All @@ -161,23 +154,24 @@ const Application = forwardRef(function Application(
initialPage,
baseUrl,
path,
buildStore,
store,
buildVisitAndRemote,
history,
mapping,
appEl,
}: ApplicationProps,
ref
) {
const navigatorRef = React.createRef<typeof NavigationProvider>()
const navigatorRef = useRef<{ navigateTo: NavigateTo }>(null)

history = history || createHistory()
prepareStore(store, initialPage)

const { store, visit, remote, connectedMapping, initialPageKey } = start(
initialPage,
const { visit, remote, connectedMapping, initialPageKey } = start(
// initialPage,
baseUrl,
path,
buildStore,
store,
mapping,
buildVisitAndRemote,
navigatorRef,
Expand Down
10 changes: 8 additions & 2 deletions superglue/lib/reducers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,15 @@ export function pageReducer(state: AllPages = {}, action: Action): AllPages {
}

export function superglueReducer(
state: Partial<SuperglueState> = {},
state: SuperglueState = {
pathname: '',
currentPageKey: '',
search: '',
hash: '',
assets: [],
},
action: Action
): Partial<SuperglueState> {
): SuperglueState {
if (setCSRFToken.match(action)) {
const { csrfToken } = action.payload
return { ...state, csrfToken: csrfToken }
Expand Down
Loading

0 comments on commit 4e8812c

Please sign in to comment.