mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-11-23 12:51:18 +00:00
Merge branch 'dev' into moderation
This commit is contained in:
commit
1bf0dfde20
22 changed files with 251 additions and 86 deletions
37
Dockerfile
37
Dockerfile
|
@ -1,12 +1,39 @@
|
||||||
FROM node:10-jessie as node
|
FROM node:10-jessie as node
|
||||||
#If encounter Invalid cross-device error -run on host 'echo N | sudo tee /sys/module/overlay/parameters/metacopy'
|
#If encounter Invalid cross-device error -run on host 'echo N | sudo tee /sys/module/overlay/parameters/metacopy'
|
||||||
COPY ui /app/ui
|
COPY ui /app/ui
|
||||||
RUN cd /app/ui && yarn && yarn build
|
WORKDIR /app/ui
|
||||||
|
RUN yarn
|
||||||
|
RUN yarn build
|
||||||
|
|
||||||
FROM rust:1.33 as rust
|
FROM rust:1.33 as rust
|
||||||
COPY server /app/server
|
|
||||||
|
# create a new empty shell project
|
||||||
|
WORKDIR /app
|
||||||
|
RUN USER=root cargo new server
|
||||||
|
WORKDIR /app/server
|
||||||
|
|
||||||
|
# copy over your manifests
|
||||||
|
COPY server/Cargo.toml server/Cargo.lock ./
|
||||||
|
|
||||||
|
# this build step will cache your dependencies
|
||||||
|
RUN mkdir -p ./src/bin \
|
||||||
|
&& echo 'fn main() { println!("Dummy") }' > ./src/bin/main.rs
|
||||||
|
RUN cargo build --release --bin lemmy
|
||||||
|
RUN ls ./target/release/.fingerprint/
|
||||||
|
RUN rm -r ./target/release/.fingerprint/server-*
|
||||||
|
|
||||||
|
# copy your source tree
|
||||||
|
# RUN rm -rf ./src/
|
||||||
|
COPY server/src ./src/
|
||||||
|
COPY server/migrations ./migrations/
|
||||||
|
|
||||||
|
# build for release
|
||||||
|
RUN cargo build --frozen --release --bin lemmy
|
||||||
|
RUN mv /app/server/target/release/lemmy /app/lemmy
|
||||||
|
|
||||||
|
# The output image
|
||||||
|
# FROM debian:stable-slim
|
||||||
|
# RUN apt-get -y update && apt-get install -y postgresql-client
|
||||||
|
# COPY --from=rust /app/server/target/release/lemmy /app/lemmy
|
||||||
COPY --from=node /app/ui/dist /app/dist
|
COPY --from=node /app/ui/dist /app/dist
|
||||||
RUN cd /app/server && cargo build --release
|
|
||||||
RUN mv /app/server/target/release/lemmy /app/
|
|
||||||
WORKDIR /app/
|
|
||||||
EXPOSE 8536
|
EXPOSE 8536
|
||||||
|
|
|
@ -10,9 +10,9 @@ services:
|
||||||
POSTGRES_DB: rrr
|
POSTGRES_DB: rrr
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "pg_isready -U rrr"]
|
test: ["CMD-SHELL", "pg_isready -U rrr"]
|
||||||
interval: 30s
|
interval: 5s
|
||||||
timeout: 30s
|
timeout: 5s
|
||||||
retries: 3
|
retries: 20
|
||||||
lemmy:
|
lemmy:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
|
@ -22,6 +22,7 @@ services:
|
||||||
environment:
|
environment:
|
||||||
LEMMY_FRONT_END_DIR: /app/dist
|
LEMMY_FRONT_END_DIR: /app/dist
|
||||||
DATABASE_URL: postgres://rrr:rrr@db:5432/rrr
|
DATABASE_URL: postgres://rrr:rrr@db:5432/rrr
|
||||||
|
restart: always
|
||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|
1
server/Cargo.lock
generated
1
server/Cargo.lock
generated
|
@ -1359,6 +1359,7 @@ dependencies = [
|
||||||
"env_logger 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"env_logger 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"jsonwebtoken 5.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jsonwebtoken 5.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
|
@ -24,4 +24,5 @@ rand = "0.6.5"
|
||||||
strum = "0.14.0"
|
strum = "0.14.0"
|
||||||
strum_macros = "0.14.0"
|
strum_macros = "0.14.0"
|
||||||
jsonwebtoken = "*"
|
jsonwebtoken = "*"
|
||||||
regex = "1"
|
regex = "*"
|
||||||
|
lazy_static = "*"
|
||||||
|
|
|
@ -2,6 +2,7 @@ extern crate diesel;
|
||||||
use diesel::*;
|
use diesel::*;
|
||||||
use diesel::result::Error;
|
use diesel::result::Error;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use {SortType};
|
||||||
|
|
||||||
table! {
|
table! {
|
||||||
community_view (id) {
|
community_view (id) {
|
||||||
|
@ -83,17 +84,27 @@ impl CommunityView {
|
||||||
query.first::<Self>(conn)
|
query.first::<Self>(conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list_all(conn: &PgConnection, from_user_id: Option<i32>) -> Result<Vec<Self>, Error> {
|
pub fn list(conn: &PgConnection, from_user_id: Option<i32>, sort: SortType, limit: Option<i64>) -> Result<Vec<Self>, Error> {
|
||||||
use actions::community_view::community_view::dsl::*;
|
use actions::community_view::community_view::dsl::*;
|
||||||
let mut query = community_view.into_boxed();
|
let mut query = community_view.into_boxed();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// The view lets you pass a null user_id, if you're not logged in
|
// The view lets you pass a null user_id, if you're not logged in
|
||||||
if let Some(from_user_id) = from_user_id {
|
|
||||||
query = query.filter(user_id.eq(from_user_id))
|
match sort {
|
||||||
.order_by((subscribed.desc(), number_of_subscribers.desc()));
|
SortType::New => query = query.order_by(published.desc()).filter(user_id.is_null()),
|
||||||
} else {
|
SortType::TopAll => {
|
||||||
query = query.filter(user_id.is_null())
|
match from_user_id {
|
||||||
.order_by(number_of_subscribers.desc());
|
Some(from_user_id) => query = query.filter(user_id.eq(from_user_id)).order_by((subscribed.desc(), number_of_subscribers.desc())),
|
||||||
|
None => query = query.order_by(number_of_subscribers.desc()).filter(user_id.is_null())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => ()
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(limit) = limit {
|
||||||
|
query = query.limit(limit);
|
||||||
};
|
};
|
||||||
|
|
||||||
query.load::<Self>(conn)
|
query.load::<Self>(conn)
|
||||||
|
|
|
@ -12,7 +12,7 @@ pub extern crate jsonwebtoken;
|
||||||
pub extern crate bcrypt;
|
pub extern crate bcrypt;
|
||||||
pub extern crate regex;
|
pub extern crate regex;
|
||||||
#[macro_use] pub extern crate strum_macros;
|
#[macro_use] pub extern crate strum_macros;
|
||||||
|
#[macro_use] pub extern crate lazy_static;
|
||||||
pub mod schema;
|
pub mod schema;
|
||||||
pub mod apub;
|
pub mod apub;
|
||||||
pub mod actions;
|
pub mod actions;
|
||||||
|
@ -89,21 +89,42 @@ pub fn naive_now() -> NaiveDateTime {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_email_regex(test: &str) -> bool {
|
pub fn is_email_regex(test: &str) -> bool {
|
||||||
let re = Regex::new(r"^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$").unwrap();
|
EMAIL_REGEX.is_match(test)
|
||||||
re.is_match(test)
|
}
|
||||||
|
|
||||||
|
pub fn remove_slurs(test: &str) -> String {
|
||||||
|
SLUR_REGEX.replace_all(test, "*removed*").to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_slurs(test: &str) -> bool {
|
||||||
|
SLUR_REGEX.is_match(test)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use {Settings, is_email_regex};
|
use {Settings, is_email_regex, remove_slurs, has_slurs};
|
||||||
#[test]
|
#[test]
|
||||||
fn test_api() {
|
fn test_api() {
|
||||||
assert_eq!(Settings::get().api_endpoint(), "http://0.0.0.0/api/v1");
|
assert_eq!(Settings::get().api_endpoint(), "http://0.0.0.0/api/v1");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test] fn test_email() {
|
||||||
fn test_email() {
|
|
||||||
assert!(is_email_regex("gush@gmail.com"));
|
assert!(is_email_regex("gush@gmail.com"));
|
||||||
assert!(!is_email_regex("nada_neutho"));
|
assert!(!is_email_regex("nada_neutho"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test] fn test_slur_filter() {
|
||||||
|
let test = "coons test dindu ladyboy tranny. This is a bunch of other safe text.".to_string();
|
||||||
|
let slur_free = "No slurs here";
|
||||||
|
assert_eq!(remove_slurs(&test), "*removed* test *removed* *removed* *removed*. This is a bunch of other safe text.".to_string());
|
||||||
|
assert!(has_slurs(&test));
|
||||||
|
assert!(!has_slurs(slur_free));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref EMAIL_REGEX: Regex = Regex::new(r"^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$").unwrap();
|
||||||
|
static ref SLUR_REGEX: Regex = Regex::new(r"(fag(g|got|tard)?|maricos?|cock\s?sucker(s|ing)?|\bnig(\b|g?(a|er)?s?)\b|dindu(s?)|mudslime?s?|kikes?|mongoloids?|towel\s*heads?|\bspi(c|k)s?\b|\bchinks?|niglets?|beaners?|\bnips?\b|\bcoons?\b|jungle\s*bunn(y|ies?)|jigg?aboo?s?|\bpakis?\b|rag\s*heads?|gooks?|cunts?|bitch(es|ing|y)?|puss(y|ies?)|twats?|feminazis?|whor(es?|ing)|\bslut(s|t?y)?|\btrann?(y|ies?)|ladyboy(s?))").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ use serde_json::{Value};
|
||||||
use bcrypt::{verify};
|
use bcrypt::{verify};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use {Crud, Joinable, Likeable, Followable, establish_connection, naive_now, SortType};
|
use {Crud, Joinable, Likeable, Followable, establish_connection, naive_now, SortType, has_slurs, remove_slurs};
|
||||||
use actions::community::*;
|
use actions::community::*;
|
||||||
use actions::user::*;
|
use actions::user::*;
|
||||||
use actions::post::*;
|
use actions::post::*;
|
||||||
|
@ -111,6 +111,8 @@ pub struct CommunityResponse {
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct ListCommunities {
|
pub struct ListCommunities {
|
||||||
|
sort: String,
|
||||||
|
limit: Option<i64>,
|
||||||
auth: Option<String>
|
auth: Option<String>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -391,25 +393,25 @@ impl Handler<StandardMessage> for ChatServer {
|
||||||
let json: Value = serde_json::from_str(&msg.msg)
|
let json: Value = serde_json::from_str(&msg.msg)
|
||||||
.expect("Couldn't parse message");
|
.expect("Couldn't parse message");
|
||||||
|
|
||||||
let data: &Value = &json["data"];
|
let data = &json["data"].to_string();
|
||||||
let op = &json["op"].as_str().unwrap();
|
let op = &json["op"].as_str().unwrap();
|
||||||
let user_operation: UserOperation = UserOperation::from_str(&op).unwrap();
|
let user_operation: UserOperation = UserOperation::from_str(&op).unwrap();
|
||||||
|
|
||||||
let res: String = match user_operation {
|
let res: String = match user_operation {
|
||||||
UserOperation::Login => {
|
UserOperation::Login => {
|
||||||
let login: Login = serde_json::from_str(&data.to_string()).unwrap();
|
let login: Login = serde_json::from_str(data).unwrap();
|
||||||
login.perform(self, msg.id)
|
login.perform(self, msg.id)
|
||||||
},
|
},
|
||||||
UserOperation::Register => {
|
UserOperation::Register => {
|
||||||
let register: Register = serde_json::from_str(&data.to_string()).unwrap();
|
let register: Register = serde_json::from_str(data).unwrap();
|
||||||
register.perform(self, msg.id)
|
register.perform(self, msg.id)
|
||||||
},
|
},
|
||||||
UserOperation::CreateCommunity => {
|
UserOperation::CreateCommunity => {
|
||||||
let create_community: CreateCommunity = serde_json::from_str(&data.to_string()).unwrap();
|
let create_community: CreateCommunity = serde_json::from_str(data).unwrap();
|
||||||
create_community.perform(self, msg.id)
|
create_community.perform(self, msg.id)
|
||||||
},
|
},
|
||||||
UserOperation::ListCommunities => {
|
UserOperation::ListCommunities => {
|
||||||
let list_communities: ListCommunities = serde_json::from_str(&data.to_string()).unwrap();
|
let list_communities: ListCommunities = serde_json::from_str(data).unwrap();
|
||||||
list_communities.perform(self, msg.id)
|
list_communities.perform(self, msg.id)
|
||||||
},
|
},
|
||||||
UserOperation::ListCategories => {
|
UserOperation::ListCategories => {
|
||||||
|
@ -417,55 +419,55 @@ impl Handler<StandardMessage> for ChatServer {
|
||||||
list_categories.perform(self, msg.id)
|
list_categories.perform(self, msg.id)
|
||||||
},
|
},
|
||||||
UserOperation::CreatePost => {
|
UserOperation::CreatePost => {
|
||||||
let create_post: CreatePost = serde_json::from_str(&data.to_string()).unwrap();
|
let create_post: CreatePost = serde_json::from_str(data).unwrap();
|
||||||
create_post.perform(self, msg.id)
|
create_post.perform(self, msg.id)
|
||||||
},
|
},
|
||||||
UserOperation::GetPost => {
|
UserOperation::GetPost => {
|
||||||
let get_post: GetPost = serde_json::from_str(&data.to_string()).unwrap();
|
let get_post: GetPost = serde_json::from_str(data).unwrap();
|
||||||
get_post.perform(self, msg.id)
|
get_post.perform(self, msg.id)
|
||||||
},
|
},
|
||||||
UserOperation::GetCommunity => {
|
UserOperation::GetCommunity => {
|
||||||
let get_community: GetCommunity = serde_json::from_str(&data.to_string()).unwrap();
|
let get_community: GetCommunity = serde_json::from_str(data).unwrap();
|
||||||
get_community.perform(self, msg.id)
|
get_community.perform(self, msg.id)
|
||||||
},
|
},
|
||||||
UserOperation::CreateComment => {
|
UserOperation::CreateComment => {
|
||||||
let create_comment: CreateComment = serde_json::from_str(&data.to_string()).unwrap();
|
let create_comment: CreateComment = serde_json::from_str(data).unwrap();
|
||||||
create_comment.perform(self, msg.id)
|
create_comment.perform(self, msg.id)
|
||||||
},
|
},
|
||||||
UserOperation::EditComment => {
|
UserOperation::EditComment => {
|
||||||
let edit_comment: EditComment = serde_json::from_str(&data.to_string()).unwrap();
|
let edit_comment: EditComment = serde_json::from_str(data).unwrap();
|
||||||
edit_comment.perform(self, msg.id)
|
edit_comment.perform(self, msg.id)
|
||||||
},
|
},
|
||||||
UserOperation::CreateCommentLike => {
|
UserOperation::CreateCommentLike => {
|
||||||
let create_comment_like: CreateCommentLike = serde_json::from_str(&data.to_string()).unwrap();
|
let create_comment_like: CreateCommentLike = serde_json::from_str(data).unwrap();
|
||||||
create_comment_like.perform(self, msg.id)
|
create_comment_like.perform(self, msg.id)
|
||||||
},
|
},
|
||||||
UserOperation::GetPosts => {
|
UserOperation::GetPosts => {
|
||||||
let get_posts: GetPosts = serde_json::from_str(&data.to_string()).unwrap();
|
let get_posts: GetPosts = serde_json::from_str(data).unwrap();
|
||||||
get_posts.perform(self, msg.id)
|
get_posts.perform(self, msg.id)
|
||||||
},
|
},
|
||||||
UserOperation::CreatePostLike => {
|
UserOperation::CreatePostLike => {
|
||||||
let create_post_like: CreatePostLike = serde_json::from_str(&data.to_string()).unwrap();
|
let create_post_like: CreatePostLike = serde_json::from_str(data).unwrap();
|
||||||
create_post_like.perform(self, msg.id)
|
create_post_like.perform(self, msg.id)
|
||||||
},
|
},
|
||||||
UserOperation::EditPost => {
|
UserOperation::EditPost => {
|
||||||
let edit_post: EditPost = serde_json::from_str(&data.to_string()).unwrap();
|
let edit_post: EditPost = serde_json::from_str(data).unwrap();
|
||||||
edit_post.perform(self, msg.id)
|
edit_post.perform(self, msg.id)
|
||||||
},
|
},
|
||||||
UserOperation::EditCommunity => {
|
UserOperation::EditCommunity => {
|
||||||
let edit_community: EditCommunity = serde_json::from_str(&data.to_string()).unwrap();
|
let edit_community: EditCommunity = serde_json::from_str(data).unwrap();
|
||||||
edit_community.perform(self, msg.id)
|
edit_community.perform(self, msg.id)
|
||||||
},
|
},
|
||||||
UserOperation::FollowCommunity => {
|
UserOperation::FollowCommunity => {
|
||||||
let follow_community: FollowCommunity = serde_json::from_str(&data.to_string()).unwrap();
|
let follow_community: FollowCommunity = serde_json::from_str(data).unwrap();
|
||||||
follow_community.perform(self, msg.id)
|
follow_community.perform(self, msg.id)
|
||||||
},
|
},
|
||||||
UserOperation::GetFollowedCommunities => {
|
UserOperation::GetFollowedCommunities => {
|
||||||
let followed_communities: GetFollowedCommunities = serde_json::from_str(&data.to_string()).unwrap();
|
let followed_communities: GetFollowedCommunities = serde_json::from_str(data).unwrap();
|
||||||
followed_communities.perform(self, msg.id)
|
followed_communities.perform(self, msg.id)
|
||||||
},
|
},
|
||||||
UserOperation::GetUserDetails => {
|
UserOperation::GetUserDetails => {
|
||||||
let get_user_details: GetUserDetails = serde_json::from_str(&data.to_string()).unwrap();
|
let get_user_details: GetUserDetails = serde_json::from_str(data).unwrap();
|
||||||
get_user_details.perform(self, msg.id)
|
get_user_details.perform(self, msg.id)
|
||||||
},
|
},
|
||||||
// _ => {
|
// _ => {
|
||||||
|
@ -541,6 +543,10 @@ impl Perform for Register {
|
||||||
return self.error("Passwords do not match.");
|
return self.error("Passwords do not match.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if has_slurs(&self.username) {
|
||||||
|
return self.error("No slurs");
|
||||||
|
}
|
||||||
|
|
||||||
// Register the new user
|
// Register the new user
|
||||||
let user_form = UserForm {
|
let user_form = UserForm {
|
||||||
name: self.username.to_owned(),
|
name: self.username.to_owned(),
|
||||||
|
@ -587,6 +593,12 @@ impl Perform for CreateCommunity {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if has_slurs(&self.name) ||
|
||||||
|
has_slurs(&self.title) ||
|
||||||
|
(self.description.is_some() && has_slurs(&self.description.to_owned().unwrap())) {
|
||||||
|
return self.error("No slurs");
|
||||||
|
}
|
||||||
|
|
||||||
let user_id = claims.id;
|
let user_id = claims.id;
|
||||||
|
|
||||||
// When you create a community, make sure the user becomes a moderator and a follower
|
// When you create a community, make sure the user becomes a moderator and a follower
|
||||||
|
@ -665,7 +677,9 @@ impl Perform for ListCommunities {
|
||||||
None => None
|
None => None
|
||||||
};
|
};
|
||||||
|
|
||||||
let communities: Vec<CommunityView> = CommunityView::list_all(&conn, user_id).unwrap();
|
let sort = SortType::from_str(&self.sort).expect("listing sort");
|
||||||
|
|
||||||
|
let communities: Vec<CommunityView> = CommunityView::list(&conn, user_id, sort, self.limit).unwrap();
|
||||||
|
|
||||||
// Return the jwt
|
// Return the jwt
|
||||||
serde_json::to_string(
|
serde_json::to_string(
|
||||||
|
@ -716,6 +730,11 @@ impl Perform for CreatePost {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if has_slurs(&self.name) ||
|
||||||
|
(self.body.is_some() && has_slurs(&self.body.to_owned().unwrap())) {
|
||||||
|
return self.error("No slurs");
|
||||||
|
}
|
||||||
|
|
||||||
let user_id = claims.id;
|
let user_id = claims.id;
|
||||||
|
|
||||||
let post_form = PostForm {
|
let post_form = PostForm {
|
||||||
|
@ -894,8 +913,10 @@ impl Perform for CreateComment {
|
||||||
|
|
||||||
let user_id = claims.id;
|
let user_id = claims.id;
|
||||||
|
|
||||||
|
let content_slurs_removed = remove_slurs(&self.content.to_owned());
|
||||||
|
|
||||||
let comment_form = CommentForm {
|
let comment_form = CommentForm {
|
||||||
content: self.content.to_owned(),
|
content: content_slurs_removed,
|
||||||
parent_id: self.parent_id.to_owned(),
|
parent_id: self.parent_id.to_owned(),
|
||||||
post_id: self.post_id,
|
post_id: self.post_id,
|
||||||
creator_id: user_id,
|
creator_id: user_id,
|
||||||
|
@ -976,8 +997,10 @@ impl Perform for EditComment {
|
||||||
return self.error("Incorrect creator.");
|
return self.error("Incorrect creator.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let content_slurs_removed = remove_slurs(&self.content.to_owned());
|
||||||
|
|
||||||
let comment_form = CommentForm {
|
let comment_form = CommentForm {
|
||||||
content: self.content.to_owned(),
|
content: content_slurs_removed,
|
||||||
parent_id: self.parent_id,
|
parent_id: self.parent_id,
|
||||||
post_id: self.post_id,
|
post_id: self.post_id,
|
||||||
creator_id: user_id,
|
creator_id: user_id,
|
||||||
|
@ -1197,6 +1220,11 @@ impl Perform for EditPost {
|
||||||
|
|
||||||
fn perform(&self, chat: &mut ChatServer, addr: usize) -> String {
|
fn perform(&self, chat: &mut ChatServer, addr: usize) -> String {
|
||||||
|
|
||||||
|
if has_slurs(&self.name) ||
|
||||||
|
(self.body.is_some() && has_slurs(&self.body.to_owned().unwrap())) {
|
||||||
|
return self.error("No slurs");
|
||||||
|
}
|
||||||
|
|
||||||
let conn = establish_connection();
|
let conn = establish_connection();
|
||||||
|
|
||||||
let claims = match Claims::decode(&self.auth) {
|
let claims = match Claims::decode(&self.auth) {
|
||||||
|
@ -1264,6 +1292,10 @@ impl Perform for EditCommunity {
|
||||||
|
|
||||||
fn perform(&self, chat: &mut ChatServer, addr: usize) -> String {
|
fn perform(&self, chat: &mut ChatServer, addr: usize) -> String {
|
||||||
|
|
||||||
|
if has_slurs(&self.name) || has_slurs(&self.title) {
|
||||||
|
return self.error("No slurs");
|
||||||
|
}
|
||||||
|
|
||||||
let conn = establish_connection();
|
let conn = establish_connection();
|
||||||
|
|
||||||
let claims = match Claims::decode(&self.auth) {
|
let claims = match Claims::decode(&self.auth) {
|
||||||
|
|
|
@ -11,7 +11,7 @@ const transformInferno = require('ts-transform-inferno').default;
|
||||||
const transformClasscat = require('ts-transform-classcat').default;
|
const transformClasscat = require('ts-transform-classcat').default;
|
||||||
let fuse, app;
|
let fuse, app;
|
||||||
let isProduction = false;
|
let isProduction = false;
|
||||||
var setVersion = require('./set_version.js').setVersion;
|
// var setVersion = require('./set_version.js').setVersion;
|
||||||
|
|
||||||
Sparky.task('config', _ => {
|
Sparky.task('config', _ => {
|
||||||
fuse = new FuseBox({
|
fuse = new FuseBox({
|
||||||
|
@ -42,7 +42,7 @@ Sparky.task('config', _ => {
|
||||||
});
|
});
|
||||||
app = fuse.bundle('app').instructions('>index.tsx');
|
app = fuse.bundle('app').instructions('>index.tsx');
|
||||||
});
|
});
|
||||||
Sparky.task('version', _ => setVersion());
|
// Sparky.task('version', _ => setVersion());
|
||||||
Sparky.task('clean', _ => Sparky.src('dist/').clean('dist/'));
|
Sparky.task('clean', _ => Sparky.src('dist/').clean('dist/'));
|
||||||
Sparky.task('env', _ => (isProduction = true));
|
Sparky.task('env', _ => (isProduction = true));
|
||||||
Sparky.task('copy-assets', () => Sparky.src('assets/*.svg').dest('dist/'));
|
Sparky.task('copy-assets', () => Sparky.src('assets/*.svg').dest('dist/'));
|
||||||
|
|
2
ui/set_version.js
Normal file → Executable file
2
ui/set_version.js
Normal file → Executable file
|
@ -7,3 +7,5 @@ exports.setVersion = function() {
|
||||||
let line = `export let version: string = "${revision}";`;
|
let line = `export let version: string = "${revision}";`;
|
||||||
fs.writeFileSync("./src/version.ts", line);
|
fs.writeFileSync("./src/version.ts", line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.setVersion()
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { Component, linkEvent } from 'inferno';
|
||||||
import { Link } from 'inferno-router';
|
import { Link } from 'inferno-router';
|
||||||
import { Subscription } from "rxjs";
|
import { Subscription } from "rxjs";
|
||||||
import { retryWhen, delay, take } from 'rxjs/operators';
|
import { retryWhen, delay, take } from 'rxjs/operators';
|
||||||
import { UserOperation, Community, ListCommunitiesResponse, CommunityResponse, FollowCommunityForm } from '../interfaces';
|
import { UserOperation, Community, ListCommunitiesResponse, CommunityResponse, FollowCommunityForm, ListCommunitiesForm, SortType } from '../interfaces';
|
||||||
import { WebSocketService } from '../services';
|
import { WebSocketService } from '../services';
|
||||||
import { msgOp } from '../utils';
|
import { msgOp } from '../utils';
|
||||||
|
|
||||||
|
@ -30,7 +30,12 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
(err) => console.error(err),
|
(err) => console.error(err),
|
||||||
() => console.log('complete')
|
() => console.log('complete')
|
||||||
);
|
);
|
||||||
WebSocketService.Instance.listCommunities();
|
|
||||||
|
let listCommunitiesForm: ListCommunitiesForm = {
|
||||||
|
sort: SortType[SortType.TopAll]
|
||||||
|
}
|
||||||
|
|
||||||
|
WebSocketService.Instance.listCommunities(listCommunitiesForm);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +50,7 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div class="container-fluid">
|
<div class="container">
|
||||||
{this.state.loading ?
|
{this.state.loading ?
|
||||||
<h4 class=""><svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg></h4> :
|
<h4 class=""><svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg></h4> :
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -155,6 +155,7 @@ export class CommunityForm extends Component<CommunityFormProps, CommunityFormSt
|
||||||
if (msg.error) {
|
if (msg.error) {
|
||||||
alert(msg.error);
|
alert(msg.error);
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
|
this.setState(this.state);
|
||||||
return;
|
return;
|
||||||
} else if (op == UserOperation.ListCategories){
|
} else if (op == UserOperation.ListCategories){
|
||||||
let res: ListCategoriesResponse = msg;
|
let res: ListCategoriesResponse = msg;
|
||||||
|
|
|
@ -97,13 +97,13 @@ export class Login extends Component<any, State> {
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label class="col-sm-2 col-form-label">Username</label>
|
<label class="col-sm-2 col-form-label">Username</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control" value={this.state.registerForm.username} onInput={linkEvent(this, this.handleRegisterUsernameChange)} required minLength={3} />
|
<input type="text" class="form-control" value={this.state.registerForm.username} onInput={linkEvent(this, this.handleRegisterUsernameChange)} required minLength={3} pattern="[a-zA-Z0-9_]+" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label class="col-sm-2 col-form-label">Email</label>
|
<label class="col-sm-2 col-form-label">Email</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="email" class="form-control" value={this.state.registerForm.email} onInput={linkEvent(this, this.handleRegisterEmailChange)} minLength={3} />
|
<input type="email" class="form-control" placeholder="Optional" value={this.state.registerForm.email} onInput={linkEvent(this, this.handleRegisterEmailChange)} minLength={3} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
|
|
|
@ -2,13 +2,14 @@ import { Component } from 'inferno';
|
||||||
import { Link } from 'inferno-router';
|
import { Link } from 'inferno-router';
|
||||||
import { Subscription } from "rxjs";
|
import { Subscription } from "rxjs";
|
||||||
import { retryWhen, delay, take } from 'rxjs/operators';
|
import { retryWhen, delay, take } from 'rxjs/operators';
|
||||||
import { UserOperation, CommunityUser, GetFollowedCommunitiesResponse } from '../interfaces';
|
import { UserOperation, CommunityUser, GetFollowedCommunitiesResponse, ListCommunitiesForm, ListCommunitiesResponse, Community, SortType } from '../interfaces';
|
||||||
import { WebSocketService, UserService } from '../services';
|
import { WebSocketService, UserService } from '../services';
|
||||||
import { PostListings } from './post-listings';
|
import { PostListings } from './post-listings';
|
||||||
import { msgOp } from '../utils';
|
import { msgOp, repoUrl } from '../utils';
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
subscribedCommunities: Array<CommunityUser>;
|
subscribedCommunities: Array<CommunityUser>;
|
||||||
|
trendingCommunities: Array<Community>;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +18,7 @@ export class Main extends Component<any, State> {
|
||||||
private subscription: Subscription;
|
private subscription: Subscription;
|
||||||
private emptyState: State = {
|
private emptyState: State = {
|
||||||
subscribedCommunities: [],
|
subscribedCommunities: [],
|
||||||
|
trendingCommunities: [],
|
||||||
loading: true
|
loading: true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,6 +38,13 @@ export class Main extends Component<any, State> {
|
||||||
if (UserService.Instance.loggedIn) {
|
if (UserService.Instance.loggedIn) {
|
||||||
WebSocketService.Instance.getFollowedCommunities();
|
WebSocketService.Instance.getFollowedCommunities();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let listCommunitiesForm: ListCommunitiesForm = {
|
||||||
|
sort: SortType[SortType.New],
|
||||||
|
limit: 8
|
||||||
|
}
|
||||||
|
|
||||||
|
WebSocketService.Instance.listCommunities(listCommunitiesForm);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
@ -46,26 +55,26 @@ export class Main extends Component<any, State> {
|
||||||
return (
|
return (
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-md-9">
|
<div class="col-12 col-md-8">
|
||||||
<PostListings />
|
<PostListings />
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-md-3">
|
<div class="col-12 col-md-4">
|
||||||
<h4>A Landing message</h4>
|
{this.state.loading ?
|
||||||
{UserService.Instance.loggedIn &&
|
<h4><svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg></h4> :
|
||||||
|
<div>
|
||||||
|
{this.trendingCommunities()}
|
||||||
|
{UserService.Instance.loggedIn ?
|
||||||
<div>
|
<div>
|
||||||
{this.state.loading ?
|
<h4>Subscribed forums</h4>
|
||||||
<h4 class="mt-3"><svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg></h4> :
|
<ul class="list-inline">
|
||||||
<div>
|
{this.state.subscribedCommunities.map(community =>
|
||||||
<hr />
|
<li class="list-inline-item"><Link to={`/community/${community.community_id}`}>{community.community_name}</Link></li>
|
||||||
<h4>Subscribed forums</h4>
|
)}
|
||||||
<ul class="list-unstyled">
|
</ul>
|
||||||
{this.state.subscribedCommunities.map(community =>
|
</div> :
|
||||||
<li><Link to={`/community/${community.community_id}`}>{community.community_name}</Link></li>
|
this.landing()
|
||||||
)}
|
}
|
||||||
</ul>
|
</div>
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -73,6 +82,34 @@ export class Main extends Component<any, State> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trendingCommunities() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h4>Trending forums</h4>
|
||||||
|
<ul class="list-inline">
|
||||||
|
{this.state.trendingCommunities.map(community =>
|
||||||
|
<li class="list-inline-item"><Link to={`/community/${community.id}`}>{community.name}</Link></li>
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
landing() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h4>Welcome to
|
||||||
|
<svg class="icon mx-2"><use xlinkHref="#icon-mouse"></use></svg>
|
||||||
|
<a href={repoUrl}>Lemmy<sup>Beta</sup></a>
|
||||||
|
</h4>
|
||||||
|
<p>Lemmy is a <a href="https://en.wikipedia.org/wiki/Link_aggregation">link aggregator</a> / reddit alternative, intended to work in the <a href="https://en.wikipedia.org/wiki/Fediverse">fediverse</a>.</p>
|
||||||
|
<p>Its self-hostable, has live-updating comment threads, and is tiny (<code>~80kB</code>). Federation into the ActivityPub network is on the roadmap.</p>
|
||||||
|
<p>This is a <b>very early beta version</b>, and a lot of features are currently broken or missing.</p>
|
||||||
|
<p>Suggest new features or report bugs <a href={repoUrl}>here.</a></p>
|
||||||
|
<p>Made with <a href="https://www.rust-lang.org">Rust</a>, <a href="https://actix.rs/">Actix</a>, <a href="https://www.infernojs.org">Inferno</a>, <a href="https://www.typescriptlang.org/">Typescript</a>.</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
parseMessage(msg: any) {
|
parseMessage(msg: any) {
|
||||||
console.log(msg);
|
console.log(msg);
|
||||||
|
@ -85,6 +122,11 @@ export class Main extends Component<any, State> {
|
||||||
this.state.subscribedCommunities = res.communities;
|
this.state.subscribedCommunities = res.communities;
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
|
} else if (op == UserOperation.ListCommunities) {
|
||||||
|
let res: ListCommunitiesResponse = msg;
|
||||||
|
this.state.trendingCommunities = res.communities;
|
||||||
|
this.state.loading = false;
|
||||||
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ export class Navbar extends Component<any, NavbarState> {
|
||||||
|
|
||||||
// Subscribe to user changes
|
// Subscribe to user changes
|
||||||
UserService.Instance.sub.subscribe(user => {
|
UserService.Instance.sub.subscribe(user => {
|
||||||
let loggedIn: boolean = user !== null;
|
let loggedIn: boolean = user !== undefined;
|
||||||
this.setState({isLoggedIn: loggedIn});
|
this.setState({isLoggedIn: loggedIn});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ export class Navbar extends Component<any, NavbarState> {
|
||||||
// TODO toggle css collapse
|
// TODO toggle css collapse
|
||||||
navbar() {
|
navbar() {
|
||||||
return (
|
return (
|
||||||
<nav class="navbar navbar-expand-sm navbar-light bg-light p-0 px-3 shadow">
|
<nav class="container navbar navbar-expand-md navbar-light navbar-bg p-0 px-3">
|
||||||
<a title={version} class="navbar-brand" href="#">
|
<a title={version} class="navbar-brand" href="#">
|
||||||
<svg class="icon mr-2"><use xlinkHref="#icon-mouse"></use></svg>
|
<svg class="icon mr-2"><use xlinkHref="#icon-mouse"></use></svg>
|
||||||
Lemmy
|
Lemmy
|
||||||
|
@ -74,7 +74,7 @@ export class Navbar extends Component<any, NavbarState> {
|
||||||
<a role="button" class="dropdown-item pointer" onClick={ linkEvent(this, this.handleLogoutClick) }>Logout</a>
|
<a role="button" class="dropdown-item pointer" onClick={ linkEvent(this, this.handleLogoutClick) }>Logout</a>
|
||||||
</div>
|
</div>
|
||||||
</li> :
|
</li> :
|
||||||
<Link class="nav-link" to="/login">Login</Link>
|
<Link class="nav-link" to="/login">Login / Sign up</Link>
|
||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Component, linkEvent } from 'inferno';
|
import { Component, linkEvent } from 'inferno';
|
||||||
import { Subscription } from "rxjs";
|
import { Subscription } from "rxjs";
|
||||||
import { retryWhen, delay, take } from 'rxjs/operators';
|
import { retryWhen, delay, take } from 'rxjs/operators';
|
||||||
import { PostForm as PostFormI, Post, PostResponse, UserOperation, Community, ListCommunitiesResponse } from '../interfaces';
|
import { PostForm as PostFormI, Post, PostResponse, UserOperation, Community, ListCommunitiesResponse, ListCommunitiesForm, SortType } from '../interfaces';
|
||||||
import { WebSocketService } from '../services';
|
import { WebSocketService } from '../services';
|
||||||
import { msgOp } from '../utils';
|
import { msgOp } from '../utils';
|
||||||
import * as autosize from 'autosize';
|
import * as autosize from 'autosize';
|
||||||
|
@ -56,7 +56,11 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
() => console.log('complete')
|
() => console.log('complete')
|
||||||
);
|
);
|
||||||
|
|
||||||
WebSocketService.Instance.listCommunities();
|
let listCommunitiesForm: ListCommunitiesForm = {
|
||||||
|
sort: SortType[SortType.TopAll]
|
||||||
|
}
|
||||||
|
|
||||||
|
WebSocketService.Instance.listCommunities(listCommunitiesForm);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -151,7 +155,9 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
parseMessage(msg: any) {
|
parseMessage(msg: any) {
|
||||||
let op: UserOperation = msgOp(msg);
|
let op: UserOperation = msgOp(msg);
|
||||||
if (msg.error) {
|
if (msg.error) {
|
||||||
|
alert(msg.error);
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
|
this.setState(this.state);
|
||||||
return;
|
return;
|
||||||
} else if (op == UserOperation.ListCommunities) {
|
} else if (op == UserOperation.ListCommunities) {
|
||||||
let res: ListCommunitiesResponse = msg;
|
let res: ListCommunitiesResponse = msg;
|
||||||
|
|
|
@ -101,17 +101,17 @@ export class Post extends Component<any, PostState> {
|
||||||
sortRadios() {
|
sortRadios() {
|
||||||
return (
|
return (
|
||||||
<div class="btn-group btn-group-toggle mb-3">
|
<div class="btn-group btn-group-toggle mb-3">
|
||||||
<label className={`btn btn-sm btn-secondary ${this.state.commentSort === CommentSortType.Hot && 'active'}`}>Hot
|
<label className={`btn btn-sm btn-secondary pointer ${this.state.commentSort === CommentSortType.Hot && 'active'}`}>Hot
|
||||||
<input type="radio" value={CommentSortType.Hot}
|
<input type="radio" value={CommentSortType.Hot}
|
||||||
checked={this.state.commentSort === CommentSortType.Hot}
|
checked={this.state.commentSort === CommentSortType.Hot}
|
||||||
onChange={linkEvent(this, this.handleCommentSortChange)} />
|
onChange={linkEvent(this, this.handleCommentSortChange)} />
|
||||||
</label>
|
</label>
|
||||||
<label className={`btn btn-sm btn-secondary ${this.state.commentSort === CommentSortType.Top && 'active'}`}>Top
|
<label className={`btn btn-sm btn-secondary pointer ${this.state.commentSort === CommentSortType.Top && 'active'}`}>Top
|
||||||
<input type="radio" value={CommentSortType.Top}
|
<input type="radio" value={CommentSortType.Top}
|
||||||
checked={this.state.commentSort === CommentSortType.Top}
|
checked={this.state.commentSort === CommentSortType.Top}
|
||||||
onChange={linkEvent(this, this.handleCommentSortChange)} />
|
onChange={linkEvent(this, this.handleCommentSortChange)} />
|
||||||
</label>
|
</label>
|
||||||
<label className={`btn btn-sm btn-secondary ${this.state.commentSort === CommentSortType.New && 'active'}`}>New
|
<label className={`btn btn-sm btn-secondary pointer ${this.state.commentSort === CommentSortType.New && 'active'}`}>New
|
||||||
<input type="radio" value={CommentSortType.New}
|
<input type="radio" value={CommentSortType.New}
|
||||||
checked={this.state.commentSort === CommentSortType.New}
|
checked={this.state.commentSort === CommentSortType.New}
|
||||||
onChange={linkEvent(this, this.handleCommentSortChange)} />
|
onChange={linkEvent(this, this.handleCommentSortChange)} />
|
||||||
|
|
|
@ -56,7 +56,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
}
|
}
|
||||||
<ul class="list-inline">
|
<ul class="mt-1 list-inline">
|
||||||
<li className="list-inline-item"><Link className="badge badge-light" to="/communities">{community.category_name}</Link></li>
|
<li className="list-inline-item"><Link className="badge badge-light" to="/communities">{community.category_name}</Link></li>
|
||||||
<li className="list-inline-item badge badge-light">{community.number_of_subscribers} Subscribers</li>
|
<li className="list-inline-item badge badge-light">{community.number_of_subscribers} Subscribers</li>
|
||||||
<li className="list-inline-item badge badge-light">{community.number_of_posts} Posts</li>
|
<li className="list-inline-item badge badge-light">{community.number_of_posts} Posts</li>
|
||||||
|
|
|
@ -67,6 +67,12 @@ export interface CommunityResponse {
|
||||||
community: Community;
|
community: Community;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ListCommunitiesForm {
|
||||||
|
sort: string;
|
||||||
|
limit?: number;
|
||||||
|
auth?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ListCommunitiesResponse {
|
export interface ListCommunitiesResponse {
|
||||||
op: string;
|
op: string;
|
||||||
communities: Array<Community>;
|
communities: Array<Community>;
|
||||||
|
|
|
@ -66,3 +66,12 @@ body {
|
||||||
0% { transform: rotate(0deg); }
|
0% { transform: rotate(0deg); }
|
||||||
100% { transform: rotate(359deg); }
|
100% { transform: rotate(359deg); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dropdown-menu {
|
||||||
|
z-index: 2000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-bg {
|
||||||
|
background-color: #222;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,10 +25,10 @@ export class UserService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public logout() {
|
public logout() {
|
||||||
this.user = null;
|
this.user = undefined;
|
||||||
Cookies.remove("jwt");
|
Cookies.remove("jwt");
|
||||||
console.log("Logged out.");
|
console.log("Logged out.");
|
||||||
this.sub.next(null);
|
this.sub.next(undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get loggedIn(): boolean {
|
public get loggedIn(): boolean {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { wsUri } from '../env';
|
import { wsUri } from '../env';
|
||||||
import { LoginForm, RegisterForm, UserOperation, CommunityForm, PostForm, CommentForm, CommentLikeForm, GetPostsForm, CreatePostLikeForm, FollowCommunityForm, GetUserDetailsForm } from '../interfaces';
|
import { LoginForm, RegisterForm, UserOperation, CommunityForm, PostForm, CommentForm, CommentLikeForm, GetPostsForm, CreatePostLikeForm, FollowCommunityForm, GetUserDetailsForm, ListCommunitiesForm } from '../interfaces';
|
||||||
import { webSocket } from 'rxjs/webSocket';
|
import { webSocket } from 'rxjs/webSocket';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
import { retryWhen, delay, take } from 'rxjs/operators';
|
import { retryWhen, delay, take } from 'rxjs/operators';
|
||||||
|
@ -47,9 +47,9 @@ export class WebSocketService {
|
||||||
this.subject.next(this.wsSendWrapper(UserOperation.FollowCommunity, followCommunityForm));
|
this.subject.next(this.wsSendWrapper(UserOperation.FollowCommunity, followCommunityForm));
|
||||||
}
|
}
|
||||||
|
|
||||||
public listCommunities() {
|
public listCommunities(form: ListCommunitiesForm) {
|
||||||
let data = {auth: UserService.Instance.auth };
|
this.setAuth(form, false);
|
||||||
this.subject.next(this.wsSendWrapper(UserOperation.ListCommunities, data));
|
this.subject.next(this.wsSendWrapper(UserOperation.ListCommunities, form));
|
||||||
}
|
}
|
||||||
|
|
||||||
public getFollowedCommunities() {
|
public getFollowedCommunities() {
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
export let version: string = "v0.0.2-0-gdae6651";
|
export let version: string = "v0.0.2-9-g8e5a5d1";
|
Loading…
Reference in a new issue