Adding change password and email address from user settings.

- Fixes #384
- Fixes #385
This commit is contained in:
Dessalines 2020-01-01 15:46:14 -05:00
parent a95704d5fc
commit b63aabfdc2
9 changed files with 339 additions and 210 deletions

19
README.md vendored
View file

@ -257,16 +257,15 @@ If you'd like to add translations, take a look a look at the [English translatio
lang | done | missing lang | done | missing
--- | --- | --- --- | --- | ---
de | 97% | avatar,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw de | 97% | avatar,old_password,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw
eo | 84% | number_of_communities,preview,upload_image,avatar,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,theme,are_you_sure,yes,no eo | 83% | number_of_communities,preview,upload_image,avatar,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,replies,mentions,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,theme,are_you_sure,yes,no
es | 92% | avatar,archive_link,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw es | 92% | avatar,archive_link,replies,mentions,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw
fr | 92% | avatar,archive_link,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw fr | 92% | avatar,archive_link,replies,mentions,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw
it | 93% | avatar,archive_link,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw it | 93% | avatar,archive_link,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw
nl | 86% | preview,upload_image,avatar,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,theme nl | 85% | preview,upload_image,avatar,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,replies,mentions,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,theme
ru | 80% | cross_posts,cross_post,number_of_communities,preview,upload_image,avatar,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,recent_comments,theme,monero,by,to,transfer_community,transfer_site,are_you_sure,yes,no ru | 79% | cross_posts,cross_post,number_of_communities,preview,upload_image,avatar,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,replies,mentions,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,recent_comments,theme,monero,by,to,transfer_community,transfer_site,are_you_sure,yes,no
sv | 92% | avatar,archive_link,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw sv | 92% | avatar,archive_link,replies,mentions,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw
zh | 78% | cross_posts,cross_post,users,number_of_communities,preview,upload_image,avatar,formatting_help,view_source,sticky,unsticky,archive_link,settings,stickied,delete_account,delete_account_confirm,banned,creator,number_online,replies,mentions,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,recent_comments,nsfw,show_nsfw,theme,monero,by,to,transfer_community,transfer_site,are_you_sure,yes,no zh | 77% | cross_posts,cross_post,users,number_of_communities,preview,upload_image,avatar,formatting_help,view_source,sticky,unsticky,archive_link,settings,stickied,delete_account,delete_account_confirm,banned,creator,number_online,replies,mentions,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,recent_comments,nsfw,show_nsfw,theme,monero,by,to,transfer_community,transfer_site,are_you_sure,yes,no
If you'd like to update this report, run: If you'd like to update this report, run:

View file

@ -0,0 +1,15 @@
-- user
drop view user_view;
create view user_view as
select id,
name,
avatar,
fedi_name,
admin,
banned,
published,
(select count(*) from post p where p.creator_id = u.id) as number_of_posts,
(select coalesce(sum(score), 0) from post p, post_like pl where u.id = p.creator_id and p.id = pl.post_id) as post_score,
(select count(*) from comment c where c.creator_id = u.id) as number_of_comments,
(select coalesce(sum(score), 0) from comment c, comment_like cl where u.id = c.creator_id and c.id = cl.comment_id) as comment_score
from user_ u;

View file

@ -0,0 +1,16 @@
-- user
drop view user_view;
create view user_view as
select id,
name,
avatar,
email,
fedi_name,
admin,
banned,
published,
(select count(*) from post p where p.creator_id = u.id) as number_of_posts,
(select coalesce(sum(score), 0) from post p, post_like pl where u.id = p.creator_id and p.id = pl.post_id) as post_score,
(select count(*) from comment c where c.creator_id = u.id) as number_of_comments,
(select coalesce(sum(score), 0) from comment c, comment_like cl where u.id = c.creator_id and c.id = cl.comment_id) as comment_score
from user_ u;

View file

