summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFelix Ableitner <me@nutomic.com>2020-03-18 22:51:34 +0100
committerFelix Ableitner <me@nutomic.com>2020-03-18 22:51:34 +0100
commit33cce053003f04a0c4e3cacae3d8caeb93f96a4a (patch)
treeacdf220ec3e1ad5037a8a14e77afd85b685a9d47
parent390b204272e06a1b880ea63ae4f12183fb8dc555 (diff)
parented0dd3b8c00d51989e8f652a3fa904c7b1eb7ec7 (diff)
Merge branch 'master' into federation
-rw-r--r--.gitignore2
-rw-r--r--ansible/templates/docker-compose.yml4
-rw-r--r--docker/dev/docker-compose.yml2
-rw-r--r--docker/prod/docker-compose.yml2
-rw-r--r--docs/src/SUMMARY.md1
-rw-r--r--docs/src/contributing_theming.md18
-rwxr-xr-xinstall.sh4
-rw-r--r--server/src/api/comment.rs11
-rw-r--r--server/src/api/user.rs3
-rw-r--r--server/src/lib.rs5
-rw-r--r--server/src/routes/websocket.rs13
-rw-r--r--server/src/websocket/server.rs22
-rw-r--r--ui/src/components/comment-node.tsx22
-rw-r--r--ui/src/components/post-listing.tsx26
-rw-r--r--ui/src/utils.ts76
-rw-r--r--ui/translations/en.json4
-rw-r--r--ui/yarn.lock6
17 files changed, 143 insertions, 78 deletions
diff --git a/.gitignore b/.gitignore
index 919358cf..5e221b03 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,4 +14,4 @@ build/
ui/src/translations
# ide config
-.idea/ \ No newline at end of file
+.idea/
diff --git a/ansible/templates/docker-compose.yml b/ansible/templates/docker-compose.yml
index 57ee142a..ffd5b9c2 100644
--- a/ansible/templates/docker-compose.yml
+++ b/ansible/templates/docker-compose.yml
@@ -6,6 +6,8 @@ services:
ports:
- "127.0.0.1:8536:8536"
restart: always
+ environment:
+ - RUST_LOG=debug
volumes:
- ./lemmy.hjson:/config/config.hjson:ro
depends_on:
@@ -43,4 +45,4 @@ services:
image: mwader/postfix-relay
environment:
- POSTFIX_myhostname={{ domain }}
- restart: "always" \ No newline at end of file
+ restart: "always"
diff --git a/docker/dev/docker-compose.yml b/docker/dev/docker-compose.yml
index 64ba1db4..a7d289b2 100644
--- a/docker/dev/docker-compose.yml
+++ b/docker/dev/docker-compose.yml
@@ -18,6 +18,8 @@ services:
ports:
- "127.0.0.1:8536:8536"
restart: always
+ environment:
+ - RUST_LOG=debug
volumes:
- ../lemmy.hjson:/config/config.hjson:ro
depends_on:
diff --git a/docker/prod/docker-compose.yml b/docker/prod/docker-compose.yml
index 70c6bfe7..9f5e8925 100644
--- a/docker/prod/docker-compose.yml
+++ b/docker/prod/docker-compose.yml
@@ -16,6 +16,8 @@ services:
ports:
- "127.0.0.1:8536:8536"
restart: always
+ environment:
+ - RUST_LOG=debug
volumes:
- ./lemmy.hjson:/config/config.hjson:ro
depends_on:
diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md
index 9a2ecde8..10a6153e 100644
--- a/docs/src/SUMMARY.md
+++ b/docs/src/SUMMARY.md
@@ -15,4 +15,5 @@
- [Local Development](contributing_local_development.md)
- [Websocket/HTTP API](contributing_websocket_http_api.md)
- [ActivityPub API Outline](contributing_apub_api_outline.md)
+ - [Theming Guide](contributing_theming.md)
- [Lemmy Council](lemmy_council.md)
diff --git a/docs/src/contributing_theming.md b/docs/src/contributing_theming.md
new file mode 100644
index 00000000..25c8ca6d
--- /dev/null
+++ b/docs/src/contributing_theming.md
@@ -0,0 +1,18 @@
+# Theming Guide
+
+Lemmy uses [Bootstrap v4](https://getbootstrap.com/), and very few custom css classes, so any bootstrap v4 compatible theme should work fine.
+
+## Creating
+
+- Use a tool like [bootstrap.build](https://bootstrap.build/) to create a bootstrap v4 theme. Export the `bootstrap.min.css` once you're done, and save the `_variables.scss` too.
+
+## Testing
+
+- To test out a theme, you can either use your browser's web tools, or a plugin like stylus to copy-paste a theme, when viewing Lemmy.
+
+## Adding
+
+1. Copy `{my-theme-name}.min.css` to `ui/assets/css/themes`. (You can also copy the `_variables.scss` here if you want).
+1. Go to `ui/src/utils.ts` and add `{my-theme-name}` to the themes list.
+1. Test locally
+1. Do a pull request with those changes.
diff --git a/install.sh b/install.sh
index b368891c..ad3e4ab3 100755
--- a/install.sh
+++ b/install.sh
@@ -16,7 +16,7 @@ init_db_final=0
while [ "$init_db_valid" == 0 ]
do
read -p "Initialize database (y/n)? " init_db
- case "${init_db,,}" in
+ case "${init_db,,}" in
y|yes ) init_db_valid=1; init_db_final=1;;
n|no ) init_db_valid=1; init_db_final=0;;
* ) echo "Invalid input" 1>&2;;
@@ -37,7 +37,7 @@ yarn build
# Build and run the backend
cd ../server
-cargo run
+RUST_LOG=debug cargo run
# For live coding, where both the front and back end, automagically reload on any save, do:
# cd ui && yarn start
diff --git a/server/src/api/comment.rs b/server/src/api/comment.rs
index 5c614966..8373a338 100644
--- a/server/src/api/comment.rs
+++ b/server/src/api/comment.rs
@@ -2,6 +2,7 @@ use super::*;
use crate::send_email;
use crate::settings::Settings;
use diesel::PgConnection;
+use log::error;
use std::str::FromStr;
#[derive(Serialize, Deserialize)]
@@ -128,7 +129,7 @@ impl Perform<CommentResponse> for Oper<CreateComment> {
// Let the uniqueness handle this fail
match UserMention::create(&conn, &user_mention_form) {
Ok(_mention) => (),
- Err(_e) => eprintln!("{}", &_e),
+ Err(_e) => error!("{}", &_e),
};
// Send an email to those users that have notifications on
@@ -145,7 +146,7 @@ impl Perform<CommentResponse> for Oper<CreateComment> {
);
match send_email(subject, &mention_email, &mention_user.name, html) {
Ok(_o) => _o,
- Err(e) => eprintln!("{}", e),
+ Err(e) => error!("{}", e),
};
}
}
@@ -174,7 +175,7 @@ impl Perform<CommentResponse> for Oper<CreateComment> {
);
match send_email(subject, &comment_reply_email, &parent_user.name, html) {
Ok(_o) => _o,
- Err(e) => eprintln!("{}", e),
+ Err(e) => error!("{}", e),
};
}
}
@@ -199,7 +200,7 @@ impl Perform<CommentResponse> for Oper<CreateComment> {
);
match send_email(subject, &post_reply_email, &parent_user.name, html) {
Ok(_o) => _o,
- Err(e) => eprintln!("{}", e),
+ Err(e) => error!("{}", e),
};
}
}
@@ -318,7 +319,7 @@ impl Perform<CommentResponse> for Oper<EditComment> {
// Let the uniqueness handle this fail
match UserMention::create(&conn, &user_mention_form) {
Ok(_mention) => (),
- Err(_e) => eprintln!("{}", &_e),
+ Err(_e) => error!("{}", &_e),
}
}
}
diff --git a/server/src/api/user.rs b/server/src/api/user.rs
index f7313895..333fd949 100644
--- a/server/src/api/user.rs
+++ b/server/src/api/user.rs
@@ -3,6 +3,7 @@ use crate::settings::Settings;
use crate::{generate_random_string, send_email};
use bcrypt::verify;
use diesel::PgConnection;
+use log::error;
use std::str::FromStr;
#[derive(Serialize, Deserialize, Debug)]
@@ -1008,7 +1009,7 @@ impl Perform<PrivateMessageResponse> for Oper<CreatePrivateMessage> {
);
match send_email(subject, &email, &recipient_user.name, html) {
Ok(_o) => _o,
- Err(e) => eprintln!("{}", e),
+ Err(e) => error!("{}", e),
};
}
}
diff --git a/server/src/lib.rs b/server/src/lib.rs
index bf3c3c0a..2507819d 100644
--- a/server/src/lib.rs
+++ b/server/src/lib.rs
@@ -40,6 +40,7 @@ use lettre::smtp::extension::ClientId;
use lettre::smtp::ConnectionReuseParameters;
use lettre::{ClientSecurity, SmtpClient, Transport};
use lettre_email::Email;
+use log::error;
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
use rand::distributions::Alphanumeric;
use rand::{thread_rng, Rng};
@@ -192,7 +193,7 @@ fn fetch_iframely_and_pictshare_data(
Some(url) => match fetch_iframely(&url) {
Ok(res) => (res.title, res.description, res.thumbnail_url, res.html),
Err(e) => {
- eprintln!("iframely err: {}", e);
+ error!("iframely err: {}", e);
(None, None, None, None)
}
},
@@ -204,7 +205,7 @@ fn fetch_iframely_and_pictshare_data(
Some(iframely_thumbnail_url) => match fetch_pictshare(&iframely_thumbnail_url) {
Ok(res) => Some(res.url),
Err(e) => {
- eprintln!("pictshare err: {}", e);
+ error!("pictshare err: {}", e);
None
}
},
diff --git a/server/src/routes/websocket.rs b/server/src/routes/websocket.rs
index a68b2bcd..6c4326fd 100644
--- a/server/src/routes/websocket.rs
+++ b/server/src/routes/websocket.rs
@@ -3,6 +3,7 @@ use actix::prelude::*;
use actix_web::web;
use actix_web::*;
use actix_web_actors::ws;
+use log::{error, info};
use std::time::{Duration, Instant};
pub fn config(cfg: &mut web::ServiceConfig) {
@@ -99,7 +100,6 @@ impl Handler<WSMessage> for WSSession {
type Result = ();
fn handle(&mut self, msg: WSMessage, ctx: &mut Self::Context) {
- // println!("id: {} msg: {}", self.id, msg.0);
ctx.text(msg.0);
}
}
@@ -107,11 +107,10 @@ impl Handler<WSMessage> for WSSession {
/// WebSocket message handler
impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WSSession {
fn handle(&mut self, result: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {
- // println!("WEBSOCKET MESSAGE: {:?} from id: {}", msg, self.id);
let message = match result {
Ok(m) => m,
Err(e) => {
- println!("{}", e);
+ error!("{}", e);
return;
}
};
@@ -125,7 +124,7 @@ impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WSSession {
}
ws::Message::Text(text) => {
let m = text.trim().to_owned();
- println!("WEBSOCKET MESSAGE: {:?} from id: {}", &m, self.id);
+ info!("Message received: {:?} from id: {}", &m, self.id);
self
.cs_addr
@@ -138,14 +137,14 @@ impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WSSession {
match res {
Ok(res) => ctx.text(res),
Err(e) => {
- eprintln!("{}", &e);
+ error!("{}", &e);
}
}
actix::fut::ready(())
})
.wait(ctx);
}
- ws::Message::Binary(_bin) => println!("Unexpected binary"),
+ ws::Message::Binary(_bin) => info!("Unexpected binary"),
ws::Message::Close(_) => {
ctx.stop();
}
@@ -163,7 +162,7 @@ impl WSSession {
// check client heartbeats
if Instant::now().duration_since(act.hb) > CLIENT_TIMEOUT {
// heartbeat timed out
- println!("Websocket Client heartbeat failed, disconnecting!");
+ error!("Websocket Client heartbeat failed, disconnecting!");
// notify chat server
act.cs_addr.do_send(Disconnect {
diff --git a/server/src/websocket/server.rs b/server/src/websocket/server.rs
index 8a8ccca0..f205c91e 100644
--- a/server/src/websocket/server.rs
+++ b/server/src/websocket/server.rs
@@ -6,6 +6,7 @@ use actix::prelude::*;
use diesel::r2d2::{ConnectionManager, Pool, PooledConnection};
use diesel::PgConnection;
use failure::Error;
+use log::{error, info, warn};
use rand::{rngs::ThreadRng, Rng};
use serde::{Deserialize, Serialize};
use serde_json::Value;
@@ -343,7 +344,7 @@ impl ChatServer {
}
if rate_limit.allowance < 1.0 {
- println!(
+ warn!(
"Rate limited IP: {}, time_passed: {}, allowance: {}",
&info.ip, time_passed, rate_limit.allowance
);
@@ -387,7 +388,7 @@ impl Handler<Connect> for ChatServer {
fn handle(&mut self, msg: Connect, _ctx: &mut Context<Self>) -> Self::Result {
// register session with random id
let id = self.rng.gen::<usize>();
- println!("{} joined", &msg.ip);
+ info!("{} joined", &msg.ip);
self.sessions.insert(
id,
@@ -448,13 +449,16 @@ impl Handler<StandardMessage> for ChatServer {
type Result = MessageResult<StandardMessage>;
fn handle(&mut self, msg: StandardMessage, _: &mut Context<Self>) -> Self::Result {
- let msg_out = match parse_json_message(self, msg) {
- Ok(m) => m,
- Err(e) => e.to_string(),
- };
-
- println!("Message Sent: {}", msg_out);
- MessageResult(msg_out)
+ match parse_json_message(self, msg) {
+ Ok(m) => {
+ info!("Message Sent: {}", m);
+ MessageResult(m)
+ }
+ Err(e) => {
+ error!("Error during message handling {}", e);
+ MessageResult(e.to_string())
+ }
+ }
}
}
diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx
index 524367bc..32e43fdc 100644
--- a/ui/src/components/comment-node.tsx
+++ b/ui/src/components/comment-node.tsx
@@ -198,9 +198,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
<span
className={`unselectable pointer ${this.scoreColor}`}
onClick={linkEvent(node, this.handleCommentUpvote)}
- data-tippy-content={i18n.t('number_of_points', {
- count: this.state.score,
- })}
+ data-tippy-content={this.pointsTippy}
>
<svg class="icon icon-inline mr-1">
<use xlinkHref="#icon-zap"></use>
@@ -916,6 +914,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
WebSocketService.Instance.likeComment(form);
this.setState(this.state);
+ setupTippy();
}
handleCommentDownvote(i: CommentNodeI) {
@@ -943,6 +942,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
WebSocketService.Instance.likeComment(form);
this.setState(this.state);
+ setupTippy();
}
handleModRemoveShow(i: CommentNode) {
@@ -1166,4 +1166,20 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
return 'text-muted';
}
}
+
+ get pointsTippy(): string {
+ let points = i18n.t('number_of_points', {
+ count: this.state.score,
+ });
+
+ let upvotes = i18n.t('number_of_upvotes', {
+ count: this.state.upvotes,
+ });
+
+ let downvotes = i18n.t('number_of_downvotes', {
+ count: this.state.downvotes,
+ });
+
+ return `${points} • ${upvotes} • ${downvotes}`;
+ }
}
diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx
index bef74999..fd34875d 100644
--- a/ui/src/components/post-listing.tsx
+++ b/ui/src/components/post-listing.tsx
@@ -262,9 +262,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
</button>
<div
class={`unselectable pointer font-weight-bold text-muted px-1`}
- data-tippy-content={i18n.t('number_of_points', {
- count: this.state.score,
- })}
+ data-tippy-content={this.pointsTippy}
>
{this.state.score}
</div>
@@ -466,9 +464,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
<>
<span
class="unselectable pointer mr-2"
- data-tippy-content={i18n.t('number_of_points', {
- count: this.state.score,
- })}
+ data-tippy-content={this.pointsTippy}
>
<li className="list-inline-item">
<span className="text-muted">
@@ -1022,6 +1018,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
WebSocketService.Instance.likePost(form);
i.setState(i.state);
+ setupTippy();
}
handlePostDisLike(i: PostListing) {
@@ -1048,6 +1045,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
WebSocketService.Instance.likePost(form);
i.setState(i.state);
+ setupTippy();
}
handleEditClick(i: PostListing) {
@@ -1291,4 +1289,20 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
i.setState(i.state);
setupTippy();
}
+
+ get pointsTippy(): string {
+ let points = i18n.t('number_of_points', {
+ count: this.state.score,
+ });
+
+ let upvotes = i18n.t('number_of_upvotes', {
+ count: this.state.upvotes,
+ });
+
+ let downvotes = i18n.t('number_of_downvotes', {
+ count: this.state.downvotes,
+ });
+
+ return `${points} • ${upvotes} • ${downvotes}`;
+ }
}
diff --git a/ui/src/utils.ts b/ui/src/utils.ts
index 27dbfb50..89fbe51c 100644
--- a/ui/src/utils.ts
+++ b/ui/src/utils.ts
@@ -16,7 +16,7 @@ import 'moment/locale/ja';
import {
UserOperation,
Comment,
- CommentNode,
+ CommentNode as CommentNodeI,
Post,
PrivateMessage,
User,
@@ -53,6 +53,39 @@ export const postRefetchSeconds: number = 60 * 1000;
export const fetchLimit: number = 20;
export const mentionDropdownFetchLimit = 10;
+export const languages = [
+ { code: 'ca', name: 'Català' },
+ { code: 'en', name: 'English' },
+ { code: 'eo', name: 'Esperanto' },
+ { code: 'es', name: 'Español' },
+ { code: 'de', name: 'Deutsch' },
+ { code: 'fa', name: 'فارسی' },
+ { code: 'ja', name: '日本語' },
+ { code: 'pt_BR', name: 'Português Brasileiro' },
+ { code: 'zh', name: '中文' },
+ { code: 'fi', name: 'Suomi' },
+ { code: 'fr', name: 'Français' },
+ { code: 'sv', name: 'Svenska' },
+ { code: 'ru', name: 'Русский' },
+ { code: 'nl', name: 'Nederlands' },
+ { code: 'it', name: 'Italiano' },
+];
+
+export const themes = [
+ 'litera',
+ 'materia',
+ 'minty',
+ 'solar',
+ 'united',
+ 'cyborg',
+ 'darkly',
+ 'journal',
+ 'sketchy',
+ 'vaporwave',
+ 'vaporwave-dark',
+ 'i386',
+];
+
export function randomStr() {
return Math.random()
.toString(36)
@@ -275,24 +308,6 @@ export function debounce(
};
}
-export const languages = [
- { code: 'ca', name: 'Català' },
- { code: 'en', name: 'English' },
- { code: 'eo', name: 'Esperanto' },
- { code: 'es', name: 'Español' },
- { code: 'de', name: 'Deutsch' },
- { code: 'fa', name: 'فارسی' },
- { code: 'ja', name: '日本語' },
- { code: 'pt_BR', name: 'Português Brasileiro' },
- { code: 'zh', name: '中文' },
- { code: 'fi', name: 'Suomi' },
- { code: 'fr', name: 'Français' },
- { code: 'sv', name: 'Svenska' },
- { code: 'ru', name: 'Русский' },
- { code: 'nl', name: 'Nederlands' },
- { code: 'it', name: 'Italiano' },
-];
-
export function getLanguage(): string {
let user = UserService.Instance.user;
let lang = user && user.lang ? user.lang : 'browser';
@@ -344,21 +359,6 @@ export function getMomentLanguage(): string {
return lang;
}
-export const themes = [
- 'litera',
- 'materia',
- 'minty',
- 'solar',
- 'united',
- 'cyborg',
- 'darkly',
- 'journal',
- 'sketchy',
- 'vaporwave',
- 'vaporwave-dark',
- 'i386',
-];
-
export function setTheme(theme: string = 'darkly') {
// unload all the other themes
for (var i = 0; i < themes.length; i++) {
@@ -668,15 +668,15 @@ export function editPostRes(data: PostResponse, post: Post) {
export function commentsToFlatNodes(
comments: Array<Comment>
-): Array<CommentNode> {
- let nodes: Array<CommentNode> = [];
+): Array<CommentNodeI> {
+ let nodes: Array<CommentNodeI> = [];
for (let comment of comments) {
nodes.push({ comment: comment });
}
return nodes;
}
-export function commentSort(tree: Array<CommentNode>, sort: CommentSortType) {
+export function commentSort(tree: Array<CommentNodeI>, sort: CommentSortType) {
// First, put removed and deleted comments at the bottom, then do your other sorts
if (sort == CommentSortType.Top) {
tree.sort(
@@ -716,7 +716,7 @@ export function commentSort(tree: Array<CommentNode>, sort: CommentSortType) {
}
}
-export function commentSortSortType(tree: Array<CommentNode>, sort: SortType) {
+export function commentSortSortType(tree: Array<CommentNodeI>, sort: SortType) {
commentSort(tree, convertCommentSortType(sort));
}
diff --git a/ui/translations/en.json b/ui/translations/en.json
index afee21bb..e9df20ba 100644
--- a/ui/translations/en.json
+++ b/ui/translations/en.json
@@ -155,7 +155,11 @@
"downvotes_disabled": "Downvotes disabled",
"enable_downvotes": "Enable Downvotes",
"upvote": "Upvote",
+ "number_of_upvotes": "{{count}} Upvote",
+ "number_of_upvotes_plural": "{{count}} Upvotes",
"downvote": "Downvote",
+ "number_of_downvotes": "{{count}} Downvote",
+ "number_of_downvotes_plural": "{{count}} Downvotes",
"open_registration": "Open Registration",
"registration_closed": "Registration closed",
"enable_nsfw": "Enable NSFW",
diff --git a/ui/yarn.lock b/ui/yarn.lock
index d36bef04..0d382f8f 100644
--- a/ui/yarn.lock
+++ b/ui/yarn.lock
@@ -258,9 +258,9 @@ acorn-jsx@^5.1.0:
integrity sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw==
acorn@^5.0.3, acorn@^5.7.3:
- version "5.7.3"
- resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279"
- integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==
+ version "5.7.4"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e"
+ integrity sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==
acorn@^7.1.0:
version "7.1.0"