diff --git a/src/core/client/stream/classes.ts b/src/core/client/stream/classes.ts index a42d5e21a..4887da9cc 100644 --- a/src/core/client/stream/classes.ts +++ b/src/core/client/stream/classes.ts @@ -106,6 +106,12 @@ const CLASSES = { myComments: "coral coral-tabBarSecondary-tab coral-tabBarMyProfile-myComments", + /** + * preferences is the button for the "Preferences" tab. + */ + preferences: + "coral coral-tabBarSecondary-tab coral-tabBarMyProfile-preferences", + /** * notifications is the button for the "Notifications" tab. */ @@ -744,6 +750,10 @@ const CLASSES = { $root: "coral coral-notifications", }, + preferencesTabPane: { + $root: "coral coral-preferences", + }, + /** * accountTabPane is the tab pane that shows account settings. */ diff --git a/src/core/client/stream/local/local.graphql b/src/core/client/stream/local/local.graphql index fc779be88..67bc8b7f2 100644 --- a/src/core/client/stream/local/local.graphql +++ b/src/core/client/stream/local/local.graphql @@ -12,7 +12,7 @@ enum TAB { enum PROFILE_TAB { MY_COMMENTS ACCOUNT - NOTIFICATIONS + PREFERENCES } enum COMMENTS_TAB { diff --git a/src/core/client/stream/tabs/Profile/CommentHistory/index.ts b/src/core/client/stream/tabs/Profile/CommentHistory/index.ts deleted file mode 100644 index 62af2ae45..000000000 --- a/src/core/client/stream/tabs/Profile/CommentHistory/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export { - default, - default as CommentHistoryContainer, -} from "./CommentHistoryContainer"; diff --git a/src/core/client/stream/tabs/Profile/CommentHistory/CommentHistory.css b/src/core/client/stream/tabs/Profile/MyComments/CommentHistory.css similarity index 100% rename from src/core/client/stream/tabs/Profile/CommentHistory/CommentHistory.css rename to src/core/client/stream/tabs/Profile/MyComments/CommentHistory.css diff --git a/src/core/client/stream/tabs/Profile/CommentHistory/CommentHistory.spec.tsx b/src/core/client/stream/tabs/Profile/MyComments/CommentHistory.spec.tsx similarity index 100% rename from src/core/client/stream/tabs/Profile/CommentHistory/CommentHistory.spec.tsx rename to src/core/client/stream/tabs/Profile/MyComments/CommentHistory.spec.tsx diff --git a/src/core/client/stream/tabs/Profile/CommentHistory/CommentHistory.tsx b/src/core/client/stream/tabs/Profile/MyComments/CommentHistory.tsx similarity index 100% rename from src/core/client/stream/tabs/Profile/CommentHistory/CommentHistory.tsx rename to src/core/client/stream/tabs/Profile/MyComments/CommentHistory.tsx diff --git a/src/core/client/stream/tabs/Profile/CommentHistory/CommentHistoryContainer.tsx b/src/core/client/stream/tabs/Profile/MyComments/CommentHistoryContainer.tsx similarity index 100% rename from src/core/client/stream/tabs/Profile/CommentHistory/CommentHistoryContainer.tsx rename to src/core/client/stream/tabs/Profile/MyComments/CommentHistoryContainer.tsx diff --git a/src/core/client/stream/tabs/Profile/Settings/DownloadCommentsContainer.css b/src/core/client/stream/tabs/Profile/MyComments/DownloadCommentsContainer.css similarity index 100% rename from src/core/client/stream/tabs/Profile/Settings/DownloadCommentsContainer.css rename to src/core/client/stream/tabs/Profile/MyComments/DownloadCommentsContainer.css diff --git a/src/core/client/stream/tabs/Profile/Settings/DownloadCommentsContainer.tsx b/src/core/client/stream/tabs/Profile/MyComments/DownloadCommentsContainer.tsx similarity index 98% rename from src/core/client/stream/tabs/Profile/Settings/DownloadCommentsContainer.tsx rename to src/core/client/stream/tabs/Profile/MyComments/DownloadCommentsContainer.tsx index ce0e7859a..051f86712 100644 --- a/src/core/client/stream/tabs/Profile/Settings/DownloadCommentsContainer.tsx +++ b/src/core/client/stream/tabs/Profile/MyComments/DownloadCommentsContainer.tsx @@ -13,7 +13,7 @@ import { Button, CallOut, Flex, Icon, Typography } from "coral-ui/components"; import { DownloadCommentsContainer_viewer } from "coral-stream/__generated__/DownloadCommentsContainer_viewer.graphql"; -import RequestCommentsDownloadMutation from "./RequestCommentsDownloadMutation"; +import RequestCommentsDownloadMutation from "../Settings/RequestCommentsDownloadMutation"; import styles from "./DownloadCommentsContainer.css"; diff --git a/src/core/client/stream/tabs/Profile/CommentHistory/HistoryComment.css b/src/core/client/stream/tabs/Profile/MyComments/HistoryComment.css similarity index 100% rename from src/core/client/stream/tabs/Profile/CommentHistory/HistoryComment.css rename to src/core/client/stream/tabs/Profile/MyComments/HistoryComment.css diff --git a/src/core/client/stream/tabs/Profile/CommentHistory/HistoryComment.spec.tsx b/src/core/client/stream/tabs/Profile/MyComments/HistoryComment.spec.tsx similarity index 100% rename from src/core/client/stream/tabs/Profile/CommentHistory/HistoryComment.spec.tsx rename to src/core/client/stream/tabs/Profile/MyComments/HistoryComment.spec.tsx diff --git a/src/core/client/stream/tabs/Profile/CommentHistory/HistoryComment.tsx b/src/core/client/stream/tabs/Profile/MyComments/HistoryComment.tsx similarity index 100% rename from src/core/client/stream/tabs/Profile/CommentHistory/HistoryComment.tsx rename to src/core/client/stream/tabs/Profile/MyComments/HistoryComment.tsx diff --git a/src/core/client/stream/tabs/Profile/CommentHistory/HistoryCommentContainer.tsx b/src/core/client/stream/tabs/Profile/MyComments/HistoryCommentContainer.tsx similarity index 100% rename from src/core/client/stream/tabs/Profile/CommentHistory/HistoryCommentContainer.tsx rename to src/core/client/stream/tabs/Profile/MyComments/HistoryCommentContainer.tsx diff --git a/src/core/client/stream/tabs/Profile/MyComments/MyCommentsContainer.tsx b/src/core/client/stream/tabs/Profile/MyComments/MyCommentsContainer.tsx new file mode 100644 index 000000000..79878c823 --- /dev/null +++ b/src/core/client/stream/tabs/Profile/MyComments/MyCommentsContainer.tsx @@ -0,0 +1,64 @@ +import React, { FunctionComponent } from "react"; +import { graphql } from "react-relay"; + +import { withFragmentContainer } from "coral-framework/lib/relay"; + +import { MyCommentsContainer_settings } from "coral-stream/__generated__/MyCommentsContainer_settings.graphql"; +import { MyCommentsContainer_story } from "coral-stream/__generated__/MyCommentsContainer_story.graphql"; +import { MyCommentsContainer_viewer } from "coral-stream/__generated__/MyCommentsContainer_viewer.graphql"; + +import CommentHistoryContainer from "./CommentHistoryContainer"; +import DownloadCommentsContainer from "./DownloadCommentsContainer"; + +import { HorizontalGutter } from "coral-ui/components/v2"; + +// import styles from "./MyComments.css"; + +interface Props { + settings: MyCommentsContainer_settings; + viewer: MyCommentsContainer_viewer; + story: MyCommentsContainer_story; +} + +const MyCommentsContainer: FunctionComponent = ({ + settings, + viewer, + story, +}) => { + return ( + + + {settings.accountFeatures.downloadComments && ( + + )} + + ); +}; + +const enhanced = withFragmentContainer({ + viewer: graphql` + fragment MyCommentsContainer_viewer on User { + ...CommentHistoryContainer_viewer + ...DownloadCommentsContainer_viewer + } + `, + story: graphql` + fragment MyCommentsContainer_story on Story { + ...CommentHistoryContainer_story + } + `, + settings: graphql` + fragment MyCommentsContainer_settings on Settings { + accountFeatures { + downloadComments + } + ...CommentHistoryContainer_settings + } + `, +})(MyCommentsContainer); + +export default enhanced; diff --git a/src/core/client/stream/tabs/Profile/CommentHistory/__snapshots__/CommentHistory.spec.tsx.snap b/src/core/client/stream/tabs/Profile/MyComments/__snapshots__/CommentHistory.spec.tsx.snap similarity index 100% rename from src/core/client/stream/tabs/Profile/CommentHistory/__snapshots__/CommentHistory.spec.tsx.snap rename to src/core/client/stream/tabs/Profile/MyComments/__snapshots__/CommentHistory.spec.tsx.snap diff --git a/src/core/client/stream/tabs/Profile/CommentHistory/__snapshots__/HistoryComment.spec.tsx.snap b/src/core/client/stream/tabs/Profile/MyComments/__snapshots__/HistoryComment.spec.tsx.snap similarity index 100% rename from src/core/client/stream/tabs/Profile/CommentHistory/__snapshots__/HistoryComment.spec.tsx.snap rename to src/core/client/stream/tabs/Profile/MyComments/__snapshots__/HistoryComment.spec.tsx.snap diff --git a/src/core/client/stream/tabs/Profile/MyComments/index.ts b/src/core/client/stream/tabs/Profile/MyComments/index.ts new file mode 100644 index 000000000..e7325675a --- /dev/null +++ b/src/core/client/stream/tabs/Profile/MyComments/index.ts @@ -0,0 +1 @@ +export { default, default as MyCommentsContainer } from "./MyCommentsContainer"; diff --git a/src/core/client/stream/tabs/Profile/Settings/IgnoreUserSettingsContainer.css b/src/core/client/stream/tabs/Profile/Preferences/IgnoreUserSettingsContainer.css similarity index 100% rename from src/core/client/stream/tabs/Profile/Settings/IgnoreUserSettingsContainer.css rename to src/core/client/stream/tabs/Profile/Preferences/IgnoreUserSettingsContainer.css diff --git a/src/core/client/stream/tabs/Profile/Settings/IgnoreUserSettingsContainer.tsx b/src/core/client/stream/tabs/Profile/Preferences/IgnoreUserSettingsContainer.tsx similarity index 98% rename from src/core/client/stream/tabs/Profile/Settings/IgnoreUserSettingsContainer.tsx rename to src/core/client/stream/tabs/Profile/Preferences/IgnoreUserSettingsContainer.tsx index b273f615a..7454fb25a 100644 --- a/src/core/client/stream/tabs/Profile/Settings/IgnoreUserSettingsContainer.tsx +++ b/src/core/client/stream/tabs/Profile/Preferences/IgnoreUserSettingsContainer.tsx @@ -16,8 +16,8 @@ import { import { IgnoreUserSettingsContainer_viewer as ViewerData } from "coral-stream/__generated__/IgnoreUserSettingsContainer_viewer.graphql"; +import Username from "../Settings/Username"; import RemoveUserIgnoreMutation from "./RemoveUserIgnoreMutation"; -import Username from "./Username"; import styles from "./IgnoreUserSettingsContainer.css"; diff --git a/src/core/client/stream/tabs/Profile/Settings/NotificationSettingsContainer.tsx b/src/core/client/stream/tabs/Profile/Preferences/NotificationSettingsContainer.tsx similarity index 100% rename from src/core/client/stream/tabs/Profile/Settings/NotificationSettingsContainer.tsx rename to src/core/client/stream/tabs/Profile/Preferences/NotificationSettingsContainer.tsx diff --git a/src/core/client/stream/tabs/Profile/Preferences/PreferencesContainer.tsx b/src/core/client/stream/tabs/Profile/Preferences/PreferencesContainer.tsx new file mode 100644 index 000000000..d0a12f5f2 --- /dev/null +++ b/src/core/client/stream/tabs/Profile/Preferences/PreferencesContainer.tsx @@ -0,0 +1,33 @@ +import React, { FunctionComponent } from "react"; + +import { graphql, withFragmentContainer } from "coral-framework/lib/relay"; +import { HorizontalGutter } from "coral-ui/components/v2"; + +import IgnoreUserSettingsContainer from "./IgnoreUserSettingsContainer"; +import NotificationSettingsContainer from "./NotificationSettingsContainer"; + +import { PreferencesContainer_viewer } from "coral-stream/__generated__/PreferencesContainer_viewer.graphql"; + +interface Props { + viewer: PreferencesContainer_viewer; +} + +const PreferencesContainer: FunctionComponent = props => { + return ( + + + + + ); +}; + +const enhanced = withFragmentContainer({ + viewer: graphql` + fragment PreferencesContainer_viewer on User { + ...NotificationSettingsContainer_viewer + ...IgnoreUserSettingsContainer_viewer + } + `, +})(PreferencesContainer); + +export default enhanced; diff --git a/src/core/client/stream/tabs/Profile/Settings/RemoveUserIgnoreMutation.ts b/src/core/client/stream/tabs/Profile/Preferences/RemoveUserIgnoreMutation.ts similarity index 100% rename from src/core/client/stream/tabs/Profile/Settings/RemoveUserIgnoreMutation.ts rename to src/core/client/stream/tabs/Profile/Preferences/RemoveUserIgnoreMutation.ts diff --git a/src/core/client/stream/tabs/Profile/Settings/UpdateNotificationSettingsMutation.ts b/src/core/client/stream/tabs/Profile/Preferences/UpdateNotificationSettingsMutation.ts similarity index 100% rename from src/core/client/stream/tabs/Profile/Settings/UpdateNotificationSettingsMutation.ts rename to src/core/client/stream/tabs/Profile/Preferences/UpdateNotificationSettingsMutation.ts diff --git a/src/core/client/stream/tabs/Profile/Preferences/index.ts b/src/core/client/stream/tabs/Profile/Preferences/index.ts new file mode 100644 index 000000000..993282725 --- /dev/null +++ b/src/core/client/stream/tabs/Profile/Preferences/index.ts @@ -0,0 +1,4 @@ +export { + default, + default as PreferencesContainer, +} from "./PreferencesContainer"; diff --git a/src/core/client/stream/tabs/Profile/Profile.tsx b/src/core/client/stream/tabs/Profile/Profile.tsx index 7e2ddaa1d..f116f1414 100644 --- a/src/core/client/stream/tabs/Profile/Profile.tsx +++ b/src/core/client/stream/tabs/Profile/Profile.tsx @@ -1,5 +1,5 @@ import { Localized } from "@fluent/react/compat"; -import React, { FunctionComponent, useCallback } from "react"; +import React, { FunctionComponent, useCallback, useMemo } from "react"; import { useViewerEvent } from "coral-framework/lib/events"; import { graphql, useLocal } from "coral-framework/lib/relay"; @@ -17,23 +17,23 @@ import { import { ProfileLocal } from "coral-stream/__generated__/ProfileLocal.graphql"; -import CommentHistoryContainer from "./CommentHistory"; import DeletionRequestCalloutContainer from "./DeletionRequest/DeletionRequestCalloutContainer"; +import MyCommentsContainer from "./MyComments"; +import PreferencesContainer from "./Preferences"; import AccountSettingsContainer from "./Settings"; -import NotificationSettingsContainer from "./Settings/NotificationSettingsContainer"; export interface ProfileProps { - story: PropTypesOf["story"]; + isSSO: boolean; + ssoURL: string | null; + story: PropTypesOf["story"]; viewer: PropTypesOf["viewer"] & - PropTypesOf["viewer"] & - PropTypesOf["viewer"] & + PropTypesOf["viewer"] & PropTypesOf["viewer"] & PropTypesOf["viewer"] & - PropTypesOf["viewer"] & - PropTypesOf["viewer"]; + PropTypesOf["viewer"]; settings: PropTypesOf["settings"] & PropTypesOf["settings"] & - PropTypesOf["settings"]; + PropTypesOf["settings"]; } const Profile: FunctionComponent = props => { @@ -45,13 +45,31 @@ const Profile: FunctionComponent = props => { `); const onTabClick = useCallback( (tab: ProfileLocal["profileTab"]) => { - if (local.profileTab !== tab) { + if ( + tab === "ACCOUNT" && + props.isSSO && + props.ssoURL && + props.ssoURL.length > 0 + ) { + window.open(props.ssoURL); + } else if (local.profileTab !== tab) { emitSetProfileTabEvent({ tab }); setLocal({ profileTab: tab }); } }, - [setLocal, local.profileTab] + [setLocal, local.profileTab, props.ssoURL, props.isSSO] ); + + const showAccountTab = useMemo(() => { + if (!props.isSSO) { + return true; + } + if (props.ssoURL) { + return true; + } + return false; + }, [props.ssoURL, props.isSSO]); + return ( @@ -67,43 +85,47 @@ const Profile: FunctionComponent = props => { - - Notifications - - - - - Account + + Preferences + {showAccountTab && ( + + + Account + + + )} - - - - - - + + {showAccountTab && ( + + + + + )} ); diff --git a/src/core/client/stream/tabs/Profile/ProfileContainer.tsx b/src/core/client/stream/tabs/Profile/ProfileContainer.tsx index 315f48acf..f6387f105 100644 --- a/src/core/client/stream/tabs/Profile/ProfileContainer.tsx +++ b/src/core/client/stream/tabs/Profile/ProfileContainer.tsx @@ -1,3 +1,4 @@ +import { isUndefined } from "lodash"; import React from "react"; import { graphql } from "react-relay"; @@ -17,11 +18,16 @@ interface ProfileContainerProps { export class ProfileContainer extends React.Component { public render() { + const ssoProfile = this.props.viewer.profiles.find( + profile => profile.__typename === "SSOProfile" + ); return ( ); } @@ -29,25 +35,27 @@ export class ProfileContainer extends React.Component { const enhanced = withFragmentContainer({ story: graphql` fragment ProfileContainer_story on Story { - ...CommentHistoryContainer_story + ...MyCommentsContainer_story } `, viewer: graphql` fragment ProfileContainer_viewer on User { ...UserBoxContainer_viewer - ...CommentHistoryContainer_viewer ...AccountSettingsContainer_viewer - ...ChangeUsernameContainer_viewer - ...ChangeEmailContainer_viewer + ...MyCommentsContainer_viewer ...DeletionRequestCalloutContainer_viewer - ...NotificationSettingsContainer_viewer + ...PreferencesContainer_viewer + profiles { + __typename + } + ssoURL } `, settings: graphql` fragment ProfileContainer_settings on Settings { ...UserBoxContainer_settings ...AccountSettingsContainer_settings - ...CommentHistoryContainer_settings + ...MyCommentsContainer_settings } `, })(ProfileContainer); diff --git a/src/core/client/stream/tabs/Profile/Settings/AccountSettingsContainer.tsx b/src/core/client/stream/tabs/Profile/Settings/AccountSettingsContainer.tsx index 9ea88f395..c03be1ad1 100644 --- a/src/core/client/stream/tabs/Profile/Settings/AccountSettingsContainer.tsx +++ b/src/core/client/stream/tabs/Profile/Settings/AccountSettingsContainer.tsx @@ -12,8 +12,7 @@ import ChangeEmailContainer from "./ChangeEmail"; import ChangePasswordContainer from "./ChangePasswordContainer"; import ChangeUsernameContainer from "./ChangeUsername"; import DeleteAccountContainer from "./DeleteAccount/DeleteAccountContainer"; -import DownloadCommentsContainer from "./DownloadCommentsContainer"; -import IgnoreUserSettingsContainer from "./IgnoreUserSettingsContainer"; +// import DownloadCommentsContainer from "../CommentHistory/DownloadCommentsContainer"; import styles from "./AccountSettingsContainer.css"; @@ -26,7 +25,7 @@ const AccountSettingsContainer: FunctionComponent = ({ viewer, settings, }) => ( - + Manage your account @@ -34,10 +33,6 @@ const AccountSettingsContainer: FunctionComponent = ({ - - {settings.accountFeatures.downloadComments && ( - - )} {settings.accountFeatures.deleteAccount && ( )} @@ -48,25 +43,20 @@ const AccountSettingsContainer: FunctionComponent = ({ const enhanced = withFragmentContainer({ viewer: graphql` fragment AccountSettingsContainer_viewer on User { - ...IgnoreUserSettingsContainer_viewer - ...DownloadCommentsContainer_viewer ...DeleteAccountContainer_viewer ...ChangeUsernameContainer_viewer ...ChangeEmailContainer_viewer - ...UserBoxContainer_viewer } `, settings: graphql` fragment AccountSettingsContainer_settings on Settings { accountFeatures { - downloadComments deleteAccount } ...ChangePasswordContainer_settings ...DeleteAccountContainer_settings ...ChangeEmailContainer_settings ...ChangeUsernameContainer_settings - ...UserBoxContainer_settings } `, })(AccountSettingsContainer); diff --git a/src/core/client/stream/test/profile/__snapshots__/account.spec.tsx.snap b/src/core/client/stream/test/profile/__snapshots__/account.spec.tsx.snap index 7734ea747..d05f56784 100644 --- a/src/core/client/stream/test/profile/__snapshots__/account.spec.tsx.snap +++ b/src/core/client/stream/test/profile/__snapshots__/account.spec.tsx.snap @@ -2,102 +2,128 @@ exports[`renders the empty settings pane 1`] = `
- -
-
+ Manage your account + +
+
+
+

+ Username +

+

+ Passivo +

+
+ +
+
+
+
+
+

+ Email +

+
+

+ +   + +

+ (Unverified) +

+
+
+ +
+
-
- Signed in as - +
-
- - Not you?  - +
+

+ Verify your email address +

+

+ An email has been sent to {$email} to verify your account. +You must verify your new email address before it can be used +to sign in to your account or to receive notifications. +

-
    - - - -
-
-
-

- Manage your account -

-
-
-
-
-

- Username -

-

- Passivo -

-
- -
-
-
-
-
-

- Email -

-
-

- -   - -

- (Unverified) -

-
-
- -
-
-
-
-
- -
-
-

- Verify your email address -

-

- An email has been sent to {$email} to verify your account. -You must verify your new email address before it can be used -to sign in to your account or to receive notifications. -

- -
-
-
-
-
-
-
-

- Password -

- -
-
-
-
-

- Ignored Commenters -

- -
-
-
-
-
-

- Download my comment history -

-

- You will receive an email with a link to download your comment history. -You can make - - one download request every 14 days. - -

-
-
- -
-
-
-
-
-
-

- Delete My Account -

-

- Deleting your account will permanently erase your profile and remove -all your comments from this site. -

-
- -
-
-
-
-
-
-
+
+
+
+

+ Password +

+ +
+
+
+
+
+

+ Delete My Account +

+

+ Deleting your account will permanently erase your profile and remove +all your comments from this site. +

+
+ +
+
+ `; diff --git a/src/core/client/stream/test/profile/account.spec.tsx b/src/core/client/stream/test/profile/account.spec.tsx index 3bd1469b9..c146aec37 100644 --- a/src/core/client/stream/test/profile/account.spec.tsx +++ b/src/core/client/stream/test/profile/account.spec.tsx @@ -7,12 +7,10 @@ import { createResolversStub, CreateTestRendererParams, waitForElement, - waitUntilThrow, within, } from "coral-framework/testHelpers"; import { - commenters, settings, settingsWithoutLocalAuth, stories, @@ -46,19 +44,9 @@ async function createTestRenderer( }, }); - const ignoredCommenters = await waitForElement(() => - within(testRenderer.root).queryByTestID("profile-account-ignoredCommenters") - ); - - const changePassword = within(testRenderer.root).queryByTestID( - "profile-account-changePassword" - ); - return { testRenderer, context, - ignoredCommenters, - changePassword, }; } @@ -66,18 +54,29 @@ it("renders the empty settings pane", async () => { const { testRenderer: { root }, } = await createTestRenderer(); - expect(within(root).toJSON()).toMatchSnapshot(); - expect(await within(root).axe()).toHaveNoViolations(); + const account = await waitForElement(() => + within(root).getByTestID("profile-manageAccount") + ); + expect(within(account).toJSON()).toMatchSnapshot(); + expect(await within(account).axe()).toHaveNoViolations(); }); it("doesn't show the change password pane when local auth is disabled", async () => { - const { changePassword } = await createTestRenderer({ + const { + testRenderer: { root }, + } = await createTestRenderer({ resolvers: createResolversStub({ Query: { settings: () => settingsWithoutLocalAuth, }, }), }); + const account = await waitForElement(() => + within(root).getByTestID("profile-manageAccount") + ); + const changePassword = within(account).queryByTestID( + "profile-account-changePassword" + ); expect(changePassword).toBeNull(); }); @@ -143,57 +142,3 @@ it("render password change form", async () => { expect(updatePassword.calledOnce).toBeTruthy(); }); - -it("render empty ignored users list", async () => { - const { ignoredCommenters } = await createTestRenderer(); - const editButton = within(ignoredCommenters).getByText("Manage"); - act(() => { - editButton.props.onClick(); - }); - await waitForElement(() => - within(ignoredCommenters).getByText( - "You are not currently ignoring anyone", - { - exact: false, - } - ) - ); -}); - -it("render ignored users list", async () => { - const { ignoredCommenters } = await createTestRenderer({ - resolvers: createResolversStub({ - Query: { - viewer: () => - pureMerge(viewer, { - ignoredUsers: [commenters[0], commenters[1]], - }), - }, - Mutation: { - removeUserIgnore: ({ variables }) => { - expectAndFail(variables).toMatchObject({ - userID: commenters[0].id, - }); - return {}; - }, - }, - }), - }); - const editButton = within(ignoredCommenters).getByText("Manage"); - act(() => { - editButton.props.onClick(); - }); - within(ignoredCommenters).getByText(commenters[0].username!); - within(ignoredCommenters).getByText(commenters[1].username!); - - // Stop ignoring first users. - within(ignoredCommenters) - .getAllByText("Stop ignoring", { selector: "button" })[0] - .props.onClick(); - - // First user should dissappear from list. - await waitUntilThrow(() => - within(ignoredCommenters).getByText(commenters[0].username!) - ); - within(ignoredCommenters).getByText(commenters[1].username!); -}); diff --git a/src/core/client/stream/test/profile/notificationSettings.spec.tsx b/src/core/client/stream/test/profile/preferences.spec.tsx similarity index 67% rename from src/core/client/stream/test/profile/notificationSettings.spec.tsx rename to src/core/client/stream/test/profile/preferences.spec.tsx index 51bfebcfd..1560fbff2 100644 --- a/src/core/client/stream/test/profile/notificationSettings.spec.tsx +++ b/src/core/client/stream/test/profile/preferences.spec.tsx @@ -7,10 +7,11 @@ import { createResolversStub, CreateTestRendererParams, waitForElement, + waitUntilThrow, within, } from "coral-framework/testHelpers"; -import { settings, stories, viewerPassive } from "../fixtures"; +import { commenters, settings, stories, viewerPassive } from "../fixtures"; import create from "./create"; const story = stories[0]; @@ -32,16 +33,20 @@ async function createTestRenderer( params.resolvers ), initLocalState: (localRecord, source, environment) => { - localRecord.setValue("NOTIFICATIONS", "profileTab"); + localRecord.setValue("PREFERENCES", "profileTab"); if (params.initLocalState) { params.initLocalState(localRecord, source, environment); } }, }); + const ignoredCommenters = await waitForElement(() => + within(testRenderer.root).queryByTestID("profile-account-ignoredCommenters") + ); return { testRenderer, context, + ignoredCommenters, }; } @@ -142,3 +147,57 @@ it("render notifications form", async () => { // The save button should now be disabled. expect(save.props.disabled).toEqual(true); }); + +it("render empty ignored users list", async () => { + const { ignoredCommenters } = await createTestRenderer(); + const editButton = within(ignoredCommenters).getByText("Manage"); + act(() => { + editButton.props.onClick(); + }); + await waitForElement(() => + within(ignoredCommenters).getByText( + "You are not currently ignoring anyone", + { + exact: false, + } + ) + ); +}); + +it("render ignored users list", async () => { + const { ignoredCommenters } = await createTestRenderer({ + resolvers: createResolversStub({ + Query: { + viewer: () => + pureMerge(viewer, { + ignoredUsers: [commenters[0], commenters[1]], + }), + }, + Mutation: { + removeUserIgnore: ({ variables }) => { + expectAndFail(variables).toMatchObject({ + userID: commenters[0].id, + }); + return {}; + }, + }, + }), + }); + const editButton = within(ignoredCommenters).getByText("Manage"); + act(() => { + editButton.props.onClick(); + }); + within(ignoredCommenters).getByText(commenters[0].username!); + within(ignoredCommenters).getByText(commenters[1].username!); + + // Stop ignoring first users. + within(ignoredCommenters) + .getAllByText("Stop ignoring", { selector: "button" })[0] + .props.onClick(); + + // First user should dissappear from list. + await waitUntilThrow(() => + within(ignoredCommenters).getByText(commenters[0].username!) + ); + within(ignoredCommenters).getByText(commenters[1].username!); +}); diff --git a/src/core/server/app/middleware/passport/strategies/verifiers/sso.ts b/src/core/server/app/middleware/passport/strategies/verifiers/sso.ts index 3a2306e7f..ad4bc1454 100644 --- a/src/core/server/app/middleware/passport/strategies/verifiers/sso.ts +++ b/src/core/server/app/middleware/passport/strategies/verifiers/sso.ts @@ -41,6 +41,7 @@ export interface SSOUserProfile { username: string; badges?: string[]; role?: GQLUSER_ROLE; + url?: string; } export interface SSOToken { @@ -64,8 +65,9 @@ export const SSOUserProfileSchema = Joi.object() username: Joi.string().required(), badges: Joi.array().items(Joi.string()), role: Joi.string().only(Object.values(GQLUSER_ROLE)), + url: Joi.string().uri(), }) - .optionalKeys(["badges", "role"]); + .optionalKeys(["badges", "role", "url"]); export const SSOTokenSchema = Joi.object() .keys({ @@ -91,7 +93,7 @@ export async function findOrCreateSSOUser( const { jti, exp, - user: { id, email, username, badges, role }, + user: { id, email, username, badges, role, url }, iat, } = decodedToken; @@ -109,6 +111,7 @@ export async function findOrCreateSSOUser( type: "sso", id, }); + if (!user) { if (!integration.allowRegistration) { // Registration is disabled, so we can't create the user user here. @@ -131,6 +134,7 @@ export async function findOrCreateSSOUser( id, username, role: role || GQLUSER_ROLE.COMMENTER, + ssoURL: url, badges, email, emailVerified: true, diff --git a/src/core/server/graph/schema/schema.graphql b/src/core/server/graph/schema/schema.graphql index d877ea72b..7a17b35ee 100644 --- a/src/core/server/graph/schema/schema.graphql +++ b/src/core/server/graph/schema/schema.graphql @@ -2098,6 +2098,11 @@ type User { roles: [ADMIN, MODERATOR] permit: [SUSPENDED, BANNED, PENDING_DELETION] ) + + """ + ssoURL is the url for managing sso account + """ + ssoURL: String } """ diff --git a/src/core/server/models/user/helpers.ts b/src/core/server/models/user/helpers.ts index 96fa6244e..df870fbf4 100644 --- a/src/core/server/models/user/helpers.ts +++ b/src/core/server/models/user/helpers.ts @@ -30,11 +30,12 @@ export function getSSOProfile(user: Pick) { export function needsSSOUpdate( token: SSOUserProfile, - user: Pick + user: Pick ) { return ( user.email !== token.email || user.username !== token.username || + (user.ssoURL && user.ssoURL !== token.url) || (token.role && user.role !== token.role) || !isEqual(user.badges, token.badges) ); diff --git a/src/core/server/models/user/user.ts b/src/core/server/models/user/user.ts index f3d206aeb..5d96a9ea0 100644 --- a/src/core/server/models/user/user.ts +++ b/src/core/server/models/user/user.ts @@ -392,6 +392,11 @@ export interface User extends TenantResource { */ badges?: string[]; + /** + * ssoURL is the url where a user can manage their sso account + */ + ssoURL?: string; + /** * emailVerificationID is used to store state regarding the verification state * of an email address to prevent replay attacks. @@ -485,6 +490,7 @@ export interface FindOrCreateUserInput { avatar?: string; email?: string; badges?: string[]; + ssoURL?: string; emailVerified?: boolean; role: GQLUSER_ROLE; profile: Profile; diff --git a/src/locales/en-US/stream.ftl b/src/locales/en-US/stream.ftl index 63c69387c..94d22019b 100644 --- a/src/locales/en-US/stream.ftl +++ b/src/locales/en-US/stream.ftl @@ -188,6 +188,7 @@ comments-featured-replies = Replies profile-myCommentsTab = My Comments profile-myCommentsTab-comments = My comments profile-accountTab = Account +profile-preferencesTab = Preferences accountSettings-manage-account = Manage your account