Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ce8f06175 | ||
|
|
86b7af6b20 | ||
|
|
35b318aea0 | ||
|
|
198535e16f | ||
|
|
003230bba7 | ||
|
|
65facf9b4c | ||
|
|
10b12300ca | ||
|
|
77522ae674 | ||
|
|
04c21f398b | ||
|
|
2b5ed7a66e | ||
|
|
6f84ed8b4f | ||
|
|
a41acbe93d | ||
|
|
1f03e73db1 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -25,3 +25,5 @@ dist
|
||||
.tsbuildinfo
|
||||
.nvmrc
|
||||
**/.test*
|
||||
examples/auth2
|
||||
.idea
|
||||
|
||||
@@ -2,7 +2,7 @@ import {useSession, useRouter, useMutation, Head} from "blitz"
|
||||
import logout from "app/auth/mutations/logout"
|
||||
|
||||
export default function Layout({title, children}: {title?: string; children: React.ReactNode}) {
|
||||
const session = useSession()
|
||||
const session = useSession({suspense: false})
|
||||
const router = useRouter()
|
||||
const [logoutMutation] = useMutation(logout)
|
||||
return (
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
import {render} from "test/utils"
|
||||
|
||||
import Home from "./index"
|
||||
import {useCurrentUser} from "app/core/hooks/useCurrentUser"
|
||||
|
||||
jest.mock("app/core/hooks/useCurrentUser")
|
||||
const mockUseCurrentUser = useCurrentUser as jest.MockedFunction<typeof useCurrentUser>
|
||||
jest.mock("blitz", () => ({
|
||||
...jest.requireActual("blitz")!,
|
||||
useQuery: () => [
|
||||
{
|
||||
id: 1,
|
||||
name: "User",
|
||||
email: "user@email.com",
|
||||
role: "user",
|
||||
},
|
||||
],
|
||||
}))
|
||||
|
||||
test("renders blitz documentation link", () => {
|
||||
// This is an example of how to ensure a specific item is in the document
|
||||
@@ -12,12 +19,6 @@ test("renders blitz documentation link", () => {
|
||||
// when you remove the the default content from the page
|
||||
|
||||
// This is an example on how to mock api hooks when testing
|
||||
mockUseCurrentUser.mockReturnValue({
|
||||
id: 1,
|
||||
name: "User",
|
||||
email: "user@email.com",
|
||||
role: "user",
|
||||
})
|
||||
|
||||
const {getByText} = render(<Home />)
|
||||
const element = getByText(/powered by blitz/i)
|
||||
|
||||
@@ -1,30 +1,21 @@
|
||||
import {Suspense} from "react"
|
||||
import {Head, Link, useSession, useRouterQuery, useMutation, invoke} from "blitz"
|
||||
import {Head, Link, useSession, useRouterQuery, useMutation, invoke, useQuery} from "blitz"
|
||||
import getUser from "app/users/queries/getUser"
|
||||
import trackView from "app/users/mutations/trackView"
|
||||
import Layout from "app/core/layouts/Layout"
|
||||
import {useCurrentUser} from "app/core/hooks/useCurrentUser"
|
||||
// import getUsers from "app/users/queries/getUsers"
|
||||
|
||||
const CurrentUserInfo = () => {
|
||||
const currentUser = useCurrentUser()
|
||||
const session = useSession()
|
||||
const [currentUser] = useQuery(getUser, {where: {id: session.userId}})
|
||||
|
||||
return <pre>{JSON.stringify(currentUser, null, 2)}</pre>
|
||||
}
|
||||
|
||||
// const Users = () => {
|
||||
// const [users] = useQuery(getUsers, {})
|
||||
//
|
||||
// return <pre style={{maxWidth: "30rem"}}>{JSON.stringify(users, null, 2)}</pre>
|
||||
// }
|
||||
|
||||
const UserStuff = () => {
|
||||
const session = useSession()
|
||||
const query = useRouterQuery()
|
||||
const [trackViewMutation] = useMutation(trackView)
|
||||
|
||||
if (session.isLoading) return <div>Loading...</div>
|
||||
|
||||
return (
|
||||
<div>
|
||||
{!session.userId && (
|
||||
@@ -48,11 +39,6 @@ const UserStuff = () => {
|
||||
<Suspense fallback="Loading...">
|
||||
<CurrentUserInfo />
|
||||
</Suspense>
|
||||
{/*
|
||||
<Suspense fallback="Loading...">
|
||||
<Users />
|
||||
</Suspense>
|
||||
*/}
|
||||
<button
|
||||
onClick={async () => {
|
||||
try {
|
||||
@@ -94,7 +80,9 @@ const Home = () => (
|
||||
<img src="/logo.png" alt="blitz.js" />
|
||||
</div>
|
||||
|
||||
<UserStuff />
|
||||
<Suspense fallback={"Loading..."}>
|
||||
<UserStuff />
|
||||
</Suspense>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
|
||||
@@ -6,7 +6,7 @@ type GetUserInput = {
|
||||
}
|
||||
|
||||
export default async function getUser({where}: GetUserInput, ctx: Ctx) {
|
||||
ctx.session.$authorize()
|
||||
if (!ctx.session.userId) return null
|
||||
|
||||
const user = await db.user.findFirst({where})
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ module.exports = withBundleAnalyzer({
|
||||
middleware: [
|
||||
sessionMiddleware({
|
||||
isAuthorized: simpleRolesIsAuthorized,
|
||||
sessionExpiryMinutes: 4,
|
||||
// sessionExpiryMinutes: 4,
|
||||
}),
|
||||
],
|
||||
log: {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"cy:run": "cypress run || cypress run",
|
||||
"test": "prisma generate && yarn test:jest && yarn test:e2e",
|
||||
"test:jest": "jest",
|
||||
"test:server": "blitz prisma migrate deploy --preview-feature && blitz build && blitz start -p 3099",
|
||||
"test:server": "cross-env NODE_ENV=test blitz prisma migrate deploy --preview-feature && blitz build && cross-env NODE_ENV=test blitz start -p 3099",
|
||||
"test:e2e": "cross-env NODE_ENV=test start-server-and-test test:server http://localhost:3099 cy:run"
|
||||
},
|
||||
"browserslist": [
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import {useState} from "react"
|
||||
import {useEffect, useState} from "react"
|
||||
import {getBlitzRuntimeData} from "./blitz-data"
|
||||
import {COOKIE_CSRF_TOKEN} from "./constants"
|
||||
import {Ctx} from "./middleware"
|
||||
import {publicDataStore} from "./public-data-store"
|
||||
import {IsAuthorizedArgs, PublicData} from "./types"
|
||||
import {isServer} from "./utils"
|
||||
import {readCookie} from "./utils/cookie"
|
||||
import {useIsomorphicLayoutEffect} from "./utils/hooks"
|
||||
|
||||
export interface SessionModel extends Record<any, any> {
|
||||
handle: string
|
||||
@@ -63,21 +64,35 @@ export interface PublicDataWithLoading extends PublicData {
|
||||
|
||||
interface UseSessionOptions {
|
||||
initialPublicData?: PublicData
|
||||
suspense?: boolean
|
||||
}
|
||||
|
||||
export const useSession = (options: UseSessionOptions = {}): PublicDataWithLoading => {
|
||||
const [publicData, setPublicData] = useState(
|
||||
options.initialPublicData ?? publicDataStore.emptyPublicData,
|
||||
)
|
||||
const [isLoading, setIsLoading] = useState(!options.initialPublicData)
|
||||
const suspense = options?.suspense ?? getBlitzRuntimeData().suspenseEnabled
|
||||
|
||||
useIsomorphicLayoutEffect(() => {
|
||||
let initialState: PublicDataWithLoading
|
||||
if (options.initialPublicData) {
|
||||
initialState = {...options.initialPublicData, isLoading: false}
|
||||
} else if (suspense) {
|
||||
if (isServer) {
|
||||
throw new Promise((_) => {})
|
||||
} else {
|
||||
initialState = {...publicDataStore.getData(), isLoading: false}
|
||||
}
|
||||
} else {
|
||||
initialState = {...publicDataStore.emptyPublicData, isLoading: true}
|
||||
}
|
||||
|
||||
const [session, setSession] = useState(initialState)
|
||||
|
||||
useEffect(() => {
|
||||
// Initialize on mount
|
||||
setPublicData(publicDataStore.getData())
|
||||
setIsLoading(false)
|
||||
const subscription = publicDataStore.observable.subscribe(setPublicData)
|
||||
setSession({...publicDataStore.getData(), isLoading: false})
|
||||
const subscription = publicDataStore.observable.subscribe((data) =>
|
||||
setSession({...data, isLoading: false}),
|
||||
)
|
||||
return subscription.unsubscribe
|
||||
}, [])
|
||||
|
||||
return {...publicData, isLoading}
|
||||
return session
|
||||
}
|
||||
|
||||
26
packages/core/src/suspense.ts
Normal file
26
packages/core/src/suspense.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
export const suspend = <T>(promise: Promise<T>) => {
|
||||
let result: any
|
||||
let status = "pending"
|
||||
|
||||
const suspender = promise.then(
|
||||
(response) => {
|
||||
status = "success"
|
||||
result = response
|
||||
},
|
||||
(error) => {
|
||||
status = "error"
|
||||
result = error
|
||||
},
|
||||
)
|
||||
|
||||
return (): T => {
|
||||
switch (status) {
|
||||
case "pending":
|
||||
throw suspender
|
||||
case "error":
|
||||
throw result
|
||||
default:
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,10 +34,7 @@ export function useQuery<T extends QueryFn, TResult = PromiseReturnType<T>>(
|
||||
throw new Error("useQuery is missing the first argument - it must be a query function")
|
||||
}
|
||||
|
||||
// TODO - useSession here is a tempory fix for logout query invalidation until RQ v3
|
||||
// https://github.com/blitz-js/blitz/issues/1711
|
||||
// NOTE: bug did not present in local dev build. Only via npm bundle
|
||||
useSession()
|
||||
const session = useSession({suspense: options?.suspense})
|
||||
const routerIsReady = useRouter().isReady
|
||||
const enhancedResolverRpcClient = sanitize(queryFn)
|
||||
const queryKey = getQueryKey(queryFn, params)
|
||||
@@ -51,6 +48,7 @@ export function useQuery<T extends QueryFn, TResult = PromiseReturnType<T>>(
|
||||
config: {
|
||||
...useDefaultQueryConfig(),
|
||||
...options,
|
||||
enabled: options?.enabled ?? !session.isLoading,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -77,8 +75,7 @@ export function usePaginatedQuery<T extends QueryFn, TResult = PromiseReturnType
|
||||
throw new Error("usePaginatedQuery is missing the first argument - it must be a query function")
|
||||
}
|
||||
|
||||
// TODO - useSession here is a tempory fix for logout query invalidation until RQ v3
|
||||
useSession()
|
||||
const session = useSession({suspense: options?.suspense})
|
||||
const routerIsReady = useRouter().isReady
|
||||
const enhancedResolverRpcClient = sanitize(queryFn)
|
||||
const queryKey = getQueryKey(queryFn, params)
|
||||
@@ -92,6 +89,7 @@ export function usePaginatedQuery<T extends QueryFn, TResult = PromiseReturnType
|
||||
config: {
|
||||
...useDefaultQueryConfig(),
|
||||
...options,
|
||||
enabled: options?.enabled ?? !session.isLoading,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -132,8 +130,7 @@ export function useInfiniteQuery<
|
||||
throw new Error("useInfiniteQuery is missing the first argument - it must be a query function")
|
||||
}
|
||||
|
||||
// TODO - useSession here is a tempory fix for logout query invalidation until RQ v3
|
||||
useSession()
|
||||
const session = useSession({suspense: options?.suspense})
|
||||
const routerIsReady = useRouter().isReady
|
||||
const enhancedResolverRpcClient = sanitize(queryFn)
|
||||
const queryKey = getQueryKey(queryFn, params)
|
||||
@@ -150,6 +147,7 @@ export function useInfiniteQuery<
|
||||
config: {
|
||||
...useDefaultQueryConfig(),
|
||||
...options,
|
||||
enabled: options?.enabled ?? !session.isLoading,
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user