diff --git a/src/core/client/framework/lib/bootstrap/createContext.tsx b/src/core/client/framework/lib/bootstrap/createContext.tsx index 07b69d945..e9f6dec89 100644 --- a/src/core/client/framework/lib/bootstrap/createContext.tsx +++ b/src/core/client/framework/lib/bootstrap/createContext.tsx @@ -114,7 +114,7 @@ export default async function createContext({ pym, eventEmitter, registerClickFarAway, - rest: new RestClient("/api"), + rest: new RestClient("/api", tokenGetter), postMessage: new PostMessageService(), localStorage: createLocalStorage(), sessionStorage: createSessionStorage(), diff --git a/src/core/client/framework/mutations/SetAuthTokenMutation.ts b/src/core/client/framework/mutations/SetAuthTokenMutation.ts index 223bc4054..90e520155 100644 --- a/src/core/client/framework/mutations/SetAuthTokenMutation.ts +++ b/src/core/client/framework/mutations/SetAuthTokenMutation.ts @@ -23,6 +23,8 @@ export async function commit( } else { localStorage.removeItem("authToken"); } + // Increment auth revision to indicate a change in auth state. + record.setValue(record.getValue("authRevision") + 1, "authRevision"); // Force gc to trigger. environment diff --git a/src/core/client/framework/mutations/SignOutMutation.ts b/src/core/client/framework/mutations/SignOutMutation.ts index 41d2a987c..2f170e32e 100644 --- a/src/core/client/framework/mutations/SignOutMutation.ts +++ b/src/core/client/framework/mutations/SignOutMutation.ts @@ -6,9 +6,13 @@ import { commit as setAuthToken } from "./SetAuthTokenMutation"; export type SignOutMutation = () => Promise; -export async function commit(environment: Environment, ctx: TalkContext) { - await setAuthToken(environment, { authToken: "" }, ctx); +export async function commit( + environment: Environment, + input: undefined, + ctx: TalkContext +) { await signOut(ctx.rest); + await setAuthToken(environment, { authToken: "" }, ctx); } export const withSignOutMutation = createMutationContainer("signOut", commit); diff --git a/src/core/client/stream/components/PostCommentForm.tsx b/src/core/client/stream/components/PostCommentForm.tsx index 2efb47786..5493519f5 100644 --- a/src/core/client/stream/components/PostCommentForm.tsx +++ b/src/core/client/stream/components/PostCommentForm.tsx @@ -13,7 +13,6 @@ interface FormProps { export interface PostCommentFormProps { onSubmit: OnSubmit; - signedIn: boolean; } const PostCommentForm: StatelessComponent = props => ( @@ -39,26 +38,19 @@ const PostCommentForm: StatelessComponent = props => ( )} - {props.signedIn ? ( -
- - - - -
- ) : ( - - )} +
+ + + + +
)} diff --git a/src/core/client/stream/components/Stream.tsx b/src/core/client/stream/components/Stream.tsx index a74435585..01f584d33 100644 --- a/src/core/client/stream/components/Stream.tsx +++ b/src/core/client/stream/components/Stream.tsx @@ -10,8 +10,6 @@ import ReplyListContainer from "../containers/ReplyListContainer"; import UserBoxContainer from "../containers/UserBoxContainer"; import * as styles from "./Stream.css"; -import { User } from "../containers/UserBoxContainer"; - export interface StreamProps { assetID: string; isClosed?: boolean; @@ -19,17 +17,14 @@ export interface StreamProps { onLoadMore?: () => void; hasMore?: boolean; disableLoadMore?: boolean; - user: User | null | undefined; + user: {} | null; } const Stream: StatelessComponent = props => { return ( - + { @@ -32,12 +31,7 @@ class PostCommentFormContainer extends Component { return undefined; }; public render() { - return ( - - ); + return ; } } diff --git a/src/core/client/stream/containers/StreamContainer.tsx b/src/core/client/stream/containers/StreamContainer.tsx index 4df5afaf5..d629c35d1 100644 --- a/src/core/client/stream/containers/StreamContainer.tsx +++ b/src/core/client/stream/containers/StreamContainer.tsx @@ -3,18 +3,18 @@ import { graphql, RelayPaginationProp } from "react-relay"; import { withPaginationContainer } from "talk-framework/lib/relay"; import { PropTypesOf } from "talk-framework/types"; -import { StreamContainer_asset as Data } from "talk-stream/__generated__/StreamContainer_asset.graphql"; +import { StreamContainer_asset as AssetData } from "talk-stream/__generated__/StreamContainer_asset.graphql"; +import { StreamContainer_user as UserData } from "talk-stream/__generated__/StreamContainer_user.graphql"; import { COMMENT_SORT, StreamContainerPaginationQueryVariables, } from "talk-stream/__generated__/StreamContainerPaginationQuery.graphql"; import Stream from "../components/Stream"; -import { User } from "../containers/UserBoxContainer"; interface InnerProps { - asset: Data; - user: User | null | undefined; + asset: AssetData; + user: UserData | null; relay: RelayPaginationProp; } @@ -64,7 +64,7 @@ interface FragmentVariables { } const enhanced = withPaginationContainer< - { asset: Data }, + { asset: AssetData; user: UserData | null }, InnerProps, FragmentVariables, StreamContainerPaginationQueryVariables @@ -91,6 +91,11 @@ const enhanced = withPaginationContainer< } } `, + user: graphql` + fragment StreamContainer_user on User { + ...UserBoxContainer_user + } + `, }, { direction: "forward", diff --git a/src/core/client/stream/containers/UserBoxContainer.tsx b/src/core/client/stream/containers/UserBoxContainer.tsx index 0ebad7cd9..cc05e3872 100644 --- a/src/core/client/stream/containers/UserBoxContainer.tsx +++ b/src/core/client/stream/containers/UserBoxContainer.tsx @@ -1,22 +1,23 @@ import * as React from "react"; import { Component } from "react"; + import { graphql, withFragmentContainer, withLocalStateContainer, } from "talk-framework/lib/relay"; +import { SignOutMutation, withSignOutMutation } from "talk-framework/mutations"; import { UserBoxContainer_user as UserData } from "talk-stream/__generated__/UserBoxContainer_user.graphql"; import { UserBoxContainerLocal as Local } from "talk-stream/__generated__/UserBoxContainerLocal.graphql"; +import UserBoxUnauthenticated from "talk-stream/components/UserBoxUnauthenticated"; import { SetAuthPopupStateMutation, ShowAuthPopupMutation, withSetAuthPopupStateMutation, withShowAuthPopupMutation, } from "talk-stream/mutations"; -import { SignOutMutation, withSignOutMutation } from "talk-framework/mutations"; import { Popup } from "talk-ui/components"; -import UserBoxUnauthenticated from "talk-stream/components/UserBoxUnauthenticated"; import UserBoxAuthenticated from "../components/UserBoxAuthenticated"; interface InnerProps { diff --git a/src/core/client/stream/local/initLocalState.ts b/src/core/client/stream/local/initLocalState.ts index 171c52294..81ba25702 100644 --- a/src/core/client/stream/local/initLocalState.ts +++ b/src/core/client/stream/local/initLocalState.ts @@ -33,6 +33,9 @@ export default async function initLocalState( // Set auth token localRecord.setValue(localStorage.getItem("authToken") || "", "authToken"); + // Set initial auth revision, this is increment whenenver auth state might have changed. + localRecord.setValue(0, "authRevision"); + // Parse query params const query = qs.parse(location.search); diff --git a/src/core/client/stream/local/local.graphql b/src/core/client/stream/local/local.graphql index 37426b867..9c2585ebf 100644 --- a/src/core/client/stream/local/local.graphql +++ b/src/core/client/stream/local/local.graphql @@ -22,6 +22,10 @@ type Local { commentID: String authPopup: AuthPopup! authToken: String + # Used to invalidate the `me` endpoint. + # This is incremented whenever the auth status + # might have changed. + authRevision: Int! } extend type Query { diff --git a/src/core/client/stream/queries/StreamQuery.tsx b/src/core/client/stream/queries/StreamQuery.tsx index 78c118cdd..41d262428 100644 --- a/src/core/client/stream/queries/StreamQuery.tsx +++ b/src/core/client/stream/queries/StreamQuery.tsx @@ -30,25 +30,25 @@ export const render = ({ error, props }: ReadyState) => { }; const StreamQuery: StatelessComponent = ({ - local: { assetID, authToken }, + local: { assetID, authRevision }, }) => ( query={graphql` - query StreamQuery($assetID: ID!, $signedIn: Boolean!) { + query StreamQuery($assetID: ID!, $authRevision: Int!) { asset(id: $assetID) { ...StreamContainer_asset } - me @include(if: $signedIn) { - id - username - displayName - role + # authRevision is increment every time auth state has changed. + # This is basically a cache invalidation and causes relay + # to automatically update this query. + me(clientAuthRevision: $authRevision) { + ...StreamContainer_user } } `} variables={{ assetID, - signedIn: !!authToken, + authRevision, }} render={render} /> @@ -58,7 +58,7 @@ const enhanced = withLocalStateContainer( graphql` fragment StreamQueryLocal on Local { assetID - authToken + authRevision } ` )(StreamQuery); diff --git a/src/core/server/graph/tenant/schema/schema.graphql b/src/core/server/graph/tenant/schema/schema.graphql index 1c96051bd..d51f4be16 100644 --- a/src/core/server/graph/tenant/schema/schema.graphql +++ b/src/core/server/graph/tenant/schema/schema.graphql @@ -769,8 +769,12 @@ type Query { """ me is the current logged in User. + + clientAuthRevision is an implementation detail that is only + used on the client to invalidate the cache. + TODO: This should move to a client side directive if this becomes possible. """ - me: User + me(clientAuthRevision: Int): User """ settings is the Settings for a given Tenant.