-docker exec -it lemmy_db_1 pg_dumpall -c -U rrr > dump_`date +%d-%m-%Y"_"%H_%M_%S`.sql
+docker exec -it lemmy_db_1 pg_dumpall -c -U rrr > dump_`date +%Y-%m-%d"_"%H_%M_%S`.sql
#[derive(EnumString,ToString,Debug)]
pub enum UserOperation {
- Login, Register, CreateCommunity, CreatePost, ListCommunities, ListCategories, GetPost, GetCommunity, CreateComment, EditComment, SaveComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, SavePost, EditCommunity, FollowCommunity, GetFollowedCommunities, GetUserDetails, GetReplies, GetModlog, BanFromCommunity, AddModToCommunity, CreateSite, EditSite, GetSite, AddAdmin, BanUser, Search, MarkAllAsRead
+ Login, Register, CreateCommunity, CreatePost, ListCommunities, ListCategories, GetPost, GetCommunity, CreateComment, EditComment, SaveComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, SavePost, EditCommunity, FollowCommunity, GetFollowedCommunities, GetUserDetails, GetReplies, GetModlog, BanFromCommunity, AddModToCommunity, CreateSite, EditSite, GetSite, AddAdmin, BanUser, Search, MarkAllAsRead, SaveUserSettings
}
#[derive(Fail, Debug)]
show_nsfw: bool,
}
+#[derive(Serialize, Deserialize)]
+pub struct SaveUserSettings {
+ show_nsfw: bool,
+ auth: String,
+}
+
#[derive(Serialize, Deserialize)]
pub struct LoginResponse {
op: String,
}
}
+impl Perform<LoginResponse> for Oper<SaveUserSettings> {
+ fn perform(&self) -> Result<LoginResponse, Error> {
+ let data: &SaveUserSettings = &self.data;
+ let conn = establish_connection();
+
+ let claims = match Claims::decode(&data.auth) {
+ Ok(claims) => claims.claims,
+ Err(_e) => {
+ return Err(APIError::err(&self.op, "not_logged_in"))?
+ }
+ };
+
+ let user_id = claims.id;
+
+ let read_user = User_::read(&conn, user_id)?;
+
+ let user_form = UserForm {
+ name: read_user.name,
+ fedi_name: read_user.fedi_name,
+ email: read_user.email,
+ password_encrypted: read_user.password_encrypted,
+ preferred_username: read_user.preferred_username,
+ updated: Some(naive_now()),
+ admin: read_user.admin,
+ banned: read_user.banned,
+ show_nsfw: data.show_nsfw,
+ };
+
+ let updated_user = match User_::update(&conn, user_id, &user_form) {
+ Ok(user) => user,
+ Err(_e) => {
+ return Err(APIError::err(&self.op, "couldnt_update_user"))?
+ }
+ };
+
+ // Return the jwt
+ Ok(
+ LoginResponse {
+ op: self.op.to_string(),
+ jwt: updated_user.jwt()
+ }
+ )
+ }
+}
impl Perform<GetUserDetailsResponse> for Oper<GetUserDetails> {
fn perform(&self) -> Result<GetUserDetailsResponse, Error> {
let res = Oper::new(user_operation, get_user_details).perform()?;
Ok(serde_json::to_string(&res)?)
},
+ UserOperation::SaveUserSettings => {
+ let save_user_settings: SaveUserSettings = serde_json::from_str(data)?;
+ let res = Oper::new(user_operation, save_user_settings).perform()?;
+ Ok(serde_json::to_string(&res)?)
+ },
UserOperation::AddAdmin => {
let add_admin: AddAdmin = serde_json::from_str(data)?;
let res = Oper::new(user_operation, add_admin).perform()?;
"fuse-box": "^3.1.3",
"ts-transform-classcat": "^0.0.2",
"ts-transform-inferno": "^4.0.2",
- "typescript": "^3.3.3333"
+ "typescript": "^3.5.3"
}
}
password: undefined,
password_verify: undefined,
admin: true,
+ show_nsfw: true,
},
doneRegisteringUser: false,
userLoading: false,
import { Link } from 'inferno-router';
import { Subscription } from "rxjs";
import { retryWhen, delay, take } from 'rxjs/operators';
-import { UserOperation, Post, Comment, CommunityUser, GetUserDetailsForm, SortType, UserDetailsResponse, UserView, CommentResponse } from '../interfaces';
-import { WebSocketService } from '../services';
+import { UserOperation, Post, Comment, CommunityUser, GetUserDetailsForm, SortType, UserDetailsResponse, UserView, CommentResponse, UserSettingsForm, LoginResponse } from '../interfaces';
+import { WebSocketService, UserService } from '../services';
import { msgOp, fetchLimit, routeSortTypeToEnum, capitalizeFirstLetter } from '../utils';
import { PostListing } from './post-listing';
import { CommentNodes } from './comment-nodes';
sort: SortType;
page: number;
loading: boolean;
+ userSettingsForm: UserSettingsForm;
+ userSettingsLoading: boolean;
}
export class User extends Component<any, UserState> {
view: this.getViewFromProps(this.props),
sort: this.getSortTypeFromProps(this.props),
page: this.getPageFromProps(this.props),
+ userSettingsForm: {
+ show_nsfw: null,
+ auth: null,
+ },
+ userSettingsLoading: null,
}
constructor(props: any, context: any) {
this.refetch();
}
+ get isCurrentUser() {
+ return UserService.Instance.user && UserService.Instance.user.id == this.state.user.id;
+ }
+
getViewFromProps(props: any): View {
return (props.match.params.view) ?
View[capitalizeFirstLetter(props.match.params.view)] :
</div>
<div class="col-12 col-md-3">
{this.userInfo()}
+ {this.isCurrentUser &&
+ this.userSettings()
+ }
{this.moderates()}
{this.follows()}
</div>
return (
<div>
<h5>{user.name}</h5>
- <div>{i18n.t('joined')}<MomentTime data={user} /></div>
+ <div>{i18n.t('joined')} <MomentTime data={user} /></div>
<table class="table table-bordered table-sm mt-2">
<tr>
<td><T i18nKey="number_of_points" interpolation={{count: user.post_score}}>#</T></td>
)
}
+ userSettings() {
+ return (
+ <div>
+ <h5><T i18nKey="settings">#</T></h5>
+ <form onSubmit={linkEvent(this, this.handleUserSettingsSubmit)}>
+ <div class="form-group row">
+ <div class="col-12">
+ <div class="form-check">
+ <input class="form-check-input" type="checkbox" checked={this.state.userSettingsForm.show_nsfw} onChange={linkEvent(this, this.handleUserSettingsShowNsfwChange)}/>
+ <label class="form-check-label"><T i18nKey="show_nsfw">#</T></label>
+ </div>
+ </div>
+ </div>
+ <div class="form-group row">
+ <div class="col-12">
+ <button type="submit" class="btn btn-secondary">{this.state.userSettingsLoading ?
+ <svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg> : capitalizeFirstLetter(i18n.t('save'))}</button>
+ </div>
+ </div>
+ </form>
+ </div>
+ )
+ }
+
moderates() {
return (
<div>
i.refetch();
}
+ handleUserSettingsShowNsfwChange(i: User, event: any) {
+ i.state.userSettingsForm.show_nsfw = event.target.checked;
+ i.setState(i.state);
+ }
+
+ handleUserSettingsSubmit(i: User, event: any) {
+ event.preventDefault();
+ i.state.userSettingsLoading = true;
+ i.setState(i.state);
+
+ WebSocketService.Instance.saveUserSettings(i.state.userSettingsForm);
+ }
+
parseMessage(msg: any) {
console.log(msg);
let op: UserOperation = msgOp(msg);
this.state.moderates = res.moderates;
this.state.posts = res.posts;
this.state.loading = false;
+ if (this.isCurrentUser) {
+ this.state.userSettingsForm.show_nsfw = UserService.Instance.user.show_nsfw;
+ }
document.title = `/u/${this.state.user.name} - ${WebSocketService.Instance.site.name}`;
window.scrollTo(0,0);
this.setState(this.state);
if (res.comment.my_vote !== null)
found.my_vote = res.comment.my_vote;
this.setState(this.state);
+ } else if (op == UserOperation.SaveUserSettings) {
+ this.state = this.emptyState;
+ this.state.userSettingsLoading = false;
+ this.setState(this.state);
+ let res: LoginResponse = msg;
+ UserService.Instance.login(res);
}
}
}
export enum UserOperation {
- Login, Register, CreateCommunity, CreatePost, ListCommunities, ListCategories, GetPost, GetCommunity, CreateComment, EditComment, SaveComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, SavePost, EditCommunity, FollowCommunity, GetFollowedCommunities, GetUserDetails, GetReplies, GetModlog, BanFromCommunity, AddModToCommunity, CreateSite, EditSite, GetSite, AddAdmin, BanUser, Search, MarkAllAsRead
+ Login, Register, CreateCommunity, CreatePost, ListCommunities, ListCategories, GetPost, GetCommunity, CreateComment, EditComment, SaveComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, SavePost, EditCommunity, FollowCommunity, GetFollowedCommunities, GetUserDetails, GetReplies, GetModlog, BanFromCommunity, AddModToCommunity, CreateSite, EditSite, GetSite, AddAdmin, BanUser, Search, MarkAllAsRead, SaveUserSettings
}
export enum CommentSortType {
jwt: string;
}
-
+export interface UserSettingsForm {
+ show_nsfw: boolean;
+ auth: string;
+}
export interface CommunityForm {
name: string;
import { wsUri } from '../env';
-import { LoginForm, RegisterForm, UserOperation, CommunityForm, PostForm, SavePostForm, CommentForm, SaveCommentForm, CommentLikeForm, GetPostsForm, CreatePostLikeForm, FollowCommunityForm, GetUserDetailsForm, ListCommunitiesForm, GetModlogForm, BanFromCommunityForm, AddModToCommunityForm, AddAdminForm, BanUserForm, SiteForm, Site, UserView, GetRepliesForm, SearchForm } from '../interfaces';
+import { LoginForm, RegisterForm, UserOperation, CommunityForm, PostForm, SavePostForm, CommentForm, SaveCommentForm, CommentLikeForm, GetPostsForm, CreatePostLikeForm, FollowCommunityForm, GetUserDetailsForm, ListCommunitiesForm, GetModlogForm, BanFromCommunityForm, AddModToCommunityForm, AddAdminForm, BanUserForm, SiteForm, Site, UserView, GetRepliesForm, SearchForm, UserSettingsForm } from '../interfaces';
import { webSocket } from 'rxjs/webSocket';
import { Subject } from 'rxjs';
import { retryWhen, delay, take } from 'rxjs/operators';
this.subject.next(this.wsSendWrapper(UserOperation.MarkAllAsRead, form));
}
+ public saveUserSettings(userSettingsForm: UserSettingsForm) {
+ this.setAuth(userSettingsForm);
+ this.subject.next(this.wsSendWrapper(UserOperation.SaveUserSettings, userSettingsForm));
+ }
+
private wsSendWrapper(op: UserOperation, data: any) {
let send = { op: UserOperation[op], data: data };
console.log(send);
mod: 'mod',
mods: 'mods',
moderates: 'Moderates',
+ settings: 'Settings',
remove_as_mod: 'remove as mod',
appoint_as_mod: 'appoint as mod',
modlog: 'Modlog',
"extends": "tslint:recommended",
"rules": {
"forin": false,
- "indent": [ true, "tabs" ],
+ "indent": [ true, "spaces" ],
"interface-name": false,
"ban-types": true,
"max-classes-per-file": true,
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c"
integrity sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==
-typescript@^3.3.3333:
+typescript@^3.5.3:
version "3.5.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977"
integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==