mirror of
https://github.com/wassname/talk.git
synced 2026-06-27 15:17:24 +08:00
[CORL-1248] Allow users to auto-show media in stream (#3086)
* Create preliminary media preferences Allows user to select whether they want to automatically show media or not. CORL-1248 * Fix up the copy around media preferences CORL-1248 * Rename EmbedPreferences to UserMediaSettings CORL-1248 * Add error/validation messages to media prefs CORL-1248 Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
This commit is contained in:
@@ -133,6 +133,7 @@ createComment.error
|
||||
- <a href="#unfeatureComment">unfeatureComment</a>
|
||||
- <a href="#updateNotificationSettings">updateNotificationSettings</a>
|
||||
- <a href="#updateStorySettings">updateStorySettings</a>
|
||||
- <a href="#updateUserMediaSettings">updateUserMediaSettings</a>
|
||||
- <a href="#viewConversation">viewConversation</a>
|
||||
- <a href="#viewFullDiscussion">viewFullDiscussion</a>
|
||||
- <a href="#viewNewComments">viewNewComments</a>
|
||||
@@ -587,6 +588,17 @@ createComment.error
|
||||
};
|
||||
}
|
||||
```
|
||||
- <a id="updateUserMediaSettings">**updateUserMediaSettings.success**, **updateUserMediaSettings.error**</a>:
|
||||
```ts
|
||||
{
|
||||
unfurlEmbeds?: boolean | null | undefined;
|
||||
success: {};
|
||||
error: {
|
||||
message: string;
|
||||
code?: string | undefined;
|
||||
};
|
||||
}
|
||||
```
|
||||
- <a id="viewConversation">**viewConversation**</a>: This event is emitted when the viewer changes to the single conversation view.
|
||||
```ts
|
||||
{
|
||||
|
||||
@@ -968,6 +968,10 @@ const CLASSES = {
|
||||
updateButton: "coral coral-emailNotifications-updateButton",
|
||||
},
|
||||
|
||||
mediaPreferences: {
|
||||
updateButton: "coral coral-mediaPreferences-updateButton",
|
||||
},
|
||||
|
||||
/**
|
||||
* spinner is the loading indicator.
|
||||
*/
|
||||
|
||||
@@ -217,6 +217,15 @@ export const UpdateNotificationSettingsEvent = createViewerNetworkEvent<{
|
||||
};
|
||||
}>("updateNotificationSettings");
|
||||
|
||||
export const UpdateUserMediaSettingsEvent = createViewerNetworkEvent<{
|
||||
unfurlEmbeds?: boolean | null;
|
||||
success: {};
|
||||
error: {
|
||||
message: string;
|
||||
code?: string;
|
||||
};
|
||||
}>("updateUserMediaSettings");
|
||||
|
||||
/**
|
||||
* This event is emitted when the viewer updates the story settings.
|
||||
*/
|
||||
|
||||
@@ -409,7 +409,11 @@ export const CommentContainer: FunctionComponent<Props> = ({
|
||||
</Flex>
|
||||
}
|
||||
media={
|
||||
<MediaSectionContainer comment={comment} settings={settings} />
|
||||
<MediaSectionContainer
|
||||
comment={comment}
|
||||
settings={settings}
|
||||
defaultExpanded={viewer?.mediaSettings?.unfurlEmbeds}
|
||||
/>
|
||||
}
|
||||
footer={
|
||||
<>
|
||||
@@ -533,6 +537,9 @@ const enhanced = withContext(({ eventEmitter }) => ({ eventEmitter }))(
|
||||
badges
|
||||
role
|
||||
scheduledDeletionDate
|
||||
mediaSettings {
|
||||
unfurlEmbeds
|
||||
}
|
||||
...UsernameWithPopoverContainer_viewer
|
||||
...ReactionButtonContainer_viewer
|
||||
...ReportFlowContainer_viewer
|
||||
|
||||
@@ -18,14 +18,16 @@ import styles from "./MediaSectionContainer.css";
|
||||
interface Props {
|
||||
comment: MediaSectionContainer_comment;
|
||||
settings: MediaSectionContainer_settings;
|
||||
defaultExpanded: boolean | null | undefined;
|
||||
}
|
||||
|
||||
const MediaSectionContainer: FunctionComponent<Props> = ({
|
||||
comment,
|
||||
settings,
|
||||
defaultExpanded,
|
||||
}) => {
|
||||
const { revision } = comment;
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const [expanded, setExpanded] = useState(defaultExpanded ? true : false);
|
||||
const onToggleExpand = useCallback(() => {
|
||||
setExpanded((v) => !v);
|
||||
}, []);
|
||||
|
||||
+8
-1
@@ -68,7 +68,11 @@ const FeaturedCommentContainer: FunctionComponent<Props> = (props) => {
|
||||
>
|
||||
{comment.body || ""}
|
||||
</HTMLContent>
|
||||
<MediaSectionContainer comment={comment} settings={settings} />
|
||||
<MediaSectionContainer
|
||||
comment={comment}
|
||||
settings={settings}
|
||||
defaultExpanded={viewer?.mediaSettings?.unfurlEmbeds}
|
||||
/>
|
||||
</HorizontalGutter>
|
||||
<Flex
|
||||
direction="row"
|
||||
@@ -165,6 +169,9 @@ const enhanced = withSetCommentIDMutation(
|
||||
ignoredUsers {
|
||||
id
|
||||
}
|
||||
mediaSettings {
|
||||
unfurlEmbeds
|
||||
}
|
||||
role
|
||||
...UsernameWithPopoverContainer_viewer
|
||||
...ReactionButtonContainer_viewer
|
||||
|
||||
@@ -59,6 +59,7 @@ const HistoryCommentContainer: FunctionComponent<Props> = (props) => {
|
||||
<MediaSectionContainer
|
||||
comment={props.comment}
|
||||
settings={props.settings}
|
||||
defaultExpanded={false}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
.form {
|
||||
padding-bottom: var(--spacing-1);
|
||||
}
|
||||
|
||||
.title {
|
||||
font-family: var(--font-family-primary);
|
||||
font-weight: var(--font-weight-primary-semi-bold);
|
||||
font-size: var(--font-size-4);
|
||||
line-height: 1.11;
|
||||
|
||||
color: var(--palette-text-900);
|
||||
|
||||
padding-bottom: var(--spacing-4);
|
||||
}
|
||||
|
||||
.options {
|
||||
padding-bottom: var(--spacing-3);
|
||||
}
|
||||
|
||||
.checkBoxDescription {
|
||||
font-family: var(--font-family-primary);
|
||||
font-weight: var(--font-weight-primary-regular);
|
||||
font-size: var(--font-size-2);
|
||||
|
||||
color: var(--palette-grey-500);
|
||||
|
||||
padding-left: calc(var(--spacing-3) + 14px);
|
||||
}
|
||||
|
||||
.updateButton {
|
||||
padding-top: var(--spacing-2);
|
||||
padding-bottom: var(--spacing-1);
|
||||
}
|
||||
|
||||
.updateButtonNotification {
|
||||
padding-top: var(--spacing-2);
|
||||
padding-bottom: var(--spacing-3);
|
||||
}
|
||||
|
||||
.callOut {
|
||||
padding-bottom: var(--spacing-2);
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
import { Localized } from "@fluent/react/compat";
|
||||
import cn from "classnames";
|
||||
import { FORM_ERROR } from "final-form";
|
||||
import React, { FunctionComponent, useCallback, useState } from "react";
|
||||
import { Field, Form } from "react-final-form";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { InvalidRequestError } from "coral-framework/lib/errors";
|
||||
import { useMutation, withFragmentContainer } from "coral-framework/lib/relay";
|
||||
import CLASSES from "coral-stream/classes";
|
||||
import {
|
||||
CheckBox,
|
||||
FieldSet,
|
||||
FormField,
|
||||
HorizontalGutter,
|
||||
Icon,
|
||||
} from "coral-ui/components/v2";
|
||||
import { Button, CallOut } from "coral-ui/components/v3";
|
||||
|
||||
import { MediaSettingsContainer_viewer } from "coral-stream/__generated__/MediaSettingsContainer_viewer.graphql";
|
||||
|
||||
import UpdateUserMediaSettingsMutation from "./UpdateUserMediaSettingsMutation";
|
||||
|
||||
import styles from "./MediaSettingsContainer.css";
|
||||
|
||||
interface Props {
|
||||
viewer: MediaSettingsContainer_viewer;
|
||||
}
|
||||
|
||||
const MediaSettingsContainer: FunctionComponent<Props> = ({ viewer }) => {
|
||||
const updateMediaSettings = useMutation(UpdateUserMediaSettingsMutation);
|
||||
const [showSuccess, setShowSuccess] = useState(false);
|
||||
const [showError, setShowError] = useState(false);
|
||||
const closeSuccess = useCallback(() => {
|
||||
setShowSuccess(false);
|
||||
}, [setShowSuccess]);
|
||||
const closeError = useCallback(() => {
|
||||
setShowError(false);
|
||||
}, [setShowError]);
|
||||
const onSubmit = useCallback(
|
||||
async (values) => {
|
||||
try {
|
||||
await updateMediaSettings(values);
|
||||
setShowSuccess(true);
|
||||
} catch (err) {
|
||||
if (err instanceof InvalidRequestError) {
|
||||
return err.invalidArgs;
|
||||
}
|
||||
|
||||
setShowError(true);
|
||||
return {
|
||||
[FORM_ERROR]: err.message,
|
||||
};
|
||||
}
|
||||
|
||||
return;
|
||||
},
|
||||
[updateMediaSettings, setShowSuccess, setShowError]
|
||||
);
|
||||
|
||||
return (
|
||||
<HorizontalGutter>
|
||||
<Form initialValues={viewer.mediaSettings} onSubmit={onSubmit}>
|
||||
{({
|
||||
handleSubmit,
|
||||
submitting,
|
||||
submitError,
|
||||
pristine,
|
||||
submitSucceeded,
|
||||
}) => (
|
||||
<form className={styles.form} onSubmit={handleSubmit}>
|
||||
<Localized id="profile-preferences-mediaPreferences">
|
||||
<div className={styles.title}>Media Preferences</div>
|
||||
</Localized>
|
||||
<div className={styles.options}>
|
||||
<FieldSet>
|
||||
<FormField>
|
||||
<Field name="unfurlEmbeds" type="checkbox">
|
||||
{({ input }) => (
|
||||
<CheckBox {...input} id={input.name} variant="streamBlue">
|
||||
<Localized id="profile-preferences-mediaPreferences-alwaysShow">
|
||||
<div>Always show GIFs, Tweets, YouTube, etc.</div>
|
||||
</Localized>
|
||||
</CheckBox>
|
||||
)}
|
||||
</Field>
|
||||
</FormField>
|
||||
</FieldSet>
|
||||
<Localized id="profile-preferences-mediaPreferences-thisMayMake">
|
||||
<div className={styles.checkBoxDescription}>
|
||||
This may make the comments slower to load
|
||||
</div>
|
||||
</Localized>
|
||||
</div>
|
||||
<div
|
||||
className={cn(styles.updateButton, {
|
||||
[styles.updateButtonNotification]: showSuccess || showError,
|
||||
})}
|
||||
>
|
||||
<Localized id="profile-preferences-mediaPreferences-update">
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={submitting || pristine}
|
||||
className={CLASSES.mediaPreferences.updateButton}
|
||||
upperCase
|
||||
>
|
||||
Update
|
||||
</Button>
|
||||
</Localized>
|
||||
</div>
|
||||
{((submitError && showError) ||
|
||||
(submitSucceeded && showSuccess)) && (
|
||||
<div className={styles.callOut}>
|
||||
{submitError && showError && (
|
||||
<CallOut
|
||||
color="error"
|
||||
onClose={closeError}
|
||||
icon={<Icon size="sm">warning</Icon>}
|
||||
titleWeight="semiBold"
|
||||
title={<span>{submitError}</span>}
|
||||
/>
|
||||
)}
|
||||
{submitSucceeded && showSuccess && (
|
||||
<CallOut
|
||||
color="success"
|
||||
onClose={closeSuccess}
|
||||
icon={<Icon size="sm">check_circle</Icon>}
|
||||
titleWeight="semiBold"
|
||||
title={
|
||||
<Localized id="profile-preferences-mediaPreferences-preferencesUpdated">
|
||||
<span>Your media preferences have been updated</span>
|
||||
</Localized>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</form>
|
||||
)}
|
||||
</Form>
|
||||
</HorizontalGutter>
|
||||
);
|
||||
};
|
||||
|
||||
const enhanced = withFragmentContainer<Props>({
|
||||
viewer: graphql`
|
||||
fragment MediaSettingsContainer_viewer on User {
|
||||
id
|
||||
mediaSettings {
|
||||
unfurlEmbeds
|
||||
}
|
||||
}
|
||||
`,
|
||||
})(MediaSettingsContainer);
|
||||
|
||||
export default enhanced;
|
||||
@@ -2,11 +2,12 @@ import React, { FunctionComponent } from "react";
|
||||
import { graphql } from "react-relay";
|
||||
|
||||
import { withFragmentContainer } from "coral-framework/lib/relay";
|
||||
import { HorizontalGutter } from "coral-ui/components/v2";
|
||||
import { HorizontalGutter, HorizontalRule } from "coral-ui/components/v2";
|
||||
|
||||
import { PreferencesContainer_viewer } from "coral-stream/__generated__/PreferencesContainer_viewer.graphql";
|
||||
|
||||
import IgnoreUserSettingsContainer from "./IgnoreUserSettingsContainer";
|
||||
import MediaSettingsContainer from "./MediaSettingsContainer";
|
||||
import NotificationSettingsContainer from "./NotificationSettingsContainer";
|
||||
|
||||
interface Props {
|
||||
@@ -17,6 +18,8 @@ const PreferencesContainer: FunctionComponent<Props> = (props) => {
|
||||
return (
|
||||
<HorizontalGutter spacing={4}>
|
||||
<NotificationSettingsContainer viewer={props.viewer} />
|
||||
<MediaSettingsContainer viewer={props.viewer} />
|
||||
<HorizontalRule></HorizontalRule>
|
||||
<IgnoreUserSettingsContainer viewer={props.viewer} />
|
||||
</HorizontalGutter>
|
||||
);
|
||||
@@ -27,6 +30,7 @@ const enhanced = withFragmentContainer<Props>({
|
||||
fragment PreferencesContainer_viewer on User {
|
||||
...NotificationSettingsContainer_viewer
|
||||
...IgnoreUserSettingsContainer_viewer
|
||||
...MediaSettingsContainer_viewer
|
||||
}
|
||||
`,
|
||||
})(PreferencesContainer);
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import { graphql } from "react-relay";
|
||||
import { Environment } from "relay-runtime";
|
||||
|
||||
import { CoralContext } from "coral-framework/lib/bootstrap";
|
||||
import {
|
||||
commitMutationPromiseNormalized,
|
||||
createMutation,
|
||||
MutationInput,
|
||||
} from "coral-framework/lib/relay";
|
||||
import { UpdateUserMediaSettingsEvent } from "coral-stream/events";
|
||||
|
||||
import { UpdateUserMediaSettingsMutation as MutationTypes } from "coral-stream/__generated__/UpdateUserMediaSettingsMutation.graphql";
|
||||
|
||||
let clientMutationId = 0;
|
||||
|
||||
const UpdateUserMediaSettingsMutation = createMutation(
|
||||
"updateUserMediaSettings",
|
||||
async (
|
||||
environment: Environment,
|
||||
input: MutationInput<MutationTypes>,
|
||||
{ eventEmitter }: CoralContext
|
||||
) => {
|
||||
const updateMediaSettings = UpdateUserMediaSettingsEvent.begin(
|
||||
eventEmitter,
|
||||
{
|
||||
unfurlEmbeds: input.unfurlEmbeds,
|
||||
}
|
||||
);
|
||||
try {
|
||||
const result = await commitMutationPromiseNormalized<MutationTypes>(
|
||||
environment,
|
||||
{
|
||||
mutation: graphql`
|
||||
mutation UpdateUserMediaSettingsMutation(
|
||||
$input: UpdateUserMediaSettingsInput!
|
||||
) {
|
||||
updateUserMediaSettings(input: $input) {
|
||||
user {
|
||||
id
|
||||
mediaSettings {
|
||||
unfurlEmbeds
|
||||
}
|
||||
}
|
||||
clientMutationId
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
input: {
|
||||
...input,
|
||||
clientMutationId: (clientMutationId++).toString(),
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
updateMediaSettings.success();
|
||||
return result;
|
||||
} catch (error) {
|
||||
updateMediaSettings.error({
|
||||
message: error.message,
|
||||
code: error.code,
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export default UpdateUserMediaSettingsMutation;
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
updateAvatar,
|
||||
updateEmail,
|
||||
updateEmailByID,
|
||||
updateMediaSettings,
|
||||
updateModerationScopes,
|
||||
updateNotificationSettings,
|
||||
updatePassword,
|
||||
@@ -62,6 +63,7 @@ import {
|
||||
GQLUpdatePasswordInput,
|
||||
GQLUpdateUserAvatarInput,
|
||||
GQLUpdateUserEmailInput,
|
||||
GQLUpdateUserMediaSettingsInput,
|
||||
GQLUpdateUserModerationScopesInput,
|
||||
GQLUpdateUsernameInput,
|
||||
GQLUpdateUserRoleInput,
|
||||
@@ -206,6 +208,9 @@ export const Users = (ctx: GraphContext) => ({
|
||||
updateNotificationSettings: async (
|
||||
input: WithoutMutationID<GQLUpdateNotificationSettingsInput>
|
||||
) => updateNotificationSettings(ctx.mongo, ctx.tenant, ctx.user!, input),
|
||||
updateUserMediaSettings: async (
|
||||
input: WithoutMutationID<GQLUpdateUserMediaSettingsInput>
|
||||
) => updateMediaSettings(ctx.mongo, ctx.tenant, ctx.user!, input),
|
||||
updateUserAvatar: async (input: GQLUpdateUserAvatarInput) =>
|
||||
updateAvatar(ctx.mongo, ctx.tenant, input.userID, input.avatar),
|
||||
updateUserRole: async (input: GQLUpdateUserRoleInput) =>
|
||||
|
||||
@@ -31,6 +31,14 @@ export const Mutation: Required<GQLMutationTypeResolver<void>> = {
|
||||
user: await ctx.mutators.Users.updateNotificationSettings(input),
|
||||
clientMutationId,
|
||||
}),
|
||||
updateUserMediaSettings: async (
|
||||
source,
|
||||
{ input: { clientMutationId, ...input } },
|
||||
ctx
|
||||
) => ({
|
||||
user: await ctx.mutators.Users.updateUserMediaSettings(input),
|
||||
clientMutationId,
|
||||
}),
|
||||
updateSettings: async (
|
||||
source,
|
||||
{ input: { clientMutationId, ...input } },
|
||||
|
||||
@@ -2184,6 +2184,17 @@ enum DIGEST_FREQUENCY {
|
||||
HOURLY
|
||||
}
|
||||
|
||||
"""
|
||||
UserMediaSettings are the user's preferences around embed stream behaviour.
|
||||
"""
|
||||
type UserMediaSettings {
|
||||
"""
|
||||
unfurlEmbeds is whether the user has chosen to immediately show embed contents
|
||||
without having to click "Show Tweet", "Show GIF", etc.
|
||||
"""
|
||||
unfurlEmbeds: Boolean
|
||||
}
|
||||
|
||||
"""
|
||||
UserNotificationSettings stores the notification settings for a given User.
|
||||
"""
|
||||
@@ -2458,6 +2469,11 @@ type User {
|
||||
"""
|
||||
ssoURL: String
|
||||
@auth(userIDField: "id", permit: [SUSPENDED, BANNED, PENDING_DELETION])
|
||||
|
||||
"""
|
||||
mediaSettings are the user's preferences around media stream behaviour.
|
||||
"""
|
||||
mediaSettings: UserMediaSettings
|
||||
}
|
||||
|
||||
"""
|
||||
@@ -3625,6 +3641,16 @@ type UpdateNotificationSettingsPayload {
|
||||
clientMutationId: String!
|
||||
}
|
||||
|
||||
input UpdateUserMediaSettingsInput {
|
||||
unfurlEmbeds: Boolean
|
||||
clientMutationId: String!
|
||||
}
|
||||
|
||||
type UpdateUserMediaSettingsPayload {
|
||||
user: User!
|
||||
clientMutationId: String!
|
||||
}
|
||||
|
||||
##################
|
||||
## createComment
|
||||
##################
|
||||
@@ -7335,6 +7361,14 @@ type Mutation {
|
||||
): UpdateNotificationSettingsPayload!
|
||||
@auth(permit: [SUSPENDED, BANNED, PENDING_DELETION])
|
||||
|
||||
"""
|
||||
updateUserMediaSettings can be used to update the media preferences for the
|
||||
current logged in user.
|
||||
"""
|
||||
updateUserMediaSettings(
|
||||
input: UpdateUserMediaSettingsInput!
|
||||
): UpdateUserMediaSettingsPayload! @auth
|
||||
|
||||
"""
|
||||
updateUserEmail allows administrators to update a given User's email address
|
||||
to the one provided.
|
||||
|
||||
@@ -37,6 +37,7 @@ import {
|
||||
GQLPremodStatus,
|
||||
GQLSuspensionStatus,
|
||||
GQLTimeRange,
|
||||
GQLUpdateUserMediaSettingsInput,
|
||||
GQLUSER_ROLE,
|
||||
GQLUsernameStatus,
|
||||
GQLUserNotificationSettings,
|
||||
@@ -2319,6 +2320,45 @@ export async function updateUserNotificationSettings(
|
||||
return result.value;
|
||||
}
|
||||
|
||||
export type UpdateUserMediaSettingsInput = Partial<
|
||||
GQLUpdateUserMediaSettingsInput
|
||||
>;
|
||||
|
||||
export async function updateUserMediaSettings(
|
||||
mongo: Db,
|
||||
tenantID: string,
|
||||
id: string,
|
||||
settings: UpdateUserMediaSettingsInput
|
||||
) {
|
||||
const result = await collection(mongo).findOneAndUpdate(
|
||||
{
|
||||
id,
|
||||
tenantID,
|
||||
},
|
||||
{
|
||||
$set: dotize({
|
||||
mediaSettings: settings,
|
||||
}),
|
||||
},
|
||||
{
|
||||
// False to return the updated document instead of the original
|
||||
// document.
|
||||
returnOriginal: false,
|
||||
}
|
||||
);
|
||||
if (!result.value) {
|
||||
// Get the user so we can figure out why the update operation failed.
|
||||
const user = await retrieveUser(mongo, tenantID, id);
|
||||
if (!user) {
|
||||
throw new UserNotFoundError(id);
|
||||
}
|
||||
|
||||
throw new Error("an unexpected error occurred");
|
||||
}
|
||||
|
||||
return result.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* insertUserNotificationDigests will push the notification contexts onto the
|
||||
* User so that notifications can now be queued.
|
||||
|
||||
@@ -68,6 +68,8 @@ import {
|
||||
suspendUser,
|
||||
updateUserAvatar,
|
||||
updateUserEmail,
|
||||
updateUserMediaSettings,
|
||||
UpdateUserMediaSettingsInput,
|
||||
updateUserModerationScopes,
|
||||
updateUserNotificationSettings,
|
||||
updateUserPassword,
|
||||
@@ -1275,6 +1277,15 @@ export async function updateNotificationSettings(
|
||||
return updateUserNotificationSettings(mongo, tenant.id, user.id, settings);
|
||||
}
|
||||
|
||||
export async function updateMediaSettings(
|
||||
mongo: Db,
|
||||
tenant: Tenant,
|
||||
user: User,
|
||||
settings: UpdateUserMediaSettingsInput
|
||||
) {
|
||||
return updateUserMediaSettings(mongo, tenant.id, user.id, settings);
|
||||
}
|
||||
|
||||
function userLastCommentIDKey(
|
||||
tenant: Pick<Tenant, "id">,
|
||||
user: Pick<User, "id">
|
||||
|
||||
@@ -338,6 +338,15 @@ profile-commentHistory-loadMore = Load More
|
||||
profile-commentHistory-empty = You have not written any comments
|
||||
profile-commentHistory-empty-subheading = A history of your comments will appear here
|
||||
|
||||
### Preferences
|
||||
|
||||
profile-preferences-mediaPreferences = Media Preferences
|
||||
profile-preferences-mediaPreferences-alwaysShow = Always show GIFs, Tweets, YouTube, etc.
|
||||
profile-preferences-mediaPreferences-thisMayMake = This may make the comments slower to load
|
||||
profile-preferences-mediaPreferences-update = Update
|
||||
profile-preferences-mediaPreferences-preferencesUpdated =
|
||||
Your media preferences have been updated
|
||||
|
||||
### Account
|
||||
profile-account-ignoredCommenters = Ignored Commenters
|
||||
profile-account-ignoredCommenters-description =
|
||||
|
||||
Reference in New Issue
Block a user