Adding 8 different themes.

- Fixes #276
This commit is contained in:
Dessalines 2019-10-15 12:21:27 -07:00
parent 62ae25f90d
commit 272ab3948c
35 changed files with 372 additions and 87 deletions

View file

@ -0,0 +1 @@
alter table user_ drop column theme;

View file

@ -0,0 +1 @@
alter table user_ add column theme varchar(20) default 'darkly' not null;

View file

@ -21,6 +21,7 @@ pub struct Register {
#[derive(Serialize, Deserialize)]
pub struct SaveUserSettings {
show_nsfw: bool,
theme: String,
auth: String,
}
@ -162,6 +163,7 @@ impl Perform<LoginResponse> for Oper<Register> {
admin: data.admin,
banned: false,
show_nsfw: data.show_nsfw,
theme: "darkly".into(),
};
// Create the user
@ -252,6 +254,7 @@ impl Perform<LoginResponse> for Oper<SaveUserSettings> {
admin: read_user.admin,
banned: read_user.banned,
show_nsfw: data.show_nsfw,
theme: data.theme.to_owned(),
};
let updated_user = match User_::update(&conn, user_id, &user_form) {
@ -416,6 +419,7 @@ impl Perform<AddAdminResponse> for Oper<AddAdmin> {
admin: data.added,
banned: read_user.banned,
show_nsfw: read_user.show_nsfw,
theme: read_user.theme,
};
match User_::update(&conn, data.user_id, &user_form) {
@ -474,6 +478,7 @@ impl Perform<BanUserResponse> for Oper<BanUser> {
admin: read_user.admin,
banned: data.ban,
show_nsfw: read_user.show_nsfw,
theme: read_user.theme,
};
match User_::update(&conn, data.user_id, &user_form) {

View file

@ -72,6 +72,7 @@ mod tests {
banned: false,
updated: None,
show_nsfw: false,
theme: "darkly".into(),
};
let person = expected_user.person();

View file

@ -178,6 +178,7 @@ mod tests {
banned: false,
updated: None,
show_nsfw: false,
theme: "darkly".into(),
};
let inserted_user = User_::create(&conn, &new_user).unwrap();

View file

@ -264,6 +264,7 @@ mod tests {
banned: false,
updated: None,
show_nsfw: false,
theme: "darkly".into(),
};
let inserted_user = User_::create(&conn, &new_user).unwrap();

View file

@ -264,6 +264,7 @@ mod tests {
banned: false,
updated: None,
show_nsfw: false,
theme: "darkly".into(),
};
let inserted_user = User_::create(&conn, &new_user).unwrap();

View file

@ -446,6 +446,7 @@ mod tests {
banned: false,
updated: None,
show_nsfw: false,
theme: "darkly".into(),
};
let inserted_mod = User_::create(&conn, &new_mod).unwrap();
@ -460,6 +461,7 @@ mod tests {
banned: false,
updated: None,
show_nsfw: false,
theme: "darkly".into(),
};
let inserted_user = User_::create(&conn, &new_user).unwrap();

View file

@ -191,6 +191,7 @@ mod tests {
banned: false,
updated: None,
show_nsfw: false,
theme: "darkly".into(),
};
let inserted_user = User_::create(&conn, &new_user).unwrap();

View file

@ -225,6 +225,7 @@ mod tests {
admin: false,
banned: false,
show_nsfw: false,
theme: "darkly".into(),
};
let inserted_user = User_::create(&conn, &new_user).unwrap();

View file

@ -20,6 +20,7 @@ pub struct User_ {
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>,
pub show_nsfw: bool,
pub theme: String,
}
#[derive(Insertable, AsChangeset, Clone)]
@ -34,6 +35,7 @@ pub struct UserForm {
pub email: Option<String>,
pub updated: Option<chrono::NaiveDateTime>,
pub show_nsfw: bool,
pub theme: String,
}
impl Crud<UserForm> for User_ {
@ -74,6 +76,7 @@ pub struct Claims {
pub username: String,
pub iss: String,
pub show_nsfw: bool,
pub theme: String,
}
impl Claims {
@ -94,6 +97,7 @@ impl User_ {
username: self.name.to_owned(),
iss: self.fedi_name.to_owned(),
show_nsfw: self.show_nsfw,
theme: self.theme.to_owned(),
};
encode(
&Header::default(),
@ -141,6 +145,7 @@ mod tests {
banned: false,
updated: None,
show_nsfw: false,
theme: "darkly".into(),
};
let inserted_user = User_::create(&conn, &new_user).unwrap();
@ -158,6 +163,7 @@ mod tests {
published: inserted_user.published,
updated: None,
show_nsfw: false,
theme: "darkly".into(),
};
let read_user = User_::read(&conn, inserted_user.id).unwrap();

View file

@ -254,6 +254,7 @@ table! {
published -> Timestamp,
updated -> Nullable<Timestamp>,
show_nsfw -> Bool,
theme -> Varchar,
}
}

View file

@ -1,7 +1,3 @@
body, .text-white, .navbar-brand, .badge-light, .btn-secondary {
color: #dedede !important;
}
.navbar-toggler {
border: 0px;
}
@ -31,29 +27,6 @@ body, .text-white, .navbar-brand, .badge-light, .btn-secondary {
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 {
margin-bottom: 0px;
}

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

File diff suppressed because one or more lines are too long

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

File diff suppressed because one or more lines are too long

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

File diff suppressed because one or more lines are too long

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

File diff suppressed because one or more lines are too long

1
ui/package.json vendored
View file

@ -21,6 +21,7 @@
"@types/markdown-it": "^0.0.7",
"@types/markdown-it-container": "^2.0.2",
"autosize": "^4.0.2",
"bootswatch": "^4.3.1",
"classcat": "^1.1.3",
"dotenv": "^6.1.0",
"emoji-short-name": "^0.1.0",

View file

@ -13,7 +13,7 @@ export class Footer extends Component<any, any> {
render() {
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">
<ul class="navbar-nav ml-auto">
<li class="nav-item">

View file

@ -96,8 +96,8 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
<div className="post-title">
<h5 className="mb-0 d-inline">
{post.url ?
<a className="text-white" 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>
<a className="text-body" href={post.url} target="_blank" title={post.url}>{post.name}</a> :
<Link className="text-body" to={`/post/${post.id}`} title={i18n.t('comments')}>{post.name}</Link>
}
</h5>
{post.url &&

View file

@ -74,7 +74,7 @@ export class Post extends Component<any, PostState> {
if (this.state.scrolled_comment_id && !this.state.scrolled && lastState.comments.length > 0) {
var elmnt = document.getElementById(`comment-${this.state.scrolled_comment_id}`);
elmnt.scrollIntoView();
elmnt.classList.add("mark-two");
elmnt.classList.add("mark");
this.state.scrolled = true;
this.markScrolledAsRead(this.state.scrolled_comment_id);
}

View file

@ -4,7 +4,7 @@ import { Subscription } from "rxjs";
import { retryWhen, delay, take } from 'rxjs/operators';
import { UserOperation, Post, Comment, CommunityUser, GetUserDetailsForm, SortType, UserDetailsResponse, UserView, CommentResponse, UserSettingsForm, LoginResponse, BanUserResponse, AddAdminResponse } from '../interfaces';
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 { CommentNodes } from './comment-nodes';
import { MomentTime } from './moment-time';
@ -61,6 +61,7 @@ export class User extends Component<any, UserState> {
page: this.getPageFromProps(this.props),
userSettingsForm: {
show_nsfw: null,
theme: null,
auth: null,
},
userSettingsLoading: null,
@ -285,7 +286,18 @@ export class User extends Component<any, UserState> {
<div class="card-body">
<h5><T i18nKey="settings">#</T></h5>
<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="form-check">
<input class="form-check-input" type="checkbox" checked={this.state.userSettingsForm.show_nsfw} onChange={linkEvent(this, this.handleUserSettingsShowNsfwChange)}/>
@ -309,10 +321,9 @@ export class User extends Component<any, UserState> {
moderates() {
return (
<div>
{this.state.moderates.length > 0 &&
<div class="card border-secondary mb-3">
<div class="card-body">
{this.state.moderates.length > 0 &&
<div>
<h5><T i18nKey="moderates">#</T></h5>
<ul class="list-unstyled mb-0">
{this.state.moderates.map(community =>
@ -320,10 +331,9 @@ export class User extends Component<any, UserState> {
)}
</ul>
</div>
</div>
}
</div>
</div>
</div>
)
}
@ -410,6 +420,12 @@ export class User extends Component<any, UserState> {
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) {
event.preventDefault();
i.state.userSettingsLoading = true;
@ -435,6 +451,7 @@ export class User extends Component<any, UserState> {
this.state.loading = false;
if (this.isCurrentUser) {
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}`;
window.scrollTo(0,0);

File diff suppressed because one or more lines are too long

16
ui/src/index.html vendored
View file

@ -6,8 +6,24 @@
<meta name="Description" content="Lemmy">
<meta charset="utf-8">
<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="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>
</head>

4
ui/src/index.tsx vendored
View file

@ -19,10 +19,6 @@ import { Sponsors } from './components/sponsors';
import { Symbols } from './components/symbols';
import { i18n } from './i18next';
import './css/tribute.css';
import './css/bootstrap.min.css';
import './css/main.css';
import { WebSocketService, UserService } from './services';
const container = document.getElementById('app');

View file

@ -23,6 +23,7 @@ export interface User {
iss: string;
username: string;
show_nsfw: boolean;
theme: string;
}
export interface UserView {
@ -381,6 +382,7 @@ export interface LoginResponse {
export interface UserSettingsForm {
show_nsfw: boolean;
theme: string;
auth: string;
}

View file

@ -1,5 +1,6 @@
import * as Cookies from 'js-cookie';
import { User, LoginResponse } from '../interfaces';
import { setTheme } from '../utils';
import * as jwt_decode from 'jwt-decode';
import { Subject } from 'rxjs';
@ -14,6 +15,7 @@ export class UserService {
if (jwt) {
this.setUser(jwt);
} else {
setTheme();
console.log('No JWT cookie found.');
}
}
@ -27,8 +29,9 @@ export class UserService {
public logout() {
this.user = undefined;
Cookies.remove("jwt");
console.log("Logged out.");
setTheme();
this.sub.next({user: undefined, unreadCount: 0});
console.log("Logged out.");
}
public get auth(): string {
@ -37,6 +40,7 @@ export class UserService {
private setUser(jwt: string) {
this.user = jwt_decode(jwt);
setTheme(this.user.theme);
this.sub.next({user: this.user, unreadCount: 0});
console.log(this.user);
}

View file

@ -129,6 +129,7 @@ export const en = {
modified: 'modified',
nsfw: 'NSFW',
show_nsfw: 'Show NSFW content',
theme: 'Theme',
sponsors: 'Sponsors',
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:',

14
ui/src/utils.ts vendored
View file

@ -238,3 +238,17 @@ export function getMomentLanguage(): string {
}
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
View file

@ -381,6 +381,11 @@ body@^5.1.0:
raw-body "~1.1.0"
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:
version "2.6.1"
resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.6.1.tgz#196599588af6f0413449c79ab3bf7a5a1bb3384f"