-import { NoOptionI18nKeys } from "i18next";
-import { Component, linkEvent } from "inferno";
-import {
- BlockCommunity,
- BlockCommunityResponse,
- BlockPerson,
- BlockPersonResponse,
- ChangePassword,
- CommunityBlockView,
- DeleteAccount,
- GetSiteResponse,
- ListingType,
- LoginResponse,
- PersonBlockView,
- SaveUserSettings,
- SortType,
- UserOperation,
- wsJsonToRes,
- wsUserOp,
-} from "lemmy-js-client";
-import { Subscription } from "rxjs";
-import { i18n, languages } from "../../i18next";
-import { UserService, WebSocketService } from "../../services";
import {
- Choice,
- capitalizeFirstLetter,
communityToChoice,
- debounce,
- elementUrl,
- emDash,
- enableNsfw,
fetchCommunities,
fetchThemeList,
fetchUsers,
- getLanguages,
myAuth,
+ myAuthRequired,
personToChoice,
- relTags,
setIsoData,
setTheme,
- setupTippy,
showLocal,
- toast,
updateCommunityBlock,
updatePersonBlock,
- wsClient,
- wsSubscribe,
-} from "../../utils";
+} from "@utils/app";
+import { capitalizeFirstLetter, debounce } from "@utils/helpers";
+import { Choice } from "@utils/types";
+import classNames from "classnames";
+import { NoOptionI18nKeys } from "i18next";
+import { Component, linkEvent } from "inferno";
+import {
+ BlockCommunityResponse,
+ BlockPersonResponse,
+ CommunityBlockView,
+ DeleteAccountResponse,
+ GetSiteResponse,
+ ListingType,
+ LoginResponse,
+ PersonBlockView,
+ SortType,
+} from "lemmy-js-client";
+import { elementUrl, emDash, relTags } from "../../config";
+import { UserService } from "../../services";
+import { HttpService, RequestState } from "../../services/HttpService";
+import { I18NextService, languages } from "../../services/I18NextService";
+import { setupTippy } from "../../tippy";
+import { toast } from "../../toast";
import { HtmlTags } from "../common/html-tags";
import { Icon, Spinner } from "../common/icon";
import { ImageUploadForm } from "../common/image-upload-form";
import { MarkdownTextArea } from "../common/markdown-textarea";
import { SearchableSelect } from "../common/searchable-select";
import { SortSelect } from "../common/sort-select";
+import Tabs from "../common/tabs";
import { CommunityLink } from "../community/community-link";
import { PersonListing } from "./person-listing";
interface SettingsState {
+ saveRes: RequestState<LoginResponse>;
+ changePasswordRes: RequestState<LoginResponse>;
+ deleteAccountRes: RequestState<DeleteAccountResponse>;
// TODO redo these forms
saveUserSettingsForm: {
show_nsfw?: boolean;
communityBlocks: CommunityBlockView[];
currentTab: string;
themeList: string[];
- saveUserSettingsLoading: boolean;
- changePasswordLoading: boolean;
- deleteAccountLoading: boolean;
deleteAccountShowConfirm: boolean;
siteRes: GetSiteResponse;
searchCommunityLoading: boolean;
onChange: (choice: Choice) => void;
loading: boolean;
}) => (
- <div className="form-group row">
+ <div className="mb-3 row">
<label
className="col-md-4 col-form-label"
htmlFor={`block-${filterType}-filter`}
>
- {i18n.t(`block_${filterType}` as NoOptionI18nKeys)}
+ {I18NextService.i18n.t(`block_${filterType}` as NoOptionI18nKeys)}
</label>
<div className="col-md-8">
<SearchableSelect
export class Settings extends Component<any, SettingsState> {
private isoData = setIsoData(this.context);
- private subscription?: Subscription;
state: SettingsState = {
+ saveRes: { state: "empty" },
+ deleteAccountRes: { state: "empty" },
+ changePasswordRes: { state: "empty" },
saveUserSettingsForm: {},
changePasswordForm: {},
- saveUserSettingsLoading: false,
- changePasswordLoading: false,
- deleteAccountLoading: false,
deleteAccountShowConfirm: false,
deleteAccountForm: {},
personBlocks: [],
this.handleBannerUpload = this.handleBannerUpload.bind(this);
this.handleBannerRemove = this.handleBannerRemove.bind(this);
+ this.userSettings = this.userSettings.bind(this);
+ this.blockCards = this.blockCards.bind(this);
- this.parseMessage = this.parseMessage.bind(this);
- this.subscription = wsSubscribe(this.parseMessage);
+ this.handleBlockPerson = this.handleBlockPerson.bind(this);
+ this.handleBlockCommunity = this.handleBlockCommunity.bind(this);
const mui = UserService.Instance.myUserInfo;
if (mui) {
this.setState({ themeList: await fetchThemeList() });
}
- componentWillUnmount() {
- this.subscription?.unsubscribe();
- }
-
get documentTitle(): string {
- return i18n.t("settings");
+ return I18NextService.i18n.t("settings");
}
render() {
return (
- <div className="container-lg">
- <>
- <HtmlTags
- title={this.documentTitle}
- path={this.context.router.route.match.url}
- description={this.documentTitle}
- image={this.state.saveUserSettingsForm.avatar}
- />
- <ul className="nav nav-tabs mb-2">
- <li className="nav-item">
- <button
- className={`nav-link btn ${
- this.state.currentTab == "settings" && "active"
- }`}
- onClick={linkEvent(
- { ctx: this, tab: "settings" },
- this.handleSwitchTab
- )}
- >
- {i18n.t("settings")}
- </button>
- </li>
- <li className="nav-item">
- <button
- className={`nav-link btn ${
- this.state.currentTab == "blocks" && "active"
- }`}
- onClick={linkEvent(
- { ctx: this, tab: "blocks" },
- this.handleSwitchTab
- )}
- >
- {i18n.t("blocks")}
- </button>
- </li>
- </ul>
- {this.state.currentTab == "settings" && this.userSettings()}
- {this.state.currentTab == "blocks" && this.blockCards()}
- </>
+ <div className="person-settings container-lg">
+ <HtmlTags
+ title={this.documentTitle}
+ path={this.context.router.route.match.url}
+ description={this.documentTitle}
+ image={this.state.saveUserSettingsForm.avatar}
+ />
+ <Tabs
+ tabs={[
+ {
+ key: "settings",
+ label: I18NextService.i18n.t("settings"),
+ getNode: this.userSettings,
+ },
+ {
+ key: "blocks",
+ label: I18NextService.i18n.t("blocks"),
+ getNode: this.blockCards,
+ },
+ ]}
+ />
</div>
);
}
- userSettings() {
+ userSettings(isSelected) {
return (
- <div className="row">
- <div className="col-12 col-md-6">
- <div className="card border-secondary mb-3">
- <div className="card-body">{this.saveUserSettingsHtmlForm()}</div>
+ <div
+ className={classNames("tab-pane show", {
+ active: isSelected,
+ })}
+ role="tabpanel"
+ id="settings-tab-pane"
+ >
+ <div className="row">
+ <div className="col-12 col-md-6">
+ <div className="card border-secondary mb-3">
+ <div className="card-body">{this.saveUserSettingsHtmlForm()}</div>
+ </div>
</div>
- </div>
- <div className="col-12 col-md-6">
- <div className="card border-secondary mb-3">
- <div className="card-body">{this.changePasswordHtmlForm()}</div>
+ <div className="col-12 col-md-6">
+ <div className="card border-secondary mb-3">
+ <div className="card-body">{this.changePasswordHtmlForm()}</div>
+ </div>
</div>
</div>
</div>
);
}
- blockCards() {
+ blockCards(isSelected) {
return (
- <div className="row">
- <div className="col-12 col-md-6">
- <div className="card border-secondary mb-3">
- <div className="card-body">{this.blockUserCard()}</div>
+ <div
+ className={classNames("tab-pane", {
+ active: isSelected,
+ })}
+ role="tabpanel"
+ id="blocks-tab-pane"
+ >
+ <div className="row">
+ <div className="col-12 col-md-6">
+ <div className="card border-secondary mb-3">
+ <div className="card-body">{this.blockUserCard()}</div>
+ </div>
</div>
- </div>
- <div className="col-12 col-md-6">
- <div className="card border-secondary mb-3">
- <div className="card-body">{this.blockCommunityCard()}</div>
+ <div className="col-12 col-md-6">
+ <div className="card border-secondary mb-3">
+ <div className="card-body">{this.blockCommunityCard()}</div>
+ </div>
</div>
</div>
</div>
changePasswordHtmlForm() {
return (
<>
- <h5>{i18n.t("change_password")}</h5>
+ <h2 className="h5">{I18NextService.i18n.t("change_password")}</h2>
<form onSubmit={linkEvent(this, this.handleChangePasswordSubmit)}>
- <div className="form-group row">
+ <div className="mb-3 row">
<label className="col-sm-5 col-form-label" htmlFor="user-password">
- {i18n.t("new_password")}
+ {I18NextService.i18n.t("new_password")}
</label>
<div className="col-sm-7">
<input
/>
</div>
</div>
- <div className="form-group row">
+ <div className="mb-3 row">
<label
className="col-sm-5 col-form-label"
htmlFor="user-verify-password"
>
- {i18n.t("verify_password")}
+ {I18NextService.i18n.t("verify_password")}
</label>
<div className="col-sm-7">
<input
/>
</div>
</div>
- <div className="form-group row">
+ <div className="mb-3 row">
<label
className="col-sm-5 col-form-label"
htmlFor="user-old-password"
>
- {i18n.t("old_password")}
+ {I18NextService.i18n.t("old_password")}
</label>
<div className="col-sm-7">
<input
/>
</div>
</div>
- <div className="form-group">
- <button type="submit" className="btn btn-block btn-secondary mr-4">
- {this.state.changePasswordLoading ? (
+ <div className="input-group mb-3">
+ <button
+ type="submit"
+ className="btn d-block btn-secondary me-4 w-100"
+ >
+ {this.state.changePasswordRes.state === "loading" ? (
<Spinner />
) : (
- capitalizeFirstLetter(i18n.t("save"))
+ capitalizeFirstLetter(I18NextService.i18n.t("save"))
)}
</button>
</div>
blockedUsersList() {
return (
<>
- <h5>{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}>
{ ctx: this, recipientId: pb.target.id },
this.handleUnblockPerson
)}
- data-tippy-content={i18n.t("unblock_user")}
+ data-tippy-content={I18NextService.i18n.t("unblock_user")}
>
<Icon icon="x" classes="icon-inline" />
</button>
blockedCommunitiesList() {
return (
<>
- <h5>{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}>
{ ctx: this, communityId: cb.community.id },
this.handleUnblockCommunity
)}
- data-tippy-content={i18n.t("unblock_community")}
+ data-tippy-content={I18NextService.i18n.t(
+ "unblock_community"
+ )}
>
<Icon icon="x" classes="icon-inline" />
</button>
}
saveUserSettingsHtmlForm() {
- let selectedLangs = this.state.saveUserSettingsForm.discussion_languages;
+ const selectedLangs = this.state.saveUserSettingsForm.discussion_languages;
return (
<>
- <h5>{i18n.t("settings")}</h5>
+ <h2 className="h5">{I18NextService.i18n.t("settings")}</h2>
<form onSubmit={linkEvent(this, this.handleSaveSettingsSubmit)}>
- <div className="form-group row">
- <label className="col-sm-5 col-form-label" htmlFor="display-name">
- {i18n.t("display_name")}
+ <div className="mb-3 row">
+ <label className="col-sm-3 col-form-label" htmlFor="display-name">
+ {I18NextService.i18n.t("display_name")}
</label>
- <div className="col-sm-7">
+ <div className="col-sm-9">
<input
id="display-name"
type="text"
className="form-control"
- placeholder={i18n.t("optional")}
+ placeholder={I18NextService.i18n.t("optional")}
value={this.state.saveUserSettingsForm.display_name}
onInput={linkEvent(this, this.handleDisplayNameChange)}
pattern="^(?!@)(.+)$"
/>
</div>
</div>
- <div className="form-group row">
+ <div className="mb-3 row">
<label className="col-sm-3 col-form-label" htmlFor="user-bio">
- {i18n.t("bio")}
+ {I18NextService.i18n.t("bio")}
</label>
<div className="col-sm-9">
<MarkdownTextArea
/>
</div>
</div>
- <div className="form-group row">
+ <div className="mb-3 row">
<label className="col-sm-3 col-form-label" htmlFor="user-email">
- {i18n.t("email")}
+ {I18NextService.i18n.t("email")}
</label>
<div className="col-sm-9">
<input
type="email"
id="user-email"
className="form-control"
- placeholder={i18n.t("optional")}
+ placeholder={I18NextService.i18n.t("optional")}
value={this.state.saveUserSettingsForm.email}
onInput={linkEvent(this, this.handleEmailChange)}
minLength={3}
/>
</div>
</div>
- <div className="form-group row">
- <label className="col-sm-5 col-form-label" htmlFor="matrix-user-id">
+ <div className="mb-3 row">
+ <label className="col-sm-3 col-form-label" htmlFor="matrix-user-id">
<a href={elementUrl} rel={relTags}>
- {i18n.t("matrix_user_id")}
+ {I18NextService.i18n.t("matrix_user_id")}
</a>
</label>
- <div className="col-sm-7">
+ <div className="col-sm-9">
<input
id="matrix-user-id"
type="text"
/>
</div>
</div>
- <div className="form-group row">
- <label className="col-sm-3">{i18n.t("avatar")}</label>
+ <div className="mb-3 row">
+ <label className="col-sm-3 col-form-label">
+ {I18NextService.i18n.t("avatar")}
+ </label>
<div className="col-sm-9">
<ImageUploadForm
- uploadTitle={i18n.t("upload_avatar")}
+ uploadTitle={I18NextService.i18n.t("upload_avatar")}
imageSrc={this.state.saveUserSettingsForm.avatar}
onUpload={this.handleAvatarUpload}
onRemove={this.handleAvatarRemove}
/>
</div>
</div>
- <div className="form-group row">
- <label className="col-sm-3">{i18n.t("banner")}</label>
+ <div className="mb-3 row">
+ <label className="col-sm-3 col-form-label">
+ {I18NextService.i18n.t("banner")}
+ </label>
<div className="col-sm-9">
<ImageUploadForm
- uploadTitle={i18n.t("upload_banner")}
+ uploadTitle={I18NextService.i18n.t("upload_banner")}
imageSrc={this.state.saveUserSettingsForm.banner}
onUpload={this.handleBannerUpload}
onRemove={this.handleBannerRemove}
/>
</div>
</div>
- <div className="form-group row">
- <label className="col-sm-3" htmlFor="user-language">
- {i18n.t("interface_language")}
+ <div className="mb-3 row">
+ <label className="col-sm-3 form-label" htmlFor="user-language">
+ {I18NextService.i18n.t("interface_language")}
</label>
<div className="col-sm-9">
<select
id="user-language"
value={this.state.saveUserSettingsForm.interface_language}
onChange={linkEvent(this, this.handleInterfaceLangChange)}
- className="custom-select w-auto"
+ className="form-select d-inline-block w-auto"
>
<option disabled aria-hidden="true">
- {i18n.t("interface_language")}
+ {I18NextService.i18n.t("interface_language")}
+ </option>
+ <option value="browser">
+ {I18NextService.i18n.t("browser_default")}
</option>
- <option value="browser">{i18n.t("browser_default")}</option>
<option disabled aria-hidden="true">
──
</option>
siteLanguages={this.state.siteRes.discussion_languages}
selectedLanguageIds={selectedLangs}
multiple={true}
+ showLanguageWarning={true}
showSite
onChange={this.handleDiscussionLanguageChange}
/>
- <div className="form-group row">
- <label className="col-sm-3" htmlFor="user-theme">
- {i18n.t("theme")}
+ <div className="mb-3 row">
+ <label className="col-sm-3 col-form-label" htmlFor="user-theme">
+ {I18NextService.i18n.t("theme")}
</label>
<div className="col-sm-9">
<select
id="user-theme"
value={this.state.saveUserSettingsForm.theme}
onChange={linkEvent(this, this.handleThemeChange)}
- className="custom-select w-auto"
+ className="form-select d-inline-block w-auto"
>
<option disabled aria-hidden="true">
- {i18n.t("theme")}
+ {I18NextService.i18n.t("theme")}
+ </option>
+ <option value="browser">
+ {I18NextService.i18n.t("browser_default")}
</option>
- <option value="browser">{i18n.t("browser_default")}</option>
{this.state.themeList.map(theme => (
<option key={theme} value={theme}>
{theme}
</select>
</div>
</div>
- <form className="form-group row">
- <label className="col-sm-3">{i18n.t("type")}</label>
+ <form className="mb-3 row">
+ <label className="col-sm-3 col-form-label">
+ {I18NextService.i18n.t("type")}
+ </label>
<div className="col-sm-9">
<ListingTypeSelect
type_={
/>
</div>
</form>
- <form className="form-group row">
- <label className="col-sm-3">{i18n.t("sort_type")}</label>
+ <form className="mb-3 row">
+ <label className="col-sm-3 col-form-label">
+ {I18NextService.i18n.t("sort_type")}
+ </label>
<div className="col-sm-9">
<SortSelect
sort={
/>
</div>
</form>
- {enableNsfw(this.state.siteRes) && (
- <div className="form-group">
- <div className="form-check">
- <input
- className="form-check-input"
- id="user-show-nsfw"
- type="checkbox"
- checked={this.state.saveUserSettingsForm.show_nsfw}
- onChange={linkEvent(this, this.handleShowNsfwChange)}
- />
- <label className="form-check-label" htmlFor="user-show-nsfw">
- {i18n.t("show_nsfw")}
- </label>
- </div>
+ <div className="input-group mb-3">
+ <div className="form-check">
+ <input
+ className="form-check-input"
+ id="user-show-nsfw"
+ type="checkbox"
+ checked={this.state.saveUserSettingsForm.show_nsfw}
+ onChange={linkEvent(this, this.handleShowNsfwChange)}
+ />
+ <label className="form-check-label" htmlFor="user-show-nsfw">
+ {I18NextService.i18n.t("show_nsfw")}
+ </label>
</div>
- )}
- <div className="form-group">
+ </div>
+ <div className="input-group mb-3">
<div className="form-check">
<input
className="form-check-input"
onChange={linkEvent(this, this.handleShowScoresChange)}
/>
<label className="form-check-label" htmlFor="user-show-scores">
- {i18n.t("show_scores")}
+ {I18NextService.i18n.t("show_scores")}
</label>
</div>
</div>
- <div className="form-group">
+ <div className="input-group mb-3">
<div className="form-check">
<input
className="form-check-input"
onChange={linkEvent(this, this.handleShowAvatarsChange)}
/>
<label className="form-check-label" htmlFor="user-show-avatars">
- {i18n.t("show_avatars")}
+ {I18NextService.i18n.t("show_avatars")}
</label>
</div>
</div>
- <div className="form-group">
+ <div className="input-group mb-3">
<div className="form-check">
<input
className="form-check-input"
onChange={linkEvent(this, this.handleBotAccount)}
/>
<label className="form-check-label" htmlFor="user-bot-account">
- {i18n.t("bot_account")}
+ {I18NextService.i18n.t("bot_account")}
</label>
</div>
</div>
- <div className="form-group">
+ <div className="input-group mb-3">
<div className="form-check">
<input
className="form-check-input"
className="form-check-label"
htmlFor="user-show-bot-accounts"
>
- {i18n.t("show_bot_accounts")}
+ {I18NextService.i18n.t("show_bot_accounts")}
</label>
</div>
</div>
- <div className="form-group">
+ <div className="input-group mb-3">
<div className="form-check">
<input
className="form-check-input"
className="form-check-label"
htmlFor="user-show-read-posts"
>
- {i18n.t("show_read_posts")}
+ {I18NextService.i18n.t("show_read_posts")}
</label>
</div>
</div>
- <div className="form-group">
+ <div className="input-group mb-3">
<div className="form-check">
<input
className="form-check-input"
className="form-check-label"
htmlFor="user-show-new-post-notifs"
>
- {i18n.t("show_new_post_notifs")}
+ {I18NextService.i18n.t("show_new_post_notifs")}
</label>
</div>
</div>
- <div className="form-group">
+ <div className="input-group mb-3">
<div className="form-check">
<input
className="form-check-input"
className="form-check-label"
htmlFor="user-send-notifications-to-email"
>
- {i18n.t("send_notifications_to_email")}
+ {I18NextService.i18n.t("send_notifications_to_email")}
</label>
</div>
</div>
{this.totpSection()}
- <div className="form-group">
- <button type="submit" className="btn btn-block btn-secondary mr-4">
- {this.state.saveUserSettingsLoading ? (
+ <div className="input-group mb-3">
+ <button type="submit" className="btn d-block btn-secondary me-4">
+ {this.state.saveRes.state === "loading" ? (
<Spinner />
) : (
- capitalizeFirstLetter(i18n.t("save"))
+ capitalizeFirstLetter(I18NextService.i18n.t("save"))
)}
</button>
</div>
<hr />
- <div className="form-group">
+ <div className="input-group mb-3">
<button
- className="btn btn-block btn-danger"
+ className="btn d-block btn-danger"
onClick={linkEvent(
this,
this.handleDeleteAccountShowConfirmToggle
)}
>
- {i18n.t("delete_account")}
+ {I18NextService.i18n.t("delete_account")}
</button>
{this.state.deleteAccountShowConfirm && (
<>
<div className="my-2 alert alert-danger" role="alert">
- {i18n.t("delete_account_confirm")}
+ {I18NextService.i18n.t("delete_account_confirm")}
</div>
<input
type="password"
className="form-control my-2"
/>
<button
- className="btn btn-danger mr-4"
+ className="btn btn-danger me-4"
disabled={!this.state.deleteAccountForm.password}
onClick={linkEvent(this, this.handleDeleteAccount)}
>
- {this.state.deleteAccountLoading ? (
+ {this.state.deleteAccountRes.state === "loading" ? (
<Spinner />
) : (
- capitalizeFirstLetter(i18n.t("delete"))
+ capitalizeFirstLetter(I18NextService.i18n.t("delete"))
)}
</button>
<button
this.handleDeleteAccountShowConfirmToggle
)}
>
- {i18n.t("cancel")}
+ {I18NextService.i18n.t("cancel")}
</button>
</>
)}
}
totpSection() {
- let totpUrl =
+ const totpUrl =
UserService.Instance.myUserInfo?.local_user_view.local_user.totp_2fa_url;
return (
<>
{!totpUrl && (
- <div className="form-group">
+ <div className="input-group mb-3">
<div className="form-check">
<input
className="form-check-input"
onChange={linkEvent(this, this.handleGenerateTotp)}
/>
<label className="form-check-label" htmlFor="user-generate-totp">
- {i18n.t("set_up_two_factor")}
+ {I18NextService.i18n.t("set_up_two_factor")}
</label>
</div>
</div>
<>
<div>
<a className="btn btn-secondary mb-2" href={totpUrl}>
- {i18n.t("two_factor_link")}
+ {I18NextService.i18n.t("two_factor_link")}
</a>
</div>
- <div className="form-group">
+ <div className="input-group mb-3">
<div className="form-check">
<input
className="form-check-input"
onChange={linkEvent(this, this.handleRemoveTotp)}
/>
<label className="form-check-label" htmlFor="user-remove-totp">
- {i18n.t("remove_two_factor")}
+ {I18NextService.i18n.t("remove_two_factor")}
</label>
</div>
</div>
const searchPersonOptions: Choice[] = [];
if (text.length > 0) {
- searchPersonOptions.push(
- ...(await fetchUsers(text)).users.map(personToChoice)
- );
+ searchPersonOptions.push(...(await fetchUsers(text)).map(personToChoice));
}
this.setState({
if (text.length > 0) {
searchCommunityOptions.push(
- ...(await fetchCommunities(text)).communities.map(communityToChoice)
+ ...(await fetchCommunities(text)).map(communityToChoice)
);
}
});
});
- handleBlockPerson({ value }: Choice) {
- const auth = myAuth();
- if (auth && value !== "0") {
- const blockUserForm: BlockPerson = {
+ async handleBlockPerson({ value }: Choice) {
+ if (value !== "0") {
+ const res = await HttpService.client.blockPerson({
person_id: Number(value),
block: true,
- auth,
- };
-
- WebSocketService.Instance.send(wsClient.blockPerson(blockUserForm));
+ auth: myAuthRequired(),
+ });
+ this.personBlock(res);
}
}
- handleUnblockPerson(i: { ctx: Settings; recipientId: number }) {
- const auth = myAuth();
- if (auth) {
- const blockUserForm: BlockPerson = {
- person_id: i.recipientId,
- block: false,
- auth,
- };
- WebSocketService.Instance.send(wsClient.blockPerson(blockUserForm));
- }
+ async handleUnblockPerson({
+ ctx,
+ recipientId,
+ }: {
+ ctx: Settings;
+ recipientId: number;
+ }) {
+ const res = await HttpService.client.blockPerson({
+ person_id: recipientId,
+ block: false,
+ auth: myAuthRequired(),
+ });
+ ctx.personBlock(res);
}
- handleBlockCommunity({ value }: Choice) {
- const auth = myAuth();
- if (auth && value !== "0") {
- const blockCommunityForm: BlockCommunity = {
+ async handleBlockCommunity({ value }: Choice) {
+ if (value !== "0") {
+ const res = await HttpService.client.blockCommunity({
community_id: Number(value),
block: true,
- auth,
- };
- WebSocketService.Instance.send(
- wsClient.blockCommunity(blockCommunityForm)
- );
+ auth: myAuthRequired(),
+ });
+ this.communityBlock(res);
}
}
- handleUnblockCommunity(i: { ctx: Settings; communityId: number }) {
+ async handleUnblockCommunity(i: { ctx: Settings; communityId: number }) {
const auth = myAuth();
if (auth) {
- const blockCommunityForm: BlockCommunity = {
+ const res = await HttpService.client.blockCommunity({
community_id: i.communityId,
block: false,
- auth,
- };
- WebSocketService.Instance.send(
- wsClient.blockCommunity(blockCommunityForm)
- );
+ auth: myAuthRequired(),
+ });
+ i.ctx.communityBlock(res);
}
}
handleShowNsfwChange(i: Settings, event: any) {
- i.state.saveUserSettingsForm.show_nsfw = event.target.checked;
- i.setState(i.state);
+ i.setState(
+ s => ((s.saveUserSettingsForm.show_nsfw = event.target.checked), s)
+ );
}
handleShowAvatarsChange(i: Settings, event: any) {
- i.state.saveUserSettingsForm.show_avatars = event.target.checked;
- let mui = UserService.Instance.myUserInfo;
+ const mui = UserService.Instance.myUserInfo;
if (mui) {
mui.local_user_view.local_user.show_avatars = event.target.checked;
}
- i.setState(i.state);
+ i.setState(
+ s => ((s.saveUserSettingsForm.show_avatars = event.target.checked), s)
+ );
}
handleBotAccount(i: Settings, event: any) {
- i.state.saveUserSettingsForm.bot_account = event.target.checked;
- i.setState(i.state);
+ i.setState(
+ s => ((s.saveUserSettingsForm.bot_account = event.target.checked), s)
+ );
}
handleShowBotAccounts(i: Settings, event: any) {
- i.state.saveUserSettingsForm.show_bot_accounts = event.target.checked;
- i.setState(i.state);
+ i.setState(
+ s => (
+ (s.saveUserSettingsForm.show_bot_accounts = event.target.checked), s
+ )
+ );
}
handleReadPosts(i: Settings, event: any) {
- i.state.saveUserSettingsForm.show_read_posts = event.target.checked;
- i.setState(i.state);
+ i.setState(
+ s => ((s.saveUserSettingsForm.show_read_posts = event.target.checked), s)
+ );
}
handleShowNewPostNotifs(i: Settings, event: any) {
- i.state.saveUserSettingsForm.show_new_post_notifs = event.target.checked;
- i.setState(i.state);
+ i.setState(
+ s => (
+ (s.saveUserSettingsForm.show_new_post_notifs = event.target.checked), s
+ )
+ );
}
handleShowScoresChange(i: Settings, event: any) {
- i.state.saveUserSettingsForm.show_scores = event.target.checked;
- let mui = UserService.Instance.myUserInfo;
+ const mui = UserService.Instance.myUserInfo;
if (mui) {
mui.local_user_view.local_user.show_scores = event.target.checked;
}
- i.setState(i.state);
+ i.setState(
+ s => ((s.saveUserSettingsForm.show_scores = event.target.checked), s)
+ );
}
handleGenerateTotp(i: Settings, event: any) {
// Coerce false to undefined here, so it won't generate it.
- let checked: boolean | undefined = event.target.checked || undefined;
+ const checked: boolean | undefined = event.target.checked || undefined;
if (checked) {
- toast(i18n.t("two_factor_setup_instructions"));
+ toast(I18NextService.i18n.t("two_factor_setup_instructions"));
}
- i.state.saveUserSettingsForm.generate_totp_2fa = checked;
- i.setState(i.state);
+ i.setState(s => ((s.saveUserSettingsForm.generate_totp_2fa = checked), s));
}
handleRemoveTotp(i: Settings, event: any) {
// Coerce true to undefined here, so it won't generate it.
- let checked: boolean | undefined = !event.target.checked && undefined;
- i.state.saveUserSettingsForm.generate_totp_2fa = checked;
- i.setState(i.state);
+ const checked: boolean | undefined = !event.target.checked && undefined;
+ i.setState(s => ((s.saveUserSettingsForm.generate_totp_2fa = checked), s));
}
handleSendNotificationsToEmailChange(i: Settings, event: any) {
- i.state.saveUserSettingsForm.send_notifications_to_email =
- event.target.checked;
- i.setState(i.state);
+ i.setState(
+ s => (
+ (s.saveUserSettingsForm.send_notifications_to_email =
+ event.target.checked),
+ s
+ )
+ );
}
handleThemeChange(i: Settings, event: any) {
- i.state.saveUserSettingsForm.theme = event.target.value;
+ i.setState(s => ((s.saveUserSettingsForm.theme = event.target.value), s));
setTheme(event.target.value, true);
- i.setState(i.state);
}
handleInterfaceLangChange(i: Settings, event: any) {
- i.state.saveUserSettingsForm.interface_language = event.target.value;
- i18n.changeLanguage(
- getLanguages(i.state.saveUserSettingsForm.interface_language).at(0)
+ const newLang = event.target.value ?? "browser";
+ I18NextService.i18n.changeLanguage(
+ newLang === "browser" ? navigator.languages : newLang
+ );
+
+ i.setState(
+ s => ((s.saveUserSettingsForm.interface_language = event.target.value), s)
);
- i.setState(i.state);
}
handleDiscussionLanguageChange(val: number[]) {
}
handleEmailChange(i: Settings, event: any) {
- i.state.saveUserSettingsForm.email = event.target.value;
- i.setState(i.state);
+ i.setState(s => ((s.saveUserSettingsForm.email = event.target.value), s));
}
handleBioChange(val: string) {
}
handleDisplayNameChange(i: Settings, event: any) {
- i.state.saveUserSettingsForm.display_name = event.target.value;
- i.setState(i.state);
+ i.setState(
+ s => ((s.saveUserSettingsForm.display_name = event.target.value), s)
+ );
}
handleMatrixUserIdChange(i: Settings, event: any) {
- i.state.saveUserSettingsForm.matrix_user_id = event.target.value;
- i.setState(i.state);
+ i.setState(
+ s => ((s.saveUserSettingsForm.matrix_user_id = event.target.value), s)
+ );
}
handleNewPasswordChange(i: Settings, event: any) {
- i.state.changePasswordForm.new_password = event.target.value;
- if (i.state.changePasswordForm.new_password == "") {
- i.state.changePasswordForm.new_password = undefined;
- }
- i.setState(i.state);
+ const newPass: string | undefined =
+ event.target.value == "" ? undefined : event.target.value;
+ i.setState(s => ((s.changePasswordForm.new_password = newPass), s));
}
handleNewPasswordVerifyChange(i: Settings, event: any) {
- i.state.changePasswordForm.new_password_verify = event.target.value;
- if (i.state.changePasswordForm.new_password_verify == "") {
- i.state.changePasswordForm.new_password_verify = undefined;
- }
- i.setState(i.state);
+ const newPassVerify: string | undefined =
+ event.target.value == "" ? undefined : event.target.value;
+ i.setState(
+ s => ((s.changePasswordForm.new_password_verify = newPassVerify), s)
+ );
}
handleOldPasswordChange(i: Settings, event: any) {
- i.state.changePasswordForm.old_password = event.target.value;
- if (i.state.changePasswordForm.old_password == "") {
- i.state.changePasswordForm.old_password = undefined;
- }
- i.setState(i.state);
+ const oldPass: string | undefined =
+ event.target.value == "" ? undefined : event.target.value;
+ i.setState(s => ((s.changePasswordForm.old_password = oldPass), s));
}
- handleSaveSettingsSubmit(i: Settings, event: any) {
+ async handleSaveSettingsSubmit(i: Settings, event: any) {
event.preventDefault();
- i.setState({ saveUserSettingsLoading: true });
- let auth = myAuth();
- if (auth) {
- let form: SaveUserSettings = { ...i.state.saveUserSettingsForm, auth };
- WebSocketService.Instance.send(wsClient.saveUserSettings(form));
+ i.setState({ saveRes: { state: "loading" } });
+
+ const saveRes = await HttpService.client.saveUserSettings({
+ ...i.state.saveUserSettingsForm,
+ auth: myAuthRequired(),
+ });
+ if (saveRes.state === "success") {
+ UserService.Instance.login(saveRes.data);
+ toast(I18NextService.i18n.t("saved"));
+ window.scrollTo(0, 0);
}
+
+ i.setState({ saveRes });
}
- handleChangePasswordSubmit(i: Settings, event: any) {
+ async handleChangePasswordSubmit(i: Settings, event: any) {
event.preventDefault();
- i.setState({ changePasswordLoading: true });
- let auth = myAuth();
- let pForm = i.state.changePasswordForm;
- let new_password = pForm.new_password;
- let new_password_verify = pForm.new_password_verify;
- let old_password = pForm.old_password;
- if (auth && new_password && old_password && new_password_verify) {
- let form: ChangePassword = {
+ const { new_password, new_password_verify, old_password } =
+ i.state.changePasswordForm;
+
+ if (new_password && old_password && new_password_verify) {
+ i.setState({ changePasswordRes: { state: "loading" } });
+ const changePasswordRes = await HttpService.client.changePassword({
new_password,
new_password_verify,
old_password,
- auth,
- };
+ auth: myAuthRequired(),
+ });
+ if (changePasswordRes.state === "success") {
+ UserService.Instance.login(changePasswordRes.data);
+ window.scrollTo(0, 0);
+ toast(I18NextService.i18n.t("password_changed"));
+ }
- WebSocketService.Instance.send(wsClient.changePassword(form));
+ i.setState({ changePasswordRes });
}
}
- handleDeleteAccountShowConfirmToggle(i: Settings, event: any) {
- event.preventDefault();
+ handleDeleteAccountShowConfirmToggle(i: Settings) {
i.setState({ deleteAccountShowConfirm: !i.state.deleteAccountShowConfirm });
}
handleDeleteAccountPasswordChange(i: Settings, event: any) {
- i.state.deleteAccountForm.password = event.target.value;
- i.setState(i.state);
+ i.setState(s => ((s.deleteAccountForm.password = event.target.value), s));
}
- handleDeleteAccount(i: Settings, event: any) {
- event.preventDefault();
- i.setState({ deleteAccountLoading: true });
- let auth = myAuth();
- let password = i.state.deleteAccountForm.password;
- if (auth && password) {
- let form: DeleteAccount = {
+ async handleDeleteAccount(i: Settings) {
+ const password = i.state.deleteAccountForm.password;
+ if (password) {
+ i.setState({ deleteAccountRes: { state: "loading" } });
+ const deleteAccountRes = await HttpService.client.deleteAccount({
password,
- auth,
- };
- WebSocketService.Instance.send(wsClient.deleteAccount(form));
+ auth: myAuthRequired(),
+ });
+ if (deleteAccountRes.state === "success") {
+ UserService.Instance.logout();
+ this.context.router.history.replace("/");
+ }
+
+ i.setState({ deleteAccountRes });
}
}
i.ctx.setState({ currentTab: i.tab });
}
- parseMessage(msg: any) {
- let op = wsUserOp(msg);
- console.log(msg);
- if (msg.error) {
- this.setState({
- saveUserSettingsLoading: false,
- changePasswordLoading: false,
- deleteAccountLoading: false,
- });
- toast(i18n.t(msg.error), "danger");
- return;
- } else if (op == UserOperation.SaveUserSettings) {
- this.setState({ saveUserSettingsLoading: false });
- toast(i18n.t("saved"));
- window.scrollTo(0, 0);
- } else if (op == UserOperation.ChangePassword) {
- let data = wsJsonToRes<LoginResponse>(msg);
- UserService.Instance.login(data);
- this.setState({ changePasswordLoading: false });
- window.scrollTo(0, 0);
- toast(i18n.t("password_changed"));
- } else if (op == UserOperation.DeleteAccount) {
- this.setState({
- deleteAccountLoading: false,
- deleteAccountShowConfirm: false,
- });
- UserService.Instance.logout();
- window.location.href = "/";
- } else if (op == UserOperation.BlockPerson) {
- let data = wsJsonToRes<BlockPersonResponse>(msg);
- updatePersonBlock(data);
- let mui = UserService.Instance.myUserInfo;
+ personBlock(res: RequestState<BlockPersonResponse>) {
+ if (res.state === "success") {
+ updatePersonBlock(res.data);
+ const mui = UserService.Instance.myUserInfo;
if (mui) {
this.setState({ personBlocks: mui.person_blocks });
}
- } else if (op == UserOperation.BlockCommunity) {
- let data = wsJsonToRes<BlockCommunityResponse>(msg);
- updateCommunityBlock(data);
- let mui = UserService.Instance.myUserInfo;
+ }
+ }
+
+ communityBlock(res: RequestState<BlockCommunityResponse>) {
+ if (res.state === "success") {
+ updateCommunityBlock(res.data);
+ const mui = UserService.Instance.myUserInfo;
if (mui) {
this.setState({ communityBlocks: mui.community_blocks });
}