import { LanguageSelect } from "../common/language-select";
import { ListingTypeSelect } from "../common/listing-type-select";
import { MarkdownTextArea } from "../common/markdown-textarea";
+import PasswordInput from "../common/password-input";
import { SearchableSelect } from "../common/searchable-select";
import { SortSelect } from "../common/sort-select";
import Tabs from "../common/tabs";
show_new_post_notifs?: boolean;
discussion_languages?: number[];
generate_totp_2fa?: boolean;
+ open_links_in_new_tab?: boolean;
};
changePasswordForm: {
new_password?: string;
);
}
- userSettings(isSelected) {
+ userSettings(isSelected: boolean) {
return (
<div
className={classNames("tab-pane show", {
);
}
- blockCards(isSelected) {
+ blockCards(isSelected: boolean) {
return (
<div
className={classNames("tab-pane", {
changePasswordHtmlForm() {
return (
<>
- <h5>{I18NextService.i18n.t("change_password")}</h5>
+ <h2 className="h5">{I18NextService.i18n.t("change_password")}</h2>
<form onSubmit={linkEvent(this, this.handleChangePasswordSubmit)}>
- <div className="mb-3 row">
- <label className="col-sm-5 col-form-label" htmlFor="user-password">
- {I18NextService.i18n.t("new_password")}
- </label>
- <div className="col-sm-7">
- <input
- type="password"
- id="user-password"
- className="form-control"
- value={this.state.changePasswordForm.new_password}
- autoComplete="new-password"
- maxLength={60}
- onInput={linkEvent(this, this.handleNewPasswordChange)}
- />
- </div>
+ <div className="mb-3">
+ <PasswordInput
+ id="new-password"
+ value={this.state.changePasswordForm.new_password}
+ onInput={linkEvent(this, this.handleNewPasswordChange)}
+ showStrength
+ label={I18NextService.i18n.t("new_password")}
+ isNew
+ />
</div>
- <div className="mb-3 row">
- <label
- className="col-sm-5 col-form-label"
- htmlFor="user-verify-password"
- >
- {I18NextService.i18n.t("verify_password")}
- </label>
- <div className="col-sm-7">
- <input
- type="password"
- id="user-verify-password"
- className="form-control"
- value={this.state.changePasswordForm.new_password_verify}
- autoComplete="new-password"
- maxLength={60}
- onInput={linkEvent(this, this.handleNewPasswordVerifyChange)}
- />
- </div>
+ <div className="mb-3">
+ <PasswordInput
+ id="verify-new-password"
+ value={this.state.changePasswordForm.new_password_verify}
+ onInput={linkEvent(this, this.handleNewPasswordVerifyChange)}
+ label={I18NextService.i18n.t("verify_password")}
+ isNew
+ />
</div>
- <div className="mb-3 row">
- <label
- className="col-sm-5 col-form-label"
- htmlFor="user-old-password"
- >
- {I18NextService.i18n.t("old_password")}
- </label>
- <div className="col-sm-7">
- <input
- type="password"
- id="user-old-password"
- className="form-control"
- value={this.state.changePasswordForm.old_password}
- autoComplete="new-password"
- maxLength={60}
- onInput={linkEvent(this, this.handleOldPasswordChange)}
- />
- </div>
+ <div className="mb-3">
+ <PasswordInput
+ id="user-old-password"
+ value={this.state.changePasswordForm.old_password}
+ onInput={linkEvent(this, this.handleOldPasswordChange)}
+ label={I18NextService.i18n.t("old_password")}
+ />
</div>
<div className="input-group mb-3">
<button
blockedUsersList() {
return (
<>
- <h5>{I18NextService.i18n.t("blocked_users")}</h5>
+ <h2 className="h5">{I18NextService.i18n.t("blocked_users")}</h2>
<ul className="list-unstyled mb-0">
{this.state.personBlocks.map(pb => (
<li key={pb.target.id}>
className="btn btn-sm"
onClick={linkEvent(
{ ctx: this, recipientId: pb.target.id },
- this.handleUnblockPerson
+ this.handleUnblockPerson,
)}
data-tippy-content={I18NextService.i18n.t("unblock_user")}
>
blockedCommunitiesList() {
return (
<>
- <h5>{I18NextService.i18n.t("blocked_communities")}</h5>
+ <h2 className="h5">{I18NextService.i18n.t("blocked_communities")}</h2>
<ul className="list-unstyled mb-0">
{this.state.communityBlocks.map(cb => (
<li key={cb.community.id}>
className="btn btn-sm"
onClick={linkEvent(
{ ctx: this, communityId: cb.community.id },
- this.handleUnblockCommunity
+ this.handleUnblockCommunity,
)}
data-tippy-content={I18NextService.i18n.t(
- "unblock_community"
+ "unblock_community",
)}
>
<Icon icon="x" classes="icon-inline" />
return (
<>
- <h5>{I18NextService.i18n.t("settings")}</h5>
+ <h2 className="h5">{I18NextService.i18n.t("settings")}</h2>
<form onSubmit={linkEvent(this, this.handleSaveSettingsSubmit)}>
<div className="mb-3 row">
<label className="col-sm-3 col-form-label" htmlFor="display-name">
selectedLanguageIds={selectedLangs}
multiple={true}
showLanguageWarning={true}
+ showAll={true}
showSite
onChange={this.handleDiscussionLanguageChange}
/>
<option value="browser">
{I18NextService.i18n.t("browser_default")}
</option>
+ <option value="browser-compact">
+ {I18NextService.i18n.t("browser_default_compact")}
+ </option>
{this.state.themeList.map(theme => (
<option key={theme} value={theme}>
{theme}
}
onChange={linkEvent(
this,
- this.handleSendNotificationsToEmailChange
+ this.handleSendNotificationsToEmailChange,
)}
/>
<label
</label>
</div>
</div>
+ <div className="input-group mb-3">
+ <div className="form-check">
+ <input
+ className="form-check-input"
+ id="user-open-links-in-new-tab"
+ type="checkbox"
+ checked={this.state.saveUserSettingsForm.open_links_in_new_tab}
+ onChange={linkEvent(this, this.handleOpenInNewTab)}
+ />
+ <label
+ className="form-check-label"
+ htmlFor="user-open-links-in-new-tab"
+ >
+ {I18NextService.i18n.t("open_links_in_new_tab")}
+ </label>
+ </div>
+ </div>
{this.totpSection()}
<div className="input-group mb-3">
<button type="submit" className="btn d-block btn-secondary me-4">
</button>
</div>
<hr />
- <div className="input-group mb-3">
+ <form
+ className="mb-3"
+ onSubmit={linkEvent(this, this.handleDeleteAccount)}
+ >
<button
+ type="button"
className="btn d-block btn-danger"
onClick={linkEvent(
this,
- this.handleDeleteAccountShowConfirmToggle
+ this.handleDeleteAccountShowConfirmToggle,
)}
>
{I18NextService.i18n.t("delete_account")}
</button>
{this.state.deleteAccountShowConfirm && (
<>
- <div className="my-2 alert alert-danger" role="alert">
+ <label
+ className="my-2 alert alert-danger d-block"
+ role="alert"
+ htmlFor="password-delete-account"
+ >
{I18NextService.i18n.t("delete_account_confirm")}
- </div>
- <input
- type="password"
+ </label>
+ <PasswordInput
+ id="password-delete-account"
value={this.state.deleteAccountForm.password}
- autoComplete="new-password"
- maxLength={60}
onInput={linkEvent(
this,
- this.handleDeleteAccountPasswordChange
+ this.handleDeleteAccountPasswordChange,
)}
- className="form-control my-2"
+ className="my-2"
/>
<button
+ type="submit"
className="btn btn-danger me-4"
disabled={!this.state.deleteAccountForm.password}
- onClick={linkEvent(this, this.handleDeleteAccount)}
>
{this.state.deleteAccountRes.state === "loading" ? (
<Spinner />
</button>
<button
className="btn btn-secondary"
+ type="button"
onClick={linkEvent(
this,
- this.handleDeleteAccountShowConfirmToggle
+ this.handleDeleteAccountShowConfirmToggle,
)}
>
{I18NextService.i18n.t("cancel")}
</button>
</>
)}
- </div>
+ </form>
</form>
</>
);
id="user-remove-totp"
type="checkbox"
checked={
- this.state.saveUserSettingsForm.generate_totp_2fa == false
+ this.state.saveUserSettingsForm.generate_totp_2fa === false
}
onChange={linkEvent(this, this.handleRemoveTotp)}
/>
if (text.length > 0) {
searchCommunityOptions.push(
- ...(await fetchCommunities(text)).map(communityToChoice)
+ ...(await fetchCommunities(text)).map(communityToChoice),
);
}
handleShowNsfwChange(i: Settings, event: any) {
i.setState(
- s => ((s.saveUserSettingsForm.show_nsfw = event.target.checked), s)
+ s => ((s.saveUserSettingsForm.show_nsfw = event.target.checked), s),
);
}
mui.local_user_view.local_user.show_avatars = event.target.checked;
}
i.setState(
- s => ((s.saveUserSettingsForm.show_avatars = event.target.checked), s)
+ s => ((s.saveUserSettingsForm.show_avatars = event.target.checked), s),
);
}
handleBotAccount(i: Settings, event: any) {
i.setState(
- s => ((s.saveUserSettingsForm.bot_account = event.target.checked), s)
+ s => ((s.saveUserSettingsForm.bot_account = event.target.checked), s),
);
}
i.setState(
s => (
(s.saveUserSettingsForm.show_bot_accounts = event.target.checked), s
- )
+ ),
);
}
handleReadPosts(i: Settings, event: any) {
i.setState(
- s => ((s.saveUserSettingsForm.show_read_posts = event.target.checked), s)
+ s => ((s.saveUserSettingsForm.show_read_posts = event.target.checked), s),
);
}
i.setState(
s => (
(s.saveUserSettingsForm.show_new_post_notifs = event.target.checked), s
- )
+ ),
+ );
+ }
+
+ handleOpenInNewTab(i: Settings, event: any) {
+ i.setState(
+ s => (
+ (s.saveUserSettingsForm.open_links_in_new_tab = event.target.checked), s
+ ),
);
}
mui.local_user_view.local_user.show_scores = event.target.checked;
}
i.setState(
- s => ((s.saveUserSettingsForm.show_scores = event.target.checked), s)
+ s => ((s.saveUserSettingsForm.show_scores = event.target.checked), s),
);
}
(s.saveUserSettingsForm.send_notifications_to_email =
event.target.checked),
s
- )
+ ),
);
}
handleInterfaceLangChange(i: Settings, event: any) {
const newLang = event.target.value ?? "browser";
I18NextService.i18n.changeLanguage(
- newLang === "browser" ? navigator.languages : newLang
+ newLang === "browser" ? navigator.languages : newLang,
);
i.setState(
- s => ((s.saveUserSettingsForm.interface_language = event.target.value), s)
+ s => (
+ (s.saveUserSettingsForm.interface_language = event.target.value), s
+ ),
);
}
handleDiscussionLanguageChange(val: number[]) {
this.setState(
- s => ((s.saveUserSettingsForm.discussion_languages = val), s)
+ s => ((s.saveUserSettingsForm.discussion_languages = val), s),
);
}
handleListingTypeChange(val: ListingType) {
this.setState(
- s => ((s.saveUserSettingsForm.default_listing_type = val), s)
+ s => ((s.saveUserSettingsForm.default_listing_type = val), s),
);
}
handleDisplayNameChange(i: Settings, event: any) {
i.setState(
- s => ((s.saveUserSettingsForm.display_name = event.target.value), s)
+ s => ((s.saveUserSettingsForm.display_name = event.target.value), s),
);
}
handleMatrixUserIdChange(i: Settings, event: any) {
i.setState(
- s => ((s.saveUserSettingsForm.matrix_user_id = event.target.value), s)
+ s => ((s.saveUserSettingsForm.matrix_user_id = event.target.value), s),
);
}
handleNewPasswordChange(i: Settings, event: any) {
const newPass: string | undefined =
- event.target.value == "" ? undefined : event.target.value;
+ event.target.value === "" ? undefined : event.target.value;
i.setState(s => ((s.changePasswordForm.new_password = newPass), s));
}
handleNewPasswordVerifyChange(i: Settings, event: any) {
const newPassVerify: string | undefined =
- event.target.value == "" ? undefined : event.target.value;
+ event.target.value === "" ? undefined : event.target.value;
i.setState(
- s => ((s.changePasswordForm.new_password_verify = newPassVerify), s)
+ s => ((s.changePasswordForm.new_password_verify = newPassVerify), s),
);
}
handleOldPasswordChange(i: Settings, event: any) {
const oldPass: string | undefined =
- event.target.value == "" ? undefined : event.target.value;
+ event.target.value === "" ? undefined : event.target.value;
i.setState(s => ((s.changePasswordForm.old_password = oldPass), s));
}
...i.state.saveUserSettingsForm,
auth: myAuthRequired(),
});
+
if (saveRes.state === "success") {
- UserService.Instance.login(saveRes.data);
+ UserService.Instance.login({
+ res: saveRes.data,
+ showToast: false,
+ });
toast(I18NextService.i18n.t("saved"));
window.scrollTo(0, 0);
}
auth: myAuthRequired(),
});
if (changePasswordRes.state === "success") {
- UserService.Instance.login(changePasswordRes.data);
+ UserService.Instance.login({
+ res: changePasswordRes.data,
+ showToast: false,
+ });
window.scrollTo(0, 0);
toast(I18NextService.i18n.t("password_changed"));
}
i.setState(s => ((s.deleteAccountForm.password = event.target.value), s));
}
- async handleDeleteAccount(i: Settings) {
+ async handleDeleteAccount(i: Settings, event: Event) {
+ event.preventDefault();
const password = i.state.deleteAccountForm.password;
if (password) {
i.setState({ deleteAccountRes: { state: "loading" } });