@ -28,6 +28,10 @@ pub struct SaveUserSettings {
default_listing_type: i16, default_listing_type: i16,
lang: String, lang: String,
avatar: Option<String>, avatar: Option<String>,
email: Option<String>,
new_password: Option<String>,
new_password_verify: Option<String>,
old_password: Option<String>,
auth: String, auth: String,
} }
@ -312,12 +316,45 @@ impl Perform<LoginResponse> for Oper<SaveUserSettings> {
let read_user = User_::read(&conn, user_id)?; let read_user = User_::read(&conn, user_id)?;
let email = match &data.email {
Some(email) => Some(email.to_owned()),
None => read_user.email,
};
let password_encrypted = match &data.new_password {
Some(new_password) => {
match &data.new_password_verify {
Some(new_password_verify) => {
// Make sure passwords match
if new_password != new_password_verify {
return Err(APIError::err(&self.op, "passwords_dont_match"))?;
}
// Check the old password
match &data.old_password {
Some(old_password) => {
let valid: bool =
verify(old_password, &read_user.password_encrypted).unwrap_or(false);
if !valid {
return Err(APIError::err(&self.op, "password_incorrect"))?;
}
User_::update_password(&conn, user_id, &new_password)?.password_encrypted
}
None => return Err(APIError::err(&self.op, "password_incorrect"))?,
}
}
None => return Err(APIError::err(&self.op, "passwords_dont_match"))?,
}
}
None => read_user.password_encrypted,
};
let user_form = UserForm { let user_form = UserForm {
name: read_user.name, name: read_user.name,
fedi_name: read_user.fedi_name, fedi_name: read_user.fedi_name,
email: read_user.email, email,
avatar: data.avatar.to_owned(), avatar: data.avatar.to_owned(),
password_encrypted: read_user.password_encrypted, password_encrypted,
preferred_username: read_user.preferred_username, preferred_username: read_user.preferred_username,
updated: Some(naive_now()), updated: Some(naive_now()),
admin: read_user.admin, admin: read_user.admin,
@ -850,28 +887,8 @@ impl Perform<LoginResponse> for Oper<PasswordChange> {
return Err(APIError::err(&self.op, "passwords_dont_match"))?; return Err(APIError::err(&self.op, "passwords_dont_match"))?;
} }
// Fetch the user
let read_user = User_::read(&conn, user_id)?;
// Update the user with the new password // Update the user with the new password
let user_form = UserForm { let updated_user = match User_::update_password(&conn, user_id, &data.password) {
name: read_user.name,
fedi_name: read_user.fedi_name,
email: read_user.email,
avatar: read_user.avatar,
password_encrypted: data.password.to_owned(),
preferred_username: read_user.preferred_username,
updated: Some(naive_now()),
admin: read_user.admin,
banned: read_user.banned,
show_nsfw: read_user.show_nsfw,
theme: read_user.theme,
default_sort_type: read_user.default_sort_type,
default_listing_type: read_user.default_listing_type,
lang: read_user.lang,
};
let updated_user = match User_::update_password(&conn, user_id, &user_form) {
Ok(user) => user, Ok(user) => user,
Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_user"))?, Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_user"))?,
}; };

View file

