diff --git a/docker_db_backup.sh b/docker_db_backup.sh index 5b87b818b..e361da194 100755 --- a/docker_db_backup.sh +++ b/docker_db_backup.sh @@ -1 +1 @@ -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 diff --git a/server/src/api/mod.rs b/server/src/api/mod.rs index e10770b4d..3a4a08658 100644 --- a/server/src/api/mod.rs +++ b/server/src/api/mod.rs @@ -22,7 +22,7 @@ pub mod site; #[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)] diff --git a/server/src/api/user.rs b/server/src/api/user.rs index b8f7408ba..2a6c214a7 100644 --- a/server/src/api/user.rs +++ b/server/src/api/user.rs @@ -18,6 +18,12 @@ pub struct Register { show_nsfw: bool, } +#[derive(Serialize, Deserialize)] +pub struct SaveUserSettings { + show_nsfw: bool, + auth: String, +} + #[derive(Serialize, Deserialize)] pub struct LoginResponse { op: String, @@ -221,6 +227,50 @@ impl Perform for Oper { } } +impl Perform for Oper { + fn perform(&self) -> Result { + 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 for Oper { fn perform(&self) -> Result { diff --git a/server/src/websocket/server.rs b/server/src/websocket/server.rs index fd2073b06..64f94f4cd 100644 --- a/server/src/websocket/server.rs +++ b/server/src/websocket/server.rs @@ -305,6 +305,11 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result { + 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()?; diff --git a/ui/package.json b/ui/package.json index d86725f25..523700a23 100644 --- a/ui/package.json +++ b/ui/package.json @@ -41,6 +41,6 @@ "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" } } diff --git a/ui/src/components/setup.tsx b/ui/src/components/setup.tsx index f11dc14e0..24a5f2d68 100644 --- a/ui/src/components/setup.tsx +++ b/ui/src/components/setup.tsx @@ -23,6 +23,7 @@ export class Setup extends Component { password: undefined, password_verify: undefined, admin: true, + show_nsfw: true, }, doneRegisteringUser: false, userLoading: false, diff --git a/ui/src/components/user.tsx b/ui/src/components/user.tsx index c6a70560f..39a13e162 100644 --- a/ui/src/components/user.tsx +++ b/ui/src/components/user.tsx @@ -2,8 +2,8 @@ import { Component, linkEvent } from 'inferno'; 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'; @@ -28,6 +28,8 @@ interface UserState { sort: SortType; page: number; loading: boolean; + userSettingsForm: UserSettingsForm; + userSettingsLoading: boolean; } export class User extends Component { @@ -54,6 +56,11 @@ export class User extends Component { 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) { @@ -75,6 +82,10 @@ export class User extends Component { 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)] : @@ -131,6 +142,9 @@ export class User extends Component {
{this.userInfo()} + {this.isCurrentUser && + this.userSettings() + } {this.moderates()} {this.follows()}
@@ -219,7 +233,7 @@ export class User extends Component { return (
{user.name}
-
{i18n.t('joined')}
+
{i18n.t('joined')}
@@ -235,6 +249,30 @@ export class User extends Component { ) } + userSettings() { + return ( +
+
#
+
+
+
+
+ + +
+
+
+
+
+ +
+
+ +
+ ) + } + moderates() { return (
@@ -329,6 +367,19 @@ export class User extends Component { 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); @@ -343,6 +394,9 @@ export class User extends Component { 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); @@ -378,6 +432,12 @@ export class User extends Component { 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); } } } diff --git a/ui/src/interfaces.ts b/ui/src/interfaces.ts index 7078ccacc..ebd42340d 100644 --- a/ui/src/interfaces.ts +++ b/ui/src/interfaces.ts @@ -1,5 +1,5 @@ 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 { @@ -347,7 +347,10 @@ export interface LoginResponse { jwt: string; } - +export interface UserSettingsForm { + show_nsfw: boolean; + auth: string; +} export interface CommunityForm { name: string; diff --git a/ui/src/services/WebSocketService.ts b/ui/src/services/WebSocketService.ts index c192c2b77..c34b6b3c1 100644 --- a/ui/src/services/WebSocketService.ts +++ b/ui/src/services/WebSocketService.ts @@ -1,5 +1,5 @@ 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'; @@ -184,6 +184,11 @@ export class WebSocketService { 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); diff --git a/ui/src/translations/en.ts b/ui/src/translations/en.ts index a8726b798..1f79bef2f 100644 --- a/ui/src/translations/en.ts +++ b/ui/src/translations/en.ts @@ -29,6 +29,7 @@ export const en = { mod: 'mod', mods: 'mods', moderates: 'Moderates', + settings: 'Settings', remove_as_mod: 'remove as mod', appoint_as_mod: 'appoint as mod', modlog: 'Modlog', diff --git a/ui/tslint.json b/ui/tslint.json index d3e7a8a97..938502e47 100644 --- a/ui/tslint.json +++ b/ui/tslint.json @@ -2,7 +2,7 @@ "extends": "tslint:recommended", "rules": { "forin": false, - "indent": [ true, "tabs" ], + "indent": [ true, "spaces" ], "interface-name": false, "ban-types": true, "max-classes-per-file": true, diff --git a/ui/yarn.lock b/ui/yarn.lock index f47c16c45..f31f45ae5 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -2773,7 +2773,7 @@ typescript@^2.6.2: 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==
#