mirror of https://github.com/LemmyNet/lemmy.git
Merge branch 'dev'
commit
fa453a5955
|
@ -27,7 +27,7 @@ use actions::moderator::*;
|
||||||
|
|
||||||
#[derive(EnumString,ToString,Debug)]
|
#[derive(EnumString,ToString,Debug)]
|
||||||
pub enum UserOperation {
|
pub enum UserOperation {
|
||||||
Login, Register, CreateCommunity, CreatePost, ListCommunities, ListCategories, GetPost, GetCommunity, CreateComment, EditComment, SaveComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, SavePost, EditCommunity, FollowCommunity, GetFollowedCommunities, GetUserDetails, GetReplies, GetModlog, BanFromCommunity, AddModToCommunity, CreateSite, EditSite, GetSite, AddAdmin, BanUser, Search
|
Login, Register, CreateCommunity, CreatePost, ListCommunities, ListCategories, GetPost, GetCommunity, CreateComment, EditComment, SaveComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, SavePost, EditCommunity, FollowCommunity, GetFollowedCommunities, GetUserDetails, GetReplies, GetModlog, BanFromCommunity, AddModToCommunity, CreateSite, EditSite, GetSite, AddAdmin, BanUser, Search, MarkAllAsRead
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Fail, Debug)]
|
#[derive(Fail, Debug)]
|
||||||
|
@ -478,6 +478,11 @@ pub struct SearchResponse {
|
||||||
posts: Vec<PostView>,
|
posts: Vec<PostView>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct MarkAllAsRead {
|
||||||
|
auth: String
|
||||||
|
}
|
||||||
|
|
||||||
/// `ChatServer` manages chat rooms and responsible for coordinating chat
|
/// `ChatServer` manages chat rooms and responsible for coordinating chat
|
||||||
/// session. implementation is super primitive
|
/// session. implementation is super primitive
|
||||||
pub struct ChatServer {
|
pub struct ChatServer {
|
||||||
|
@ -728,6 +733,10 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<Str
|
||||||
let search: Search = serde_json::from_str(data)?;
|
let search: Search = serde_json::from_str(data)?;
|
||||||
search.perform(chat, msg.id)
|
search.perform(chat, msg.id)
|
||||||
},
|
},
|
||||||
|
UserOperation::MarkAllAsRead => {
|
||||||
|
let mark_all_as_read: MarkAllAsRead = serde_json::from_str(data)?;
|
||||||
|
mark_all_as_read.perform(chat, msg.id)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2709,3 +2718,56 @@ impl Perform for Search {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl Perform for MarkAllAsRead {
|
||||||
|
fn op_type(&self) -> UserOperation {
|
||||||
|
UserOperation::MarkAllAsRead
|
||||||
|
}
|
||||||
|
|
||||||
|
fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> Result<String, Error> {
|
||||||
|
|
||||||
|
let conn = establish_connection();
|
||||||
|
|
||||||
|
let claims = match Claims::decode(&self.auth) {
|
||||||
|
Ok(claims) => claims.claims,
|
||||||
|
Err(_e) => {
|
||||||
|
return Err(self.error("Not logged in."))?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let user_id = claims.id;
|
||||||
|
|
||||||
|
let replies = ReplyView::get_replies(&conn, user_id, &SortType::New, true, Some(1), Some(999))?;
|
||||||
|
|
||||||
|
for reply in &replies {
|
||||||
|
let comment_form = CommentForm {
|
||||||
|
content: reply.to_owned().content,
|
||||||
|
parent_id: reply.to_owned().parent_id,
|
||||||
|
post_id: reply.to_owned().post_id,
|
||||||
|
creator_id: reply.to_owned().creator_id,
|
||||||
|
removed: None,
|
||||||
|
read: Some(true),
|
||||||
|
updated: reply.to_owned().updated
|
||||||
|
};
|
||||||
|
|
||||||
|
let _updated_comment = match Comment::update(&conn, reply.id, &comment_form) {
|
||||||
|
Ok(comment) => comment,
|
||||||
|
Err(_e) => {
|
||||||
|
return Err(self.error("Couldn't update Comment"))?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let replies = ReplyView::get_replies(&conn, user_id, &SortType::New, true, Some(1), Some(999))?;
|
||||||
|
|
||||||
|
Ok(
|
||||||
|
serde_json::to_string(
|
||||||
|
&GetRepliesResponse {
|
||||||
|
op: self.op_type().to_string(),
|
||||||
|
replies: replies,
|
||||||
|
}
|
||||||
|
)?
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
"@types/js-cookie": "^2.2.1",
|
"@types/js-cookie": "^2.2.1",
|
||||||
"@types/jwt-decode": "^2.2.1",
|
"@types/jwt-decode": "^2.2.1",
|
||||||
"@types/markdown-it": "^0.0.7",
|
"@types/markdown-it": "^0.0.7",
|
||||||
|
"@types/markdown-it-container": "^2.0.2",
|
||||||
"autosize": "^4.0.2",
|
"autosize": "^4.0.2",
|
||||||
"classcat": "^1.1.3",
|
"classcat": "^1.1.3",
|
||||||
"dotenv": "^6.1.0",
|
"dotenv": "^6.1.0",
|
||||||
|
@ -27,6 +28,7 @@
|
||||||
"js-cookie": "^2.2.0",
|
"js-cookie": "^2.2.0",
|
||||||
"jwt-decode": "^2.2.0",
|
"jwt-decode": "^2.2.0",
|
||||||
"markdown-it": "^8.4.2",
|
"markdown-it": "^8.4.2",
|
||||||
|
"markdown-it-container": "^2.0.0",
|
||||||
"moment": "^2.24.0",
|
"moment": "^2.24.0",
|
||||||
"rxjs": "^6.4.0"
|
"rxjs": "^6.4.0"
|
||||||
},
|
},
|
||||||
|
|
|
@ -18,13 +18,23 @@ export class CreatePost extends Component<any, any> {
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-lg-6 mb-4">
|
<div class="col-12 col-lg-6 mb-4">
|
||||||
<h5>Create a Post</h5>
|
<h5>Create a Post</h5>
|
||||||
<PostForm onCreate={this.handlePostCreate}/>
|
<PostForm onCreate={this.handlePostCreate} prevCommunityName={this.prevCommunityName} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get prevCommunityName(): string {
|
||||||
|
if (this.props.location.state) {
|
||||||
|
let lastLocation = this.props.location.state.prevPath;
|
||||||
|
if (lastLocation.includes("/c/")) {
|
||||||
|
return lastLocation.split("/c/")[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
handlePostCreate(id: number) {
|
handlePostCreate(id: number) {
|
||||||
this.props.history.push(`/post/${id}`);
|
this.props.history.push(`/post/${id}`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,16 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<h5>Inbox for <Link to={`/u/${user.username}`}>{user.username}</Link></h5>
|
<h5 class="mb-0">
|
||||||
|
<span>Inbox for <Link to={`/u/${user.username}`}>{user.username}</Link></span>
|
||||||
|
</h5>
|
||||||
|
{this.state.replies.length > 0 && this.state.unreadType == UnreadType.Unread &&
|
||||||
|
<ul class="list-inline mb-1 text-muted small font-weight-bold">
|
||||||
|
<li className="list-inline-item">
|
||||||
|
<span class="pointer" onClick={this.markAllAsRead}>mark all as read</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
}
|
||||||
{this.selects()}
|
{this.selects()}
|
||||||
{this.replies()}
|
{this.replies()}
|
||||||
{this.paginator()}
|
{this.paginator()}
|
||||||
|
@ -147,13 +156,17 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
i.refetch();
|
i.refetch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
markAllAsRead() {
|
||||||
|
WebSocketService.Instance.markAllAsRead();
|
||||||
|
}
|
||||||
|
|
||||||
parseMessage(msg: any) {
|
parseMessage(msg: any) {
|
||||||
console.log(msg);
|
console.log(msg);
|
||||||
let op: UserOperation = msgOp(msg);
|
let op: UserOperation = msgOp(msg);
|
||||||
if (msg.error) {
|
if (msg.error) {
|
||||||
alert(msg.error);
|
alert(msg.error);
|
||||||
return;
|
return;
|
||||||
} else if (op == UserOperation.GetReplies) {
|
} else if (op == UserOperation.GetReplies || op == UserOperation.MarkAllAsRead) {
|
||||||
let res: GetRepliesResponse = msg;
|
let res: GetRepliesResponse = msg;
|
||||||
this.state.replies = res.replies;
|
this.state.replies = res.replies;
|
||||||
this.sendRepliesCount();
|
this.sendRepliesCount();
|
||||||
|
|
|
@ -79,7 +79,7 @@ export class Navbar extends Component<any, NavbarState> {
|
||||||
<Link class="nav-link" to="/search">Search</Link>
|
<Link class="nav-link" to="/search">Search</Link>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<Link class="nav-link" to="/create_post">Create Post</Link>
|
<Link class="nav-link" to={{pathname: '/create_post', state: { prevPath: this.currentLocation }}}>Create Post</Link>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<Link class="nav-link" to="/create_community">Create Community</Link>
|
<Link class="nav-link" to="/create_community">Create Community</Link>
|
||||||
|
@ -165,6 +165,10 @@ export class Navbar extends Component<any, NavbarState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get currentLocation() {
|
||||||
|
return this.context.router.history.location.pathname;
|
||||||
|
}
|
||||||
|
|
||||||
sendRepliesCount(res: GetRepliesResponse) {
|
sendRepliesCount(res: GetRepliesResponse) {
|
||||||
UserService.Instance.sub.next({user: UserService.Instance.user, unreadCount: res.replies.filter(r => !r.read).length});
|
UserService.Instance.sub.next({user: UserService.Instance.user, unreadCount: res.replies.filter(r => !r.read).length});
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import * as autosize from 'autosize';
|
||||||
|
|
||||||
interface PostFormProps {
|
interface PostFormProps {
|
||||||
post?: Post; // If a post is given, that means this is an edit
|
post?: Post; // If a post is given, that means this is an edit
|
||||||
|
prevCommunityName?: string;
|
||||||
onCancel?(): any;
|
onCancel?(): any;
|
||||||
onCreate?(id: number): any;
|
onCreate?(id: number): any;
|
||||||
onEdit?(post: Post): any;
|
onEdit?(post: Post): any;
|
||||||
|
@ -170,6 +171,9 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
this.state.communities = res.communities;
|
this.state.communities = res.communities;
|
||||||
if (this.props.post) {
|
if (this.props.post) {
|
||||||
this.state.postForm.community_id = this.props.post.community_id;
|
this.state.postForm.community_id = this.props.post.community_id;
|
||||||
|
} else if (this.props.prevCommunityName) {
|
||||||
|
let foundCommunityId = res.communities.find(r => r.name == this.props.prevCommunityName).id;
|
||||||
|
this.state.postForm.community_id = foundCommunityId;
|
||||||
} else {
|
} else {
|
||||||
this.state.postForm.community_id = res.communities[0].id;
|
this.state.postForm.community_id = res.communities[0].id;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
export enum UserOperation {
|
export enum UserOperation {
|
||||||
Login, Register, CreateCommunity, CreatePost, ListCommunities, ListCategories, GetPost, GetCommunity, CreateComment, EditComment, SaveComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, SavePost, EditCommunity, FollowCommunity, GetFollowedCommunities, GetUserDetails, GetReplies, GetModlog, BanFromCommunity, AddModToCommunity, CreateSite, EditSite, GetSite, AddAdmin, BanUser, Search
|
Login, Register, CreateCommunity, CreatePost, ListCommunities, ListCategories, GetPost, GetCommunity, CreateComment, EditComment, SaveComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, SavePost, EditCommunity, FollowCommunity, GetFollowedCommunities, GetUserDetails, GetReplies, GetModlog, BanFromCommunity, AddModToCommunity, CreateSite, EditSite, GetSite, AddAdmin, BanUser, Search, MarkAllAsRead
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum CommentSortType {
|
export enum CommentSortType {
|
||||||
|
|
|
@ -177,6 +177,12 @@ export class WebSocketService {
|
||||||
this.subject.next(this.wsSendWrapper(UserOperation.Search, form));
|
this.subject.next(this.wsSendWrapper(UserOperation.Search, form));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public markAllAsRead() {
|
||||||
|
let form = {};
|
||||||
|
this.setAuth(form);
|
||||||
|
this.subject.next(this.wsSendWrapper(UserOperation.MarkAllAsRead, form));
|
||||||
|
}
|
||||||
|
|
||||||
private wsSendWrapper(op: UserOperation, data: any) {
|
private wsSendWrapper(op: UserOperation, data: any) {
|
||||||
let send = { op: UserOperation[op], data: data };
|
let send = { op: UserOperation[op], data: data };
|
||||||
console.log(send);
|
console.log(send);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { UserOperation, Comment, User, SortType, ListingType } from './interfaces';
|
import { UserOperation, Comment, User, SortType, ListingType } from './interfaces';
|
||||||
import * as markdown_it from 'markdown-it';
|
import * as markdown_it from 'markdown-it';
|
||||||
|
import * as markdown_it_container from 'markdown-it-container';
|
||||||
|
|
||||||
export let repoUrl = 'https://github.com/dessalines/lemmy';
|
export let repoUrl = 'https://github.com/dessalines/lemmy';
|
||||||
|
|
||||||
|
@ -12,6 +13,23 @@ var md = new markdown_it({
|
||||||
html: true,
|
html: true,
|
||||||
linkify: true,
|
linkify: true,
|
||||||
typographer: true
|
typographer: true
|
||||||
|
}).use(markdown_it_container, 'spoiler', {
|
||||||
|
validate: function(params: any) {
|
||||||
|
return params.trim().match(/^spoiler\s+(.*)$/);
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function (tokens: any, idx: any) {
|
||||||
|
var m = tokens[idx].info.trim().match(/^spoiler\s+(.*)$/);
|
||||||
|
|
||||||
|
if (tokens[idx].nesting === 1) {
|
||||||
|
// opening tag
|
||||||
|
return '<details><summary>' + md.utils.escapeHtml(m[1]) + '</summary>\n';
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// closing tag
|
||||||
|
return '</details>\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export function hotRank(comment: Comment): number {
|
export function hotRank(comment: Comment): number {
|
||||||
|
|
14
ui/yarn.lock
14
ui/yarn.lock
|
@ -38,7 +38,14 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-2.1.0.tgz#ea3dd64c4805597311790b61e872cbd1ed2cd806"
|
resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-2.1.0.tgz#ea3dd64c4805597311790b61e872cbd1ed2cd806"
|
||||||
integrity sha512-Q7DYAOi9O/+cLLhdaSvKdaumWyHbm7HAk/bFwwyTuU0arR5yyCeW5GOoqt4tJTpDRxhpx9Q8kQL6vMpuw9hDSw==
|
integrity sha512-Q7DYAOi9O/+cLLhdaSvKdaumWyHbm7HAk/bFwwyTuU0arR5yyCeW5GOoqt4tJTpDRxhpx9Q8kQL6vMpuw9hDSw==
|
||||||
|
|
||||||
"@types/markdown-it@^0.0.7":
|
"@types/markdown-it-container@^2.0.2":
|
||||||
|
version "2.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/markdown-it-container/-/markdown-it-container-2.0.2.tgz#0e624653415a1c2f088a5ae51f7bfff480c03f49"
|
||||||
|
integrity sha512-T770GL+zJz8Ssh1NpLiOruYhrU96yb8ovPSegLrWY5XIkJc6PVVC7kH/oQaVD0rkePpWMFJK018OgS/pwviOMw==
|
||||||
|
dependencies:
|
||||||
|
"@types/markdown-it" "*"
|
||||||
|
|
||||||
|
"@types/markdown-it@*", "@types/markdown-it@^0.0.7":
|
||||||
version "0.0.7"
|
version "0.0.7"
|
||||||
resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-0.0.7.tgz#75070485a3d8ad11e7deb8287f4430be15bf4d39"
|
resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-0.0.7.tgz#75070485a3d8ad11e7deb8287f4430be15bf4d39"
|
||||||
integrity sha512-WyL6pa76ollQFQNEaLVa41ZUUvDvPY+qAUmlsphnrpL6I9p1m868b26FyeoOmo7X3/Ta/S9WKXcEYXUSHnxoVQ==
|
integrity sha512-WyL6pa76ollQFQNEaLVa41ZUUvDvPY+qAUmlsphnrpL6I9p1m868b26FyeoOmo7X3/Ta/S9WKXcEYXUSHnxoVQ==
|
||||||
|
@ -1674,6 +1681,11 @@ map-visit@^1.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
object-visit "^1.0.0"
|
object-visit "^1.0.0"
|
||||||
|
|
||||||
|
markdown-it-container@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/markdown-it-container/-/markdown-it-container-2.0.0.tgz#0019b43fd02eefece2f1960a2895fba81a404695"
|
||||||
|
integrity sha1-ABm0P9Au7+zi8ZYKKJX7qBpARpU=
|
||||||
|
|
||||||
markdown-it@^8.4.2:
|
markdown-it@^8.4.2:
|
||||||
version "8.4.2"
|
version "8.4.2"
|
||||||
resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-8.4.2.tgz#386f98998dc15a37722aa7722084f4020bdd9b54"
|
resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-8.4.2.tgz#386f98998dc15a37722aa7722084f4020bdd9b54"
|
||||||
|
|
Loading…
Reference in New Issue