mirror of
https://github.com/wassname/talk.git
synced 2026-07-09 05:19:33 +08:00
[CORL-420] Upgrade Relay (#2346)
* chore: upgrade Relay * fix: fix errors * fix: snapshot * fix: relay prefix * fix: fragment spec error
This commit is contained in:
@@ -9,7 +9,7 @@ interface Props {
|
||||
}
|
||||
|
||||
const NavigationLink: FunctionComponent<Props> = props => (
|
||||
<Link to={props.to} Component={AppBarNavigationItem} activePropName="active">
|
||||
<Link to={props.to} as={AppBarNavigationItem} activePropName="active">
|
||||
{props.children}
|
||||
</Link>
|
||||
);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders correctly 1`] = `
|
||||
<Link
|
||||
Component={[Function]}
|
||||
<ForwardRef(render)
|
||||
activePropName="active"
|
||||
as={[Function]}
|
||||
to="/moderate"
|
||||
>
|
||||
link
|
||||
</Link>
|
||||
</ForwardRef(render)>
|
||||
`;
|
||||
|
||||
@@ -5,6 +5,7 @@ import { AuthCheckContainerQueryResponse } from "coral-admin/__generated__/AuthC
|
||||
import { SetRedirectPathMutation } from "coral-admin/mutations";
|
||||
import { AbilityType, can } from "coral-admin/permissions";
|
||||
import RestrictedContainer from "coral-admin/views/restricted/containers/RestrictedContainer";
|
||||
import { roleIsAtLeast } from "coral-framework/helpers";
|
||||
import { graphql, MutationProp, withMutation } from "coral-framework/lib/relay";
|
||||
import { withRouteConfig } from "coral-framework/lib/router";
|
||||
import { GQLUSER_ROLE } from "coral-framework/schema";
|
||||
@@ -13,114 +14,115 @@ interface Props {
|
||||
match: Match;
|
||||
router: Router;
|
||||
setRedirectPath: MutationProp<typeof SetRedirectPathMutation>;
|
||||
data:
|
||||
| AuthCheckContainerQueryResponse & {
|
||||
route: {
|
||||
// An AbilityType can be passed in as the Route data
|
||||
// to perform a permission check.
|
||||
data?: AbilityType;
|
||||
};
|
||||
}
|
||||
| null;
|
||||
data: AuthCheckContainerQueryResponse;
|
||||
}
|
||||
|
||||
class AuthCheckContainer extends React.Component<Props> {
|
||||
private wasLoggedIn = false;
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.redirectIfNotLoggedIn();
|
||||
}
|
||||
|
||||
public componentWillReceiveProps(nextProps: Props) {
|
||||
if (nextProps.data && nextProps.data.viewer) {
|
||||
this.wasLoggedIn = true;
|
||||
type CheckParams =
|
||||
| {
|
||||
role: GQLUSER_ROLE;
|
||||
ability?: AbilityType;
|
||||
}
|
||||
this.redirectIfNotLoggedIn(nextProps, this.props);
|
||||
if (nextProps.data && !nextProps.data.viewer) {
|
||||
this.wasLoggedIn = false;
|
||||
}
|
||||
}
|
||||
| {
|
||||
role?: GQLUSER_ROLE;
|
||||
ability: AbilityType;
|
||||
};
|
||||
|
||||
private shouldRedirectTo(props: Props = this.props) {
|
||||
if (!props.data || props.data.viewer) {
|
||||
return false;
|
||||
function createAuthCheckContainer(check: CheckParams) {
|
||||
class AuthCheckContainer extends React.Component<Props> {
|
||||
private wasLoggedIn = false;
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.redirectIfNotLoggedIn();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private hasAccess(props: Props = this.props) {
|
||||
const { viewer } = props.data!;
|
||||
if (viewer) {
|
||||
if (
|
||||
viewer.role === GQLUSER_ROLE.COMMENTER ||
|
||||
viewer.role === GQLUSER_ROLE.STAFF ||
|
||||
(props.data &&
|
||||
props.data.route.data &&
|
||||
// Perform permission check on the ability passed in by the route data
|
||||
!can(viewer, props.data.route.data))
|
||||
) {
|
||||
public componentWillReceiveProps(nextProps: Props) {
|
||||
if (nextProps.data && nextProps.data.viewer) {
|
||||
this.wasLoggedIn = true;
|
||||
}
|
||||
this.redirectIfNotLoggedIn(nextProps, this.props);
|
||||
if (nextProps.data && !nextProps.data.viewer) {
|
||||
this.wasLoggedIn = false;
|
||||
}
|
||||
}
|
||||
|
||||
private shouldRedirectTo(props: Props = this.props) {
|
||||
if (!props.data || props.data.viewer) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private async redirectIfNotLoggedIn(
|
||||
props: Props = this.props,
|
||||
prevProps: Props | null = null
|
||||
) {
|
||||
if (!this.shouldRedirectTo(props)) {
|
||||
return;
|
||||
}
|
||||
// If I was previously logged in then logged out, we don't need to set the redirect path.
|
||||
if (!this.wasLoggedIn) {
|
||||
const location = props.match.location;
|
||||
await props.setRedirectPath({
|
||||
path: location.pathname + location.search + location.hash,
|
||||
});
|
||||
}
|
||||
props.router.replace("/admin/login");
|
||||
}
|
||||
|
||||
public render() {
|
||||
if (!this.props.data || this.shouldRedirectTo()) {
|
||||
return null;
|
||||
}
|
||||
if (this.hasAccess()) {
|
||||
return this.props.children;
|
||||
}
|
||||
return <RestrictedContainer viewer={this.props.data.viewer!} />;
|
||||
}
|
||||
}
|
||||
|
||||
const enhanced = withRouteConfig<Props>({
|
||||
query: graphql`
|
||||
query AuthCheckContainerQuery {
|
||||
viewer {
|
||||
...RestrictedContainer_viewer
|
||||
username
|
||||
email
|
||||
profiles {
|
||||
__typename
|
||||
private hasAccess(props: Props = this.props) {
|
||||
const { viewer } = props.data!;
|
||||
if (viewer) {
|
||||
if (
|
||||
(check.role && !roleIsAtLeast(viewer.role, check.role)) ||
|
||||
(check.ability && !can(viewer, check.ability))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
role
|
||||
return true;
|
||||
}
|
||||
settings {
|
||||
auth {
|
||||
integrations {
|
||||
local {
|
||||
enabled
|
||||
targetFilter {
|
||||
admin
|
||||
stream
|
||||
return false;
|
||||
}
|
||||
|
||||
private async redirectIfNotLoggedIn(
|
||||
props: Props = this.props,
|
||||
prevProps: Props | null = null
|
||||
) {
|
||||
if (!this.shouldRedirectTo(props)) {
|
||||
return;
|
||||
}
|
||||
// If I was previously logged in then logged out, we don't need to set the redirect path.
|
||||
if (!this.wasLoggedIn) {
|
||||
const location = props.match.location;
|
||||
await props.setRedirectPath({
|
||||
path: location.pathname + location.search + location.hash,
|
||||
});
|
||||
}
|
||||
props.router.replace("/admin/login");
|
||||
}
|
||||
|
||||
public render() {
|
||||
if (!this.props.data || this.shouldRedirectTo()) {
|
||||
return null;
|
||||
}
|
||||
if (this.hasAccess()) {
|
||||
return this.props.children;
|
||||
}
|
||||
return <RestrictedContainer viewer={this.props.data.viewer!} />;
|
||||
}
|
||||
}
|
||||
|
||||
const enhanced = withRouteConfig<Props>({
|
||||
query: graphql`
|
||||
query AuthCheckContainerQuery {
|
||||
viewer {
|
||||
...RestrictedContainer_viewer
|
||||
username
|
||||
email
|
||||
profiles {
|
||||
__typename
|
||||
}
|
||||
role
|
||||
}
|
||||
settings {
|
||||
auth {
|
||||
integrations {
|
||||
local {
|
||||
enabled
|
||||
targetFilter {
|
||||
admin
|
||||
stream
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
})(withRouter(withMutation(SetRedirectPathMutation)(AuthCheckContainer)));
|
||||
`,
|
||||
})(withRouter(withMutation(SetRedirectPathMutation)(AuthCheckContainer)));
|
||||
return enhanced;
|
||||
}
|
||||
|
||||
export default enhanced;
|
||||
export default createAuthCheckContainer;
|
||||
|
||||
@@ -47,18 +47,12 @@ const ApproveCommentMutation = createMutation(
|
||||
input: {
|
||||
commentID: input.commentID,
|
||||
commentRevisionID: input.commentRevisionID,
|
||||
clientMutationId: clientMutationId.toString(),
|
||||
},
|
||||
},
|
||||
optimisticResponse: {
|
||||
approveComment: {
|
||||
comment: {
|
||||
id: input.commentID,
|
||||
status: "APPROVED",
|
||||
},
|
||||
clientMutationId: (clientMutationId++).toString(),
|
||||
},
|
||||
},
|
||||
optimisticUpdater: store => {
|
||||
store.get(input.commentID)!.setValue("APPROVED", "status");
|
||||
},
|
||||
updater: store => {
|
||||
const connections = [
|
||||
getQueueConnection(store, "reported", input.storyID),
|
||||
|
||||
@@ -46,7 +46,7 @@ const BanUserMutation = createMutation(
|
||||
current: lookup<GQLUser>(
|
||||
environment,
|
||||
input.userID
|
||||
)!.status.current.concat([GQLUSER_STATUS.BANNED]),
|
||||
)!.status.current.concat(GQLUSER_STATUS.BANNED),
|
||||
ban: {
|
||||
active: true,
|
||||
},
|
||||
|
||||
@@ -47,18 +47,12 @@ const RejectCommentMutation = createMutation(
|
||||
input: {
|
||||
commentID: input.commentID,
|
||||
commentRevisionID: input.commentRevisionID,
|
||||
clientMutationId: clientMutationId.toString(),
|
||||
},
|
||||
},
|
||||
optimisticResponse: {
|
||||
rejectComment: {
|
||||
comment: {
|
||||
id: input.commentID,
|
||||
status: "REJECTED",
|
||||
},
|
||||
clientMutationId: (clientMutationId++).toString(),
|
||||
},
|
||||
},
|
||||
optimisticUpdater: store => {
|
||||
store.get(input.commentID)!.setValue("REJECTED", "status");
|
||||
},
|
||||
updater: store => {
|
||||
const connections = [
|
||||
getQueueConnection(store, "reported", input.storyID),
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { makeRouteConfig, Redirect, Route } from "found";
|
||||
import React from "react";
|
||||
|
||||
import { GQLUSER_ROLE } from "coral-framework/schema";
|
||||
import AppContainer from "./containers/AppContainer";
|
||||
import AuthCheckContainer from "./containers/AuthCheckContainer";
|
||||
import createAuthCheckContainer from "./containers/AuthCheckContainer";
|
||||
import { Ability } from "./permissions";
|
||||
import CommunityContainer from "./routes/community/containers/CommunityContainer";
|
||||
import ConfigureContainer from "./routes/configure/containers/ConfigureContainer";
|
||||
@@ -26,7 +27,10 @@ import StoriesContainer from "./routes/stories/containers/StoriesContainer";
|
||||
|
||||
export default makeRouteConfig(
|
||||
<Route path="admin">
|
||||
<Route {...AuthCheckContainer.routeConfig}>
|
||||
<Route
|
||||
{...createAuthCheckContainer({ role: GQLUSER_ROLE.MODERATOR })
|
||||
.routeConfig}
|
||||
>
|
||||
<Route {...AppContainer.routeConfig}>
|
||||
<Redirect from="/" to="/admin/moderate" />
|
||||
<Route
|
||||
@@ -64,8 +68,9 @@ export default makeRouteConfig(
|
||||
<Route path="community" {...CommunityContainer.routeConfig} />
|
||||
<Route path="stories" Component={Stories} />
|
||||
<Route
|
||||
{...AuthCheckContainer.routeConfig}
|
||||
data={Ability.CHANGE_CONFIGURATION}
|
||||
{...createAuthCheckContainer({
|
||||
ability: Ability.CHANGE_CONFIGURATION,
|
||||
}).routeConfig}
|
||||
>
|
||||
<Route path="configure" Component={ConfigureContainer}>
|
||||
<Redirect from="/" to="/admin/configure/general" />
|
||||
|
||||
+2
-2
@@ -4,12 +4,12 @@ exports[`renders correctly 1`] = `
|
||||
<li
|
||||
className="customClassName"
|
||||
>
|
||||
<Link
|
||||
<ForwardRef(render)
|
||||
activeClassName="Link-linkActive"
|
||||
className="Link-link"
|
||||
to="/admin"
|
||||
>
|
||||
child
|
||||
</Link>
|
||||
</ForwardRef(render)>
|
||||
</li>
|
||||
`;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { withRouter, WithRouter } from "found";
|
||||
import { RouterState, withRouter } from "found";
|
||||
import * as React from "react";
|
||||
import { Component } from "react";
|
||||
|
||||
@@ -25,7 +25,7 @@ type Props = {
|
||||
auth: AuthData;
|
||||
viewer: UserData | null;
|
||||
setRedirectPath: MutationProp<typeof SetRedirectPathMutation>;
|
||||
} & WithRouter;
|
||||
} & RouterState;
|
||||
|
||||
function handleAccountCompletion(props: Props) {
|
||||
const {
|
||||
|
||||
@@ -9,7 +9,7 @@ interface Props {
|
||||
}
|
||||
|
||||
const NavigationLink: FunctionComponent<Props> = props => (
|
||||
<Link to={props.to} Component={SubBarNavigationItem} activePropName="active">
|
||||
<Link to={props.to} as={SubBarNavigationItem} activePropName="active">
|
||||
{props.children}
|
||||
</Link>
|
||||
);
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ exports[`renders correctly 1`] = `
|
||||
<div
|
||||
data-testid="moderate-container"
|
||||
>
|
||||
<withRouter(Relay(ModerateSearchBarContainer))
|
||||
<ForwardRef(render)
|
||||
allStories={true}
|
||||
story={Object {}}
|
||||
/>
|
||||
|
||||
+3
-3
@@ -1,11 +1,11 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders correctly 1`] = `
|
||||
<Link
|
||||
Component={[Function]}
|
||||
<ForwardRef(render)
|
||||
activePropName="active"
|
||||
as={[Function]}
|
||||
to="/moderate"
|
||||
>
|
||||
link
|
||||
</Link>
|
||||
</ForwardRef(render)>
|
||||
`;
|
||||
|
||||
+2
-2
@@ -12,12 +12,12 @@ exports[`renders correctly 1`] = `
|
||||
<Localized
|
||||
id="moderate-single-goToModerationQueues"
|
||||
>
|
||||
<Link
|
||||
<ForwardRef(render)
|
||||
className="SingleModerate-subBarBegin"
|
||||
to="/admin/moderate/"
|
||||
>
|
||||
Go to moderation queues
|
||||
</Link>
|
||||
</ForwardRef(render)>
|
||||
</Localized>
|
||||
<Localized
|
||||
id="moderate-single-singleCommentView"
|
||||
|
||||
@@ -20,11 +20,12 @@ export class FlagDetailsContainer extends React.Component<Props> {
|
||||
const nodes = this.props.comment.flags.nodes;
|
||||
const offensiveList: React.ReactElement[] = [];
|
||||
const spamList: React.ReactElement[] = [];
|
||||
nodes.forEach(n => {
|
||||
nodes.forEach((n, i) => {
|
||||
switch (n.reason) {
|
||||
case GQLCOMMENT_FLAG_REASON.COMMENT_REPORTED_OFFENSIVE:
|
||||
offensiveList.push(
|
||||
<FlagDetailsEntry
|
||||
key={i}
|
||||
user={n.flagger ? n.flagger.username : <NotAvailable />}
|
||||
details={n.additionalDetails}
|
||||
/>
|
||||
@@ -33,6 +34,7 @@ export class FlagDetailsContainer extends React.Component<Props> {
|
||||
case GQLCOMMENT_FLAG_REASON.COMMENT_REPORTED_SPAM:
|
||||
spamList.push(
|
||||
<FlagDetailsEntry
|
||||
key={i}
|
||||
user={n.flagger ? n.flagger.username : <NotAvailable />}
|
||||
details={n.additionalDetails}
|
||||
/>
|
||||
|
||||
@@ -23,10 +23,7 @@ interface Props {
|
||||
const UserRow: FunctionComponent<Props> = props => (
|
||||
<TableRow>
|
||||
<TableCell className={styles.titleColumn}>
|
||||
<Link
|
||||
to={getModerationLink("default", props.storyID)}
|
||||
Component={TextLink}
|
||||
>
|
||||
<Link to={getModerationLink("default", props.storyID)} as={TextLink}>
|
||||
{props.title || <NotAvailable />}
|
||||
</Link>
|
||||
</TableCell>
|
||||
|
||||
@@ -14,7 +14,7 @@ interface Props {
|
||||
const GoToCommentLink: FunctionComponent<Props> = props => {
|
||||
return (
|
||||
<Link
|
||||
Component={TextLink}
|
||||
as={TextLink}
|
||||
className={styles.root}
|
||||
to={props.href}
|
||||
onClick={props.onClick}
|
||||
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders correctly 1`] = `
|
||||
<Link
|
||||
Component={[Function]}
|
||||
<ForwardRef(render)
|
||||
as={[Function]}
|
||||
className="GoToCommentLink-root"
|
||||
onClick={[Function]}
|
||||
to="#"
|
||||
@@ -18,5 +18,5 @@ exports[`renders correctly 1`] = `
|
||||
<ForwardRef(forwardRef)>
|
||||
chevron_right
|
||||
</ForwardRef(forwardRef)>
|
||||
</Link>
|
||||
</ForwardRef(render)>
|
||||
`;
|
||||
|
||||
Reference in New Issue
Block a user