@ -75,14 +75,13 @@ impl User_ {
pub fn update_password( pub fn update_password(
conn: &PgConnection, conn: &PgConnection,
user_id: i32, user_id: i32,
form: &UserForm, new_password: &str,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let mut edited_user = form.clone(); let password_hash = hash(new_password, DEFAULT_COST).expect("Couldn't hash password");
let password_hash =
hash(&form.password_encrypted, DEFAULT_COST).expect("Couldn't hash password");
edited_user.password_encrypted = password_hash;
Self::update(&conn, user_id, &edited_user) diesel::update(user_.find(user_id))
.set(password_encrypted.eq(password_hash))
.get_result::<Self>(conn)
} }
pub fn read_from_name(conn: &PgConnection, from_user_name: String) -> Result<Self, Error> { pub fn read_from_name(conn: &PgConnection, from_user_name: String) -> Result<Self, Error> {

View file

@ -7,6 +7,7 @@ table! {
id -> Int4, id -> Int4,
name -> Varchar, name -> Varchar,
avatar -> Nullable<Text>, avatar -> Nullable<Text>,
email -> Nullable<Text>,
fedi_name -> Varchar, fedi_name -> Varchar,
admin -> Bool, admin -> Bool,
banned -> Bool, banned -> Bool,
@ -26,6 +27,7 @@ pub struct UserView {
pub id: i32, pub id: i32,
pub name: String, pub name: String,
pub avatar: Option<String>, pub avatar: Option<String>,
pub email: Option<String>,
pub fedi_name: String, pub fedi_name: String,
pub admin: bool, pub admin: bool,
pub banned: bool, pub banned: bool,

View file

@ -99,7 +99,6 @@ export class User extends Component<any, UserState> {
default_sort_type: null, default_sort_type: null,
default_listing_type: null, default_listing_type: null,
lang: null, lang: null,
avatar: null,
auth: null, auth: null,
}, },
userSettingsLoading: null, userSettingsLoading: null,
@ -437,199 +436,240 @@ export class User extends Component<any, UserState> {
</h5> </h5>
<form onSubmit={linkEvent(this, this.handleUserSettingsSubmit)}> <form onSubmit={linkEvent(this, this.handleUserSettingsSubmit)}>
<div class="form-group"> <div class="form-group">
<div class="col-12"> <label>
<label> <T i18nKey="avatar">#</T>
<T i18nKey="avatar">#</T> </label>
</label> <form class="d-inline">
<form class="d-inline"> <label
<label htmlFor="file-upload"
htmlFor="file-upload" class="pointer ml-4 text-muted small font-weight-bold"
class="pointer ml-4 text-muted small font-weight-bold" >
> <img
<img height="80"
height="80" width="80"
width="80" src={
src={ this.state.userSettingsForm.avatar
this.state.userSettingsForm.avatar ? this.state.userSettingsForm.avatar
? this.state.userSettingsForm.avatar : 'https://via.placeholder.com/300/000?text=Avatar'
: 'https://via.placeholder.com/300/000?text=Avatar' }
} class="rounded-circle"
class="rounded-circle"
/>
</label>
<input
id="file-upload"
type="file"
accept="image/*,video/*"
name="file"
class="d-none"
disabled={!UserService.Instance.user}
onChange={linkEvent(this, this.handleImageUpload)}
/> />
</form> </label>
</div> <input
id="file-upload"
type="file"
accept="image/*,video/*"
name="file"
class="d-none"
disabled={!UserService.Instance.user}
onChange={linkEvent(this, this.handleImageUpload)}
/>
</form>
</div> </div>
<div class="form-group"> <div class="form-group">
<div class="col-12"> <label>
<label> <T i18nKey="language">#</T>
</label>
<select
value={this.state.userSettingsForm.lang}
onChange={linkEvent(this, this.handleUserSettingsLangChange)}
class="ml-2 custom-select custom-select-sm w-auto"
>
<option disabled>
<T i18nKey="language">#</T> <T i18nKey="language">#</T>
</label> </option>
<select <option value="browser">
value={this.state.userSettingsForm.lang} <T i18nKey="browser_default">#</T>
onChange={linkEvent( </option>
this, <option disabled></option>
this.handleUserSettingsLangChange {languages.map(lang => (
)} <option value={lang.code}>{lang.name}</option>
class="ml-2 custom-select custom-select-sm w-auto" ))}
> </select>
<option disabled>
<T i18nKey="language">#</T>
</option>
<option value="browser">
<T i18nKey="browser_default">#</T>
</option>
<option disabled></option>
{languages.map(lang => (
<option value={lang.code}>{lang.name}</option>
))}
</select>
</div>
</div> </div>
<div class="form-group"> <div class="form-group">
<div class="col-12"> <label>
<label> <T i18nKey="theme">#</T>
</label>
<select
value={this.state.userSettingsForm.theme}
onChange={linkEvent(this, this.handleUserSettingsThemeChange)}
class="ml-2 custom-select custom-select-sm w-auto"
>
<option disabled>
<T i18nKey="theme">#</T> <T i18nKey="theme">#</T>
</label> </option>
<select {themes.map(theme => (
value={this.state.userSettingsForm.theme} <option value={theme}>{theme}</option>
onChange={linkEvent( ))}
this, </select>
this.handleUserSettingsThemeChange
)}
class="ml-2 custom-select custom-select-sm w-auto"
>
<option disabled>
<T i18nKey="theme">#</T>
</option>
{themes.map(theme => (
<option value={theme}>{theme}</option>
))}
</select>
</div>
</div> </div>
<form className="form-group"> <form className="form-group">
<div class="col-12"> <label>
<label> <T i18nKey="sort_type" class="mr-2">
<T i18nKey="sort_type" class="mr-2"> #
# </T>
</T> </label>
</label> <ListingTypeSelect
<ListingTypeSelect type_={this.state.userSettingsForm.default_listing_type}
type_={this.state.userSettingsForm.default_listing_type} onChange={this.handleUserSettingsListingTypeChange}
onChange={this.handleUserSettingsListingTypeChange} />
/>
</div>
</form> </form>
<form className="form-group"> <form className="form-group">
<div class="col-12"> <label>
<label> <T i18nKey="type" class="mr-2">
<T i18nKey="type" class="mr-2"> #
# </T>
</T> </label>
</label> <SortSelect
<SortSelect sort={this.state.userSettingsForm.default_sort_type}
sort={this.state.userSettingsForm.default_sort_type} onChange={this.handleUserSettingsSortTypeChange}
onChange={this.handleUserSettingsSortTypeChange} />
</form>
<div class="form-group row">
<label class="col-lg-3 col-form-label">
<T i18nKey="email">#</T>
</label>
<div class="col-lg-9">
<input
type="email"
class="form-control"
placeholder={i18n.t('optional')}
value={this.state.userSettingsForm.email}
onInput={linkEvent(
this,
this.handleUserSettingsEmailChange
)}
minLength={3}
/> />
</div> </div>
</form> </div>
<div class="form-group row">
<label class="col-lg-5 col-form-label">
<T i18nKey="new_password">#</T>
</label>
<div class="col-lg-7">
<input
type="password"
class="form-control"
value={this.state.userSettingsForm.new_password}
onInput={linkEvent(
this,
this.handleUserSettingsNewPasswordChange
)}
/>
</div>
</div>
<div class="form-group row">
<label class="col-lg-5 col-form-label">
<T i18nKey="verify_password">#</T>
</label>
<div class="col-lg-7">
<input
type="password"
class="form-control"
value={this.state.userSettingsForm.new_password_verify}
onInput={linkEvent(
this,
this.handleUserSettingsNewPasswordVerifyChange
)}
/>
</div>
</div>
<div class="form-group row">
<label class="col-lg-5 col-form-label">
<T i18nKey="old_password">#</T>
</label>
<div class="col-lg-7">
<input
type="password"
class="form-control"
value={this.state.userSettingsForm.old_password}
onInput={linkEvent(
this,
this.handleUserSettingsOldPasswordChange
)}
/>
</div>
</div>
{WebSocketService.Instance.site.enable_nsfw && ( {WebSocketService.Instance.site.enable_nsfw && (
<div class="form-group"> <div class="form-group">
<div class="col-12"> <div class="form-check">
<div class="form-check"> <input
<input class="form-check-input"
class="form-check-input" type="checkbox"
type="checkbox" checked={this.state.userSettingsForm.show_nsfw}
checked={this.state.userSettingsForm.show_nsfw} onChange={linkEvent(
onChange={linkEvent( this,
this, this.handleUserSettingsShowNsfwChange
this.handleUserSettingsShowNsfwChange )}
)} />
/> <label class="form-check-label">
<label class="form-check-label"> <T i18nKey="show_nsfw">#</T>
<T i18nKey="show_nsfw">#</T> </label>
</label>
</div>
</div> </div>
</div> </div>
)} )}
<div class="form-group"> <div class="form-group">
<div class="col-12"> <button type="submit" class="btn btn-block btn-secondary mr-4">
<button {this.state.userSettingsLoading ? (
type="submit" <svg class="icon icon-spinner spin">
class="btn btn-block btn-secondary mr-4" <use xlinkHref="#icon-spinner"></use>
> </svg>
{this.state.userSettingsLoading ? ( ) : (
<svg class="icon icon-spinner spin"> capitalizeFirstLetter(i18n.t('save'))
<use xlinkHref="#icon-spinner"></use> )}
</svg> </button>
) : (
capitalizeFirstLetter(i18n.t('save'))
)}
</button>
</div>
</div> </div>
<hr /> <hr />
<div class="form-group mb-0"> <div class="form-group mb-0">
<div class="col-12"> <button
<button class="btn btn-block btn-danger"
class="btn btn-block btn-danger" onClick={linkEvent(
onClick={linkEvent( this,
this, this.handleDeleteAccountShowConfirmToggle
this.handleDeleteAccountShowConfirmToggle
)}
>
<T i18nKey="delete_account">#</T>
</button>
{this.state.deleteAccountShowConfirm && (
<>
<div class="my-2 alert alert-danger" role="alert">
<T i18nKey="delete_account_confirm">#</T>
</div>
<input
type="password"
value={this.state.deleteAccountForm.password}
onInput={linkEvent(
this,
this.handleDeleteAccountPasswordChange
)}
class="form-control my-2"
/>
<button
class="btn btn-danger mr-4"
disabled={!this.state.deleteAccountForm.password}
onClick={linkEvent(this, this.handleDeleteAccount)}
>
{this.state.deleteAccountLoading ? (
<svg class="icon icon-spinner spin">
<use xlinkHref="#icon-spinner"></use>
</svg>
) : (
capitalizeFirstLetter(i18n.t('delete'))
)}
</button>
<button
class="btn btn-secondary"
onClick={linkEvent(
this,
this.handleDeleteAccountShowConfirmToggle
)}
>
<T i18nKey="cancel">#</T>
</button>
</>
)} )}
</div> >
<T i18nKey="delete_account">#</T>
</button>
{this.state.deleteAccountShowConfirm && (
<>
<div class="my-2 alert alert-danger" role="alert">
<T i18nKey="delete_account_confirm">#</T>
</div>
<input
type="password"
value={this.state.deleteAccountForm.password}
onInput={linkEvent(
this,
this.handleDeleteAccountPasswordChange
)}
class="form-control my-2"
/>
<button
class="btn btn-danger mr-4"
disabled={!this.state.deleteAccountForm.password}
onClick={linkEvent(this, this.handleDeleteAccount)}
>
{this.state.deleteAccountLoading ? (
<svg class="icon icon-spinner spin">
<use xlinkHref="#icon-spinner"></use>
</svg>
) : (
capitalizeFirstLetter(i18n.t('delete'))
)}
</button>
<button
class="btn btn-secondary"
onClick={linkEvent(
this,
this.handleDeleteAccountShowConfirmToggle
)}
>
<T i18nKey="cancel">#</T>
</button>
</>
)}
</div> </div>
</form> </form>
</div> </div>
@ -786,6 +826,38 @@ export class User extends Component<any, UserState> {
this.setState(this.state); this.setState(this.state);
} }
handleUserSettingsEmailChange(i: User, event: any) {
i.state.userSettingsForm.email = event.target.value;
if (i.state.userSettingsForm.email == '' && !i.state.user.email) {
i.state.userSettingsForm.email = undefined;
}
i.setState(i.state);
}
handleUserSettingsNewPasswordChange(i: User, event: any) {
i.state.userSettingsForm.new_password = event.target.value;
if (i.state.userSettingsForm.new_password == '') {
i.state.userSettingsForm.new_password = undefined;
}
i.setState(i.state);
}
handleUserSettingsNewPasswordVerifyChange(i: User, event: any) {
i.state.userSettingsForm.new_password_verify = event.target.value;
if (i.state.userSettingsForm.new_password_verify == '') {
i.state.userSettingsForm.new_password_verify = undefined;
}
i.setState(i.state);
}
handleUserSettingsOldPasswordChange(i: User, event: any) {
i.state.userSettingsForm.old_password = event.target.value;
if (i.state.userSettingsForm.old_password == '') {
i.state.userSettingsForm.old_password = undefined;
}
i.setState(i.state);
}
handleImageUpload(i: User, event: any) { handleImageUpload(i: User, event: any) {
event.preventDefault(); event.preventDefault();
let file = event.target.files[0]; let file = event.target.files[0];
@ -856,6 +928,8 @@ export class User extends Component<any, UserState> {
if (msg.error) { if (msg.error) {
alert(i18n.t(msg.error)); alert(i18n.t(msg.error));
this.state.deleteAccountLoading = false; this.state.deleteAccountLoading = false;
this.state.avatarLoading = false;
this.state.userSettingsLoading = false;
if (msg.error == 'couldnt_find_that_username_or_email') { if (msg.error == 'couldnt_find_that_username_or_email') {
this.context.router.history.push('/'); this.context.router.history.push('/');
} }
@ -882,6 +956,7 @@ export class User extends Component<any, UserState> {
UserService.Instance.user.default_listing_type; UserService.Instance.user.default_listing_type;
this.state.userSettingsForm.lang = UserService.Instance.user.lang; this.state.userSettingsForm.lang = UserService.Instance.user.lang;
this.state.userSettingsForm.avatar = UserService.Instance.user.avatar; this.state.userSettingsForm.avatar = UserService.Instance.user.avatar;
this.state.userSettingsForm.email = this.state.user.email;
} }
document.title = `/u/${this.state.user.name} - ${WebSocketService.Instance.site.name}`; document.title = `/u/${this.state.user.name} - ${WebSocketService.Instance.site.name}`;
window.scrollTo(0, 0); window.scrollTo(0, 0);

View file

@ -87,6 +87,7 @@ export interface UserView {
id: number; id: number;
name: string; name: string;
avatar?: string; avatar?: string;
email?: string;
fedi_name: string; fedi_name: string;
published: string; published: string;
number_of_posts: number; number_of_posts: number;
@ -481,6 +482,10 @@ export interface UserSettingsForm {
default_listing_type: ListingType; default_listing_type: ListingType;
lang: string; lang: string;
avatar?: string; avatar?: string;
email?: string;
new_password?: string;
new_password_verify?: string;
old_password?: string;
auth: string; auth: string;
} }

View file

@ -118,6 +118,7 @@ export const en = {
unread_messages: 'Unread Messages', unread_messages: 'Unread Messages',
password: 'Password', password: 'Password',
verify_password: 'Verify Password', verify_password: 'Verify Password',
old_password: 'Old Password',
forgot_password: 'forgot password', forgot_password: 'forgot password',
reset_password_mail_sent: 'Sent an Email to reset your password.', reset_password_mail_sent: 'Sent an Email to reset your password.',
password_change: 'Password Change', password_change: 'Password Change',