parent
62ae25f90d
commit
272ab3948c
35 changed files with 372 additions and 87 deletions
1
server/migrations/2019-10-15-181630_add_themes/down.sql
vendored
Normal file
1
server/migrations/2019-10-15-181630_add_themes/down.sql
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
alter table user_ drop column theme;
|
1
server/migrations/2019-10-15-181630_add_themes/up.sql
vendored
Normal file
1
server/migrations/2019-10-15-181630_add_themes/up.sql
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
alter table user_ add column theme varchar(20) default 'darkly' not null;
|
|
@ -21,6 +21,7 @@ pub struct Register {
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct SaveUserSettings {
|
pub struct SaveUserSettings {
|
||||||
show_nsfw: bool,
|
show_nsfw: bool,
|
||||||
|
theme: String,
|
||||||
auth: String,
|
auth: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,6 +163,7 @@ impl Perform<LoginResponse> for Oper<Register> {
|
||||||
admin: data.admin,
|
admin: data.admin,
|
||||||
banned: false,
|
banned: false,
|
||||||
show_nsfw: data.show_nsfw,
|
show_nsfw: data.show_nsfw,
|
||||||
|
theme: "darkly".into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create the user
|
// Create the user
|
||||||
|
@ -252,6 +254,7 @@ impl Perform<LoginResponse> for Oper<SaveUserSettings> {
|
||||||
admin: read_user.admin,
|
admin: read_user.admin,
|
||||||
banned: read_user.banned,
|
banned: read_user.banned,
|
||||||
show_nsfw: data.show_nsfw,
|
show_nsfw: data.show_nsfw,
|
||||||
|
theme: data.theme.to_owned(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let updated_user = match User_::update(&conn, user_id, &user_form) {
|
let updated_user = match User_::update(&conn, user_id, &user_form) {
|
||||||
|
@ -416,6 +419,7 @@ impl Perform<AddAdminResponse> for Oper<AddAdmin> {
|
||||||
admin: data.added,
|
admin: data.added,
|
||||||
banned: read_user.banned,
|
banned: read_user.banned,
|
||||||
show_nsfw: read_user.show_nsfw,
|
show_nsfw: read_user.show_nsfw,
|
||||||
|
theme: read_user.theme,
|
||||||
};
|
};
|
||||||
|
|
||||||
match User_::update(&conn, data.user_id, &user_form) {
|
match User_::update(&conn, data.user_id, &user_form) {
|
||||||
|
@ -474,6 +478,7 @@ impl Perform<BanUserResponse> for Oper<BanUser> {
|
||||||
admin: read_user.admin,
|
admin: read_user.admin,
|
||||||
banned: data.ban,
|
banned: data.ban,
|
||||||
show_nsfw: read_user.show_nsfw,
|
show_nsfw: read_user.show_nsfw,
|
||||||
|
theme: read_user.theme,
|
||||||
};
|
};
|
||||||
|
|
||||||
match User_::update(&conn, data.user_id, &user_form) {
|
match User_::update(&conn, data.user_id, &user_form) {
|
||||||
|
|
|
@ -72,6 +72,7 @@ mod tests {
|
||||||
banned: false,
|
banned: false,
|
||||||
updated: None,
|
updated: None,
|
||||||
show_nsfw: false,
|
show_nsfw: false,
|
||||||
|
theme: "darkly".into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let person = expected_user.person();
|
let person = expected_user.person();
|
||||||
|
|
|
@ -178,6 +178,7 @@ mod tests {
|
||||||
banned: false,
|
banned: false,
|
||||||
updated: None,
|
updated: None,
|
||||||
show_nsfw: false,
|
show_nsfw: false,
|
||||||
|
theme: "darkly".into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
||||||
|
|
|
@ -264,6 +264,7 @@ mod tests {
|
||||||
banned: false,
|
banned: false,
|
||||||
updated: None,
|
updated: None,
|
||||||
show_nsfw: false,
|
show_nsfw: false,
|
||||||
|
theme: "darkly".into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
||||||
|
|
|
@ -264,6 +264,7 @@ mod tests {
|
||||||
banned: false,
|
banned: false,
|
||||||
updated: None,
|
updated: None,
|
||||||
show_nsfw: false,
|
show_nsfw: false,
|
||||||
|
theme: "darkly".into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
||||||
|
|
|
@ -446,6 +446,7 @@ mod tests {
|
||||||
banned: false,
|
banned: false,
|
||||||
updated: None,
|
updated: None,
|
||||||
show_nsfw: false,
|
show_nsfw: false,
|
||||||
|
theme: "darkly".into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_mod = User_::create(&conn, &new_mod).unwrap();
|
let inserted_mod = User_::create(&conn, &new_mod).unwrap();
|
||||||
|
@ -460,6 +461,7 @@ mod tests {
|
||||||
banned: false,
|
banned: false,
|
||||||
updated: None,
|
updated: None,
|
||||||
show_nsfw: false,
|
show_nsfw: false,
|
||||||
|
theme: "darkly".into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
||||||
|
|
|
@ -191,6 +191,7 @@ mod tests {
|
||||||
banned: false,
|
banned: false,
|
||||||
updated: None,
|
updated: None,
|
||||||
show_nsfw: false,
|
show_nsfw: false,
|
||||||
|
theme: "darkly".into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
||||||
|
|
|
@ -225,6 +225,7 @@ mod tests {
|
||||||
admin: false,
|
admin: false,
|
||||||
banned: false,
|
banned: false,
|
||||||
show_nsfw: false,
|
show_nsfw: false,
|
||||||
|
theme: "darkly".into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
||||||
|
|
|
@ -20,6 +20,7 @@ pub struct User_ {
|
||||||
pub published: chrono::NaiveDateTime,
|
pub published: chrono::NaiveDateTime,
|
||||||
pub updated: Option<chrono::NaiveDateTime>,
|
pub updated: Option<chrono::NaiveDateTime>,
|
||||||
pub show_nsfw: bool,
|
pub show_nsfw: bool,
|
||||||
|
pub theme: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Insertable, AsChangeset, Clone)]
|
#[derive(Insertable, AsChangeset, Clone)]
|
||||||
|
@ -34,6 +35,7 @@ pub struct UserForm {
|
||||||
pub email: Option<String>,
|
pub email: Option<String>,
|
||||||
pub updated: Option<chrono::NaiveDateTime>,
|
pub updated: Option<chrono::NaiveDateTime>,
|
||||||
pub show_nsfw: bool,
|
pub show_nsfw: bool,
|
||||||
|
pub theme: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Crud<UserForm> for User_ {
|
impl Crud<UserForm> for User_ {
|
||||||
|
@ -74,6 +76,7 @@ pub struct Claims {
|
||||||
pub username: String,
|
pub username: String,
|
||||||
pub iss: String,
|
pub iss: String,
|
||||||
pub show_nsfw: bool,
|
pub show_nsfw: bool,
|
||||||
|
pub theme: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Claims {
|
impl Claims {
|
||||||
|
@ -94,6 +97,7 @@ impl User_ {
|
||||||
username: self.name.to_owned(),
|
username: self.name.to_owned(),
|
||||||
iss: self.fedi_name.to_owned(),
|
iss: self.fedi_name.to_owned(),
|
||||||
show_nsfw: self.show_nsfw,
|
show_nsfw: self.show_nsfw,
|
||||||
|
theme: self.theme.to_owned(),
|
||||||
};
|
};
|
||||||
encode(
|
encode(
|
||||||
&Header::default(),
|
&Header::default(),
|
||||||
|
@ -141,6 +145,7 @@ mod tests {
|
||||||
banned: false,
|
banned: false,
|
||||||
updated: None,
|
updated: None,
|
||||||
show_nsfw: false,
|
show_nsfw: false,
|
||||||
|
theme: "darkly".into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
let inserted_user = User_::create(&conn, &new_user).unwrap();
|
||||||
|
@ -158,6 +163,7 @@ mod tests {
|
||||||
published: inserted_user.published,
|
published: inserted_user.published,
|
||||||
updated: None,
|
updated: None,
|
||||||
show_nsfw: false,
|
show_nsfw: false,
|
||||||
|
theme: "darkly".into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let read_user = User_::read(&conn, inserted_user.id).unwrap();
|
let read_user = User_::read(&conn, inserted_user.id).unwrap();
|
||||||
|
|
|
@ -254,6 +254,7 @@ table! {
|
||||||
published -> Timestamp,
|
published -> Timestamp,
|
||||||
updated -> Nullable<Timestamp>,
|
updated -> Nullable<Timestamp>,
|
||||||
show_nsfw -> Bool,
|
show_nsfw -> Bool,
|
||||||
|
theme -> Varchar,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,28 +305,28 @@ joinable!(site -> user_ (creator_id));
|
||||||
joinable!(user_ban -> user_ (user_id));
|
joinable!(user_ban -> user_ (user_id));
|
||||||
|
|
||||||
allow_tables_to_appear_in_same_query!(
|
allow_tables_to_appear_in_same_query!(
|
||||||
category,
|
category,
|
||||||
comment,
|
comment,
|
||||||
comment_like,
|
comment_like,
|
||||||
comment_saved,
|
comment_saved,
|
||||||
community,
|
community,
|
||||||
community_follower,
|
community_follower,
|
||||||
community_moderator,
|
community_moderator,
|
||||||
community_user_ban,
|
community_user_ban,
|
||||||
mod_add,
|
mod_add,
|
||||||
mod_add_community,
|
mod_add_community,
|
||||||
mod_ban,
|
mod_ban,
|
||||||
mod_ban_from_community,
|
mod_ban_from_community,
|
||||||
mod_lock_post,
|
mod_lock_post,
|
||||||
mod_remove_comment,
|
mod_remove_comment,
|
||||||
mod_remove_community,
|
mod_remove_community,
|
||||||
mod_remove_post,
|
mod_remove_post,
|
||||||
mod_sticky_post,
|
mod_sticky_post,
|
||||||
post,
|
post,
|
||||||
post_like,
|
post_like,
|
||||||
post_read,
|
post_read,
|
||||||
post_saved,
|
post_saved,
|
||||||
site,
|
site,
|
||||||
user_,
|
user_,
|
||||||
user_ban,
|
user_ban,
|
||||||
);
|
);
|
||||||
|
|
27
ui/src/css/main.css → ui/assets/css/main.css
vendored
27
ui/src/css/main.css → ui/assets/css/main.css
vendored
|
@ -1,7 +1,3 @@
|
||||||
body, .text-white, .navbar-brand, .badge-light, .btn-secondary {
|
|
||||||
color: #dedede !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-toggler {
|
.navbar-toggler {
|
||||||
border: 0px;
|
border: 0px;
|
||||||
}
|
}
|
||||||
|
@ -31,29 +27,6 @@ body, .text-white, .navbar-brand, .badge-light, .btn-secondary {
|
||||||
margin-top: -10px;
|
margin-top: -10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-control, .form-control:focus {
|
|
||||||
background-color: var(--secondary);
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-control:disabled {
|
|
||||||
background-color: var(--secondary);
|
|
||||||
opacity: .5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-select {
|
|
||||||
color: #fff;
|
|
||||||
background-color: var(--secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.mark {
|
|
||||||
background-color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mark-two {
|
|
||||||
background-color: #444 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.md-div p:last-child {
|
.md-div p:last-child {
|
||||||
margin-bottom: 0px;
|
margin-bottom: 0px;
|
||||||
}
|
}
|
83
ui/assets/css/themes/cyborg.min.css
vendored
Normal file
83
ui/assets/css/themes/cyborg.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
35
ui/assets/css/themes/darkly.min.css
vendored
Normal file
35
ui/assets/css/themes/darkly.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
12
ui/assets/css/themes/journal.min.css
vendored
Normal file
12
ui/assets/css/themes/journal.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
12
ui/assets/css/themes/litera.min.css
vendored
Normal file
12
ui/assets/css/themes/litera.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
12
ui/assets/css/themes/minty.min.css
vendored
Normal file
12
ui/assets/css/themes/minty.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
12
ui/assets/css/themes/sketchy.min.css
vendored
Normal file
12
ui/assets/css/themes/sketchy.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
68
ui/assets/css/themes/solar.min.css
vendored
Normal file
68
ui/assets/css/themes/solar.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
12
ui/assets/css/themes/united.min.css
vendored
Normal file
12
ui/assets/css/themes/united.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
ui/package.json
vendored
1
ui/package.json
vendored
|
@ -21,6 +21,7 @@
|
||||||
"@types/markdown-it": "^0.0.7",
|
"@types/markdown-it": "^0.0.7",
|
||||||
"@types/markdown-it-container": "^2.0.2",
|
"@types/markdown-it-container": "^2.0.2",
|
||||||
"autosize": "^4.0.2",
|
"autosize": "^4.0.2",
|
||||||
|
"bootswatch": "^4.3.1",
|
||||||
"classcat": "^1.1.3",
|
"classcat": "^1.1.3",
|
||||||
"dotenv": "^6.1.0",
|
"dotenv": "^6.1.0",
|
||||||
"emoji-short-name": "^0.1.0",
|
"emoji-short-name": "^0.1.0",
|
||||||
|
|
2
ui/src/components/footer.tsx
vendored
2
ui/src/components/footer.tsx
vendored
|
@ -13,7 +13,7 @@ export class Footer extends Component<any, any> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<nav class="container navbar navbar-expand-md navbar-light navbar-bg p-0 px-3 my-2">
|
<nav class="container navbar navbar-expand-md navbar-light navbar-bg p-0 px-3 mt-2">
|
||||||
<div className="navbar-collapse">
|
<div className="navbar-collapse">
|
||||||
<ul class="navbar-nav ml-auto">
|
<ul class="navbar-nav ml-auto">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
|
|
4
ui/src/components/post-listing.tsx
vendored
4
ui/src/components/post-listing.tsx
vendored
|
@ -96,8 +96,8 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
<div className="post-title">
|
<div className="post-title">
|
||||||
<h5 className="mb-0 d-inline">
|
<h5 className="mb-0 d-inline">
|
||||||
{post.url ?
|
{post.url ?
|
||||||
<a className="text-white" href={post.url} target="_blank" title={post.url}>{post.name}</a> :
|
<a className="text-body" href={post.url} target="_blank" title={post.url}>{post.name}</a> :
|
||||||
<Link className="text-white" to={`/post/${post.id}`} title={i18n.t('comments')}>{post.name}</Link>
|
<Link className="text-body" to={`/post/${post.id}`} title={i18n.t('comments')}>{post.name}</Link>
|
||||||
}
|
}
|
||||||
</h5>
|
</h5>
|
||||||
{post.url &&
|
{post.url &&
|
||||||
|
|
2
ui/src/components/post.tsx
vendored
2
ui/src/components/post.tsx
vendored
|
@ -74,7 +74,7 @@ export class Post extends Component<any, PostState> {
|
||||||
if (this.state.scrolled_comment_id && !this.state.scrolled && lastState.comments.length > 0) {
|
if (this.state.scrolled_comment_id && !this.state.scrolled && lastState.comments.length > 0) {
|
||||||
var elmnt = document.getElementById(`comment-${this.state.scrolled_comment_id}`);
|
var elmnt = document.getElementById(`comment-${this.state.scrolled_comment_id}`);
|
||||||
elmnt.scrollIntoView();
|
elmnt.scrollIntoView();
|
||||||
elmnt.classList.add("mark-two");
|
elmnt.classList.add("mark");
|
||||||
this.state.scrolled = true;
|
this.state.scrolled = true;
|
||||||
this.markScrolledAsRead(this.state.scrolled_comment_id);
|
this.markScrolledAsRead(this.state.scrolled_comment_id);
|
||||||
}
|
}
|
||||||
|
|
47
ui/src/components/user.tsx
vendored
47
ui/src/components/user.tsx
vendored
|
@ -4,7 +4,7 @@ import { Subscription } from "rxjs";
|
||||||
import { retryWhen, delay, take } from 'rxjs/operators';
|
import { retryWhen, delay, take } from 'rxjs/operators';
|
||||||
import { UserOperation, Post, Comment, CommunityUser, GetUserDetailsForm, SortType, UserDetailsResponse, UserView, CommentResponse, UserSettingsForm, LoginResponse, BanUserResponse, AddAdminResponse } from '../interfaces';
|
import { UserOperation, Post, Comment, CommunityUser, GetUserDetailsForm, SortType, UserDetailsResponse, UserView, CommentResponse, UserSettingsForm, LoginResponse, BanUserResponse, AddAdminResponse } from '../interfaces';
|
||||||
import { WebSocketService, UserService } from '../services';
|
import { WebSocketService, UserService } from '../services';
|
||||||
import { msgOp, fetchLimit, routeSortTypeToEnum, capitalizeFirstLetter } from '../utils';
|
import { msgOp, fetchLimit, routeSortTypeToEnum, capitalizeFirstLetter, themes, setTheme } from '../utils';
|
||||||
import { PostListing } from './post-listing';
|
import { PostListing } from './post-listing';
|
||||||
import { CommentNodes } from './comment-nodes';
|
import { CommentNodes } from './comment-nodes';
|
||||||
import { MomentTime } from './moment-time';
|
import { MomentTime } from './moment-time';
|
||||||
|
@ -61,6 +61,7 @@ export class User extends Component<any, UserState> {
|
||||||
page: this.getPageFromProps(this.props),
|
page: this.getPageFromProps(this.props),
|
||||||
userSettingsForm: {
|
userSettingsForm: {
|
||||||
show_nsfw: null,
|
show_nsfw: null,
|
||||||
|
theme: null,
|
||||||
auth: null,
|
auth: null,
|
||||||
},
|
},
|
||||||
userSettingsLoading: null,
|
userSettingsLoading: null,
|
||||||
|
@ -285,7 +286,18 @@ export class User extends Component<any, UserState> {
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5><T i18nKey="settings">#</T></h5>
|
<h5><T i18nKey="settings">#</T></h5>
|
||||||
<form onSubmit={linkEvent(this, this.handleUserSettingsSubmit)}>
|
<form onSubmit={linkEvent(this, this.handleUserSettingsSubmit)}>
|
||||||
<div class="form-group row">
|
<div class="form-group">
|
||||||
|
<div class="col-12">
|
||||||
|
<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></option>
|
||||||
|
{themes.map(theme =>
|
||||||
|
<option value={theme}>{theme}</option>
|
||||||
|
)}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<input class="form-check-input" type="checkbox" checked={this.state.userSettingsForm.show_nsfw} onChange={linkEvent(this, this.handleUserSettingsShowNsfwChange)}/>
|
<input class="form-check-input" type="checkbox" checked={this.state.userSettingsForm.show_nsfw} onChange={linkEvent(this, this.handleUserSettingsShowNsfwChange)}/>
|
||||||
|
@ -309,20 +321,18 @@ export class User extends Component<any, UserState> {
|
||||||
moderates() {
|
moderates() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div class="card border-secondary mb-3">
|
{this.state.moderates.length > 0 &&
|
||||||
<div class="card-body">
|
<div class="card border-secondary mb-3">
|
||||||
{this.state.moderates.length > 0 &&
|
<div class="card-body">
|
||||||
<div>
|
<h5><T i18nKey="moderates">#</T></h5>
|
||||||
<h5><T i18nKey="moderates">#</T></h5>
|
<ul class="list-unstyled mb-0">
|
||||||
<ul class="list-unstyled mb-0">
|
{this.state.moderates.map(community =>
|
||||||
{this.state.moderates.map(community =>
|
<li><Link to={`/c/${community.community_name}`}>{community.community_name}</Link></li>
|
||||||
<li><Link to={`/c/${community.community_name}`}>{community.community_name}</Link></li>
|
)}
|
||||||
)}
|
</ul>
|
||||||
</ul>
|
</div>
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -410,6 +420,12 @@ export class User extends Component<any, UserState> {
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleUserSettingsThemeChange(i: User, event: any) {
|
||||||
|
i.state.userSettingsForm.theme = event.target.value;
|
||||||
|
setTheme(event.target.value);
|
||||||
|
i.setState(i.state);
|
||||||
|
}
|
||||||
|
|
||||||
handleUserSettingsSubmit(i: User, event: any) {
|
handleUserSettingsSubmit(i: User, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
i.state.userSettingsLoading = true;
|
i.state.userSettingsLoading = true;
|
||||||
|
@ -435,6 +451,7 @@ export class User extends Component<any, UserState> {
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
if (this.isCurrentUser) {
|
if (this.isCurrentUser) {
|
||||||
this.state.userSettingsForm.show_nsfw = UserService.Instance.user.show_nsfw;
|
this.state.userSettingsForm.show_nsfw = UserService.Instance.user.show_nsfw;
|
||||||
|
this.state.userSettingsForm.theme = UserService.Instance.user.theme ? UserService.Instance.user.theme : 'darkly';
|
||||||
}
|
}
|
||||||
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);
|
||||||
|
|
12
ui/src/css/bootstrap.min.css
vendored
12
ui/src/css/bootstrap.min.css
vendored
File diff suppressed because one or more lines are too long
16
ui/src/index.html
vendored
16
ui/src/index.html
vendored
|
@ -6,8 +6,24 @@
|
||||||
<meta name="Description" content="Lemmy">
|
<meta name="Description" content="Lemmy">
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
|
||||||
|
<!-- Icons -->
|
||||||
<link rel="shortcut icon" type="image/svg+xml" href="/static/assets/favicon.svg" />
|
<link rel="shortcut icon" type="image/svg+xml" href="/static/assets/favicon.svg" />
|
||||||
<link rel="apple-touch-icon" href="/static/assets/apple-touch-icon.png" />
|
<link rel="apple-touch-icon" href="/static/assets/apple-touch-icon.png" />
|
||||||
|
|
||||||
|
<!-- Styles -->
|
||||||
|
<link rel="stylesheet" type="text/css" href="/static/assets/css/tribute.css" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="/static/assets/css/themes/litera.min.css" id="litera" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="/static/assets/css/themes/minty.min.css" id="minty" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="/static/assets/css/themes/solar.min.css" id="solar" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="/static/assets/css/themes/united.min.css" id="united" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="/static/assets/css/themes/cyborg.min.css" id="cyborg" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="/static/assets/css/themes/darkly.min.css" id="darkly" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="/static/assets/css/themes/journal.min.css" id="journal" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="/static/assets/css/themes/sketchy.min.css" id="sketchy" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="/static/assets/css/main.css" />
|
||||||
|
|
||||||
|
<!-- Scripts -->
|
||||||
<script async src="/static/assets/libs/sortable/sortable.min.js"></script>
|
<script async src="/static/assets/libs/sortable/sortable.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|
4
ui/src/index.tsx
vendored
4
ui/src/index.tsx
vendored
|
@ -19,10 +19,6 @@ import { Sponsors } from './components/sponsors';
|
||||||
import { Symbols } from './components/symbols';
|
import { Symbols } from './components/symbols';
|
||||||
import { i18n } from './i18next';
|
import { i18n } from './i18next';
|
||||||
|
|
||||||
import './css/tribute.css';
|
|
||||||
import './css/bootstrap.min.css';
|
|
||||||
import './css/main.css';
|
|
||||||
|
|
||||||
import { WebSocketService, UserService } from './services';
|
import { WebSocketService, UserService } from './services';
|
||||||
|
|
||||||
const container = document.getElementById('app');
|
const container = document.getElementById('app');
|
||||||
|
|
2
ui/src/interfaces.ts
vendored
2
ui/src/interfaces.ts
vendored
|
@ -23,6 +23,7 @@ export interface User {
|
||||||
iss: string;
|
iss: string;
|
||||||
username: string;
|
username: string;
|
||||||
show_nsfw: boolean;
|
show_nsfw: boolean;
|
||||||
|
theme: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UserView {
|
export interface UserView {
|
||||||
|
@ -381,6 +382,7 @@ export interface LoginResponse {
|
||||||
|
|
||||||
export interface UserSettingsForm {
|
export interface UserSettingsForm {
|
||||||
show_nsfw: boolean;
|
show_nsfw: boolean;
|
||||||
|
theme: string;
|
||||||
auth: string;
|
auth: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
6
ui/src/services/UserService.ts
vendored
6
ui/src/services/UserService.ts
vendored
|
@ -1,5 +1,6 @@
|
||||||
import * as Cookies from 'js-cookie';
|
import * as Cookies from 'js-cookie';
|
||||||
import { User, LoginResponse } from '../interfaces';
|
import { User, LoginResponse } from '../interfaces';
|
||||||
|
import { setTheme } from '../utils';
|
||||||
import * as jwt_decode from 'jwt-decode';
|
import * as jwt_decode from 'jwt-decode';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
|
|
||||||
|
@ -14,6 +15,7 @@ export class UserService {
|
||||||
if (jwt) {
|
if (jwt) {
|
||||||
this.setUser(jwt);
|
this.setUser(jwt);
|
||||||
} else {
|
} else {
|
||||||
|
setTheme();
|
||||||
console.log('No JWT cookie found.');
|
console.log('No JWT cookie found.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,8 +29,9 @@ export class UserService {
|
||||||
public logout() {
|
public logout() {
|
||||||
this.user = undefined;
|
this.user = undefined;
|
||||||
Cookies.remove("jwt");
|
Cookies.remove("jwt");
|
||||||
console.log("Logged out.");
|
setTheme();
|
||||||
this.sub.next({user: undefined, unreadCount: 0});
|
this.sub.next({user: undefined, unreadCount: 0});
|
||||||
|
console.log("Logged out.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public get auth(): string {
|
public get auth(): string {
|
||||||
|
@ -37,6 +40,7 @@ export class UserService {
|
||||||
|
|
||||||
private setUser(jwt: string) {
|
private setUser(jwt: string) {
|
||||||
this.user = jwt_decode(jwt);
|
this.user = jwt_decode(jwt);
|
||||||
|
setTheme(this.user.theme);
|
||||||
this.sub.next({user: this.user, unreadCount: 0});
|
this.sub.next({user: this.user, unreadCount: 0});
|
||||||
console.log(this.user);
|
console.log(this.user);
|
||||||
}
|
}
|
||||||
|
|
1
ui/src/translations/en.ts
vendored
1
ui/src/translations/en.ts
vendored
|
@ -129,6 +129,7 @@ export const en = {
|
||||||
modified: 'modified',
|
modified: 'modified',
|
||||||
nsfw: 'NSFW',
|
nsfw: 'NSFW',
|
||||||
show_nsfw: 'Show NSFW content',
|
show_nsfw: 'Show NSFW content',
|
||||||
|
theme: 'Theme',
|
||||||
sponsors: 'Sponsors',
|
sponsors: 'Sponsors',
|
||||||
sponsors_of_lemmy: 'Sponsors of Lemmy',
|
sponsors_of_lemmy: 'Sponsors of Lemmy',
|
||||||
sponsor_message: 'Lemmy is free, <1>open-source</1> software, meaning no advertising, monetizing, or venture capital, ever. Your donations directly support full-time development of the project. Thank you to the following people:',
|
sponsor_message: 'Lemmy is free, <1>open-source</1> software, meaning no advertising, monetizing, or venture capital, ever. Your donations directly support full-time development of the project. Thank you to the following people:',
|
||||||
|
|
14
ui/src/utils.ts
vendored
14
ui/src/utils.ts
vendored
|
@ -238,3 +238,17 @@ export function getMomentLanguage(): string {
|
||||||
}
|
}
|
||||||
return lang;
|
return lang;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const themes = ['litera', 'minty', 'solar', 'united', 'cyborg','darkly', 'journal', 'sketchy'];
|
||||||
|
|
||||||
|
export function setTheme(theme: string = 'darkly') {
|
||||||
|
for (var i=0; i < themes.length; i++) {
|
||||||
|
|
||||||
|
let styleSheet = document.getElementById(themes[i]);
|
||||||
|
if (themes[i] == theme) {
|
||||||
|
styleSheet.removeAttribute("disabled");
|
||||||
|
} else {
|
||||||
|
styleSheet.setAttribute("disabled", "disabled");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
5
ui/yarn.lock
vendored
5
ui/yarn.lock
vendored
|
@ -381,6 +381,11 @@ body@^5.1.0:
|
||||||
raw-body "~1.1.0"
|
raw-body "~1.1.0"
|
||||||
safe-json-parse "~1.0.1"
|
safe-json-parse "~1.0.1"
|
||||||
|
|
||||||
|
bootswatch@^4.3.1:
|
||||||
|
version "4.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/bootswatch/-/bootswatch-4.3.1.tgz#be54748b420a1962dbcf9782605aac092f842e38"
|
||||||
|
integrity sha512-kNdpo/TnhO++aic1IODLIe1V0lx6pXwHMpwXMacpANDnuVDtgU1MUgUbVMC3rSWm4UcbImfwPraNYgjKDT0BtA==
|
||||||
|
|
||||||
bowser@^2.0.0-beta.3:
|
bowser@^2.0.0-beta.3:
|
||||||
version "2.6.1"
|
version "2.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.6.1.tgz#196599588af6f0413449c79ab3bf7a5a1bb3384f"
|
resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.6.1.tgz#196599588af6f0413449c79ab3bf7a5a1bb3384f"
|
||||||
|
|
Reference in a new issue