1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-11-21 23:00:50 +03:00
Files
authentication-service/frontend/src/atoms.ts
Quentin Gliech f67cc0d6d0 Frontend cleanups
Mainly:

 - better handling of GraphQL errors
 - better logout state
 - dependencies update
 - a way to end browser sessions in the GraphQL API
2023-06-20 16:53:53 +02:00

124 lines
3.1 KiB
TypeScript

// Copyright 2023 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { AnyVariables, CombinedError, OperationContext } from "@urql/core";
import { atom, WritableAtom } from "jotai";
import { useHydrateAtoms } from "jotai/utils";
import { AtomWithQuery, atomWithQuery, clientAtom } from "jotai-urql";
import type { ReactElement } from "react";
import { graphql } from "./gql";
import { client } from "./graphql";
import { err, ok, Result } from "./result";
export type GqlResult<T> = Result<T, CombinedError>;
export type GqlAtom<T> = WritableAtom<
Promise<GqlResult<T>>,
[context?: Partial<OperationContext>],
void
>;
/**
* Map the result of a query atom to a new value, making it a GqlResult
*
* @param queryAtom: An atom got from atomWithQuery
* @param mapper: A function that takes the data from the query and returns a new value
*/
export const mapQueryAtom = <Data, Variables extends AnyVariables, NewData>(
queryAtom: AtomWithQuery<Data, Variables>,
mapper: (data: Data) => NewData
): GqlAtom<NewData> => {
return atom(
async (get): Promise<GqlResult<NewData>> => {
const result = await get(queryAtom);
if (result.error) {
return err(result.error);
}
if (result.data === undefined) {
throw new Error("Query result is undefined");
}
return ok(mapper(result.data));
},
(_get, set, context) => {
set(queryAtom, context);
}
);
};
export const HydrateAtoms: React.FC<{ children: ReactElement }> = ({
children,
}) => {
useHydrateAtoms([[clientAtom, client]]);
return children;
};
const CURRENT_VIEWER_QUERY = graphql(/* GraphQL */ `
query CurrentViewerQuery {
viewer {
__typename
... on User {
id
}
... on Anonymous {
id
}
}
}
`);
const currentViewerAtom = atomWithQuery({ query: CURRENT_VIEWER_QUERY });
export const currentUserIdAtom: GqlAtom<string | null> = mapQueryAtom(
currentViewerAtom,
(data) => {
if (data.viewer.__typename === "User") {
return data.viewer.id;
}
return null;
}
);
const CURRENT_VIEWER_SESSION_QUERY = graphql(/* GraphQL */ `
query CurrentViewerSessionQuery {
viewerSession {
__typename
... on BrowserSession {
id
}
... on Anonymous {
id
}
}
}
`);
const currentViewerSessionAtom = atomWithQuery({
query: CURRENT_VIEWER_SESSION_QUERY,
});
export const currentBrowserSessionIdAtom: GqlAtom<string | null> = mapQueryAtom(
currentViewerSessionAtom,
(data) => {
if (data.viewerSession.__typename === "BrowserSession") {
return data.viewerSession.id;
}
return null;
}
);