From 9afadfb9c4c5db1796848ec4af9756fe03d51ee3 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Fri, 19 Apr 2019 21:06:25 -0700 Subject: Saving replies, the actual fixes will be in the merge to dev. --- ui/src/components/comment-node.tsx | 229 +++++++++++++++++++++++---------- ui/src/components/comment-nodes.tsx | 7 +- ui/src/components/communities.tsx | 4 +- ui/src/components/community.tsx | 6 +- ui/src/components/create-community.tsx | 2 +- ui/src/components/create-post.tsx | 2 +- ui/src/components/login.tsx | 4 +- ui/src/components/main.tsx | 12 +- ui/src/components/modlog.tsx | 40 ++++-- ui/src/components/navbar.tsx | 26 ++-- ui/src/components/post-listing.tsx | 35 +++-- ui/src/components/post-listings.tsx | 2 +- ui/src/components/post.tsx | 50 +++++-- ui/src/components/setup.tsx | 2 +- ui/src/components/sidebar.tsx | 4 +- ui/src/components/site-form.tsx | 2 +- ui/src/components/user.tsx | 14 +- ui/src/index.html | 2 - ui/src/index.tsx | 3 +- ui/src/interfaces.ts | 72 +++++++---- ui/src/main.css | 87 ------------- ui/src/services/WebSocketService.ts | 23 +++- ui/src/utils.ts | 21 ++- 23 files changed, 397 insertions(+), 252 deletions(-) delete mode 100644 ui/src/main.css (limited to 'ui/src') diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index dcfb18a9..c1fc059b 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -1,12 +1,14 @@ import { Component, linkEvent } from 'inferno'; import { Link } from 'inferno-router'; -import { CommentNode as CommentNodeI, CommentLikeForm, CommentForm as CommentFormI, BanFromCommunityForm, CommunityUser, AddModToCommunityForm } from '../interfaces'; +import { CommentNode as CommentNodeI, CommentLikeForm, CommentForm as CommentFormI, SaveCommentForm, BanFromCommunityForm, BanUserForm, CommunityUser, UserView, AddModToCommunityForm, AddAdminForm } from '../interfaces'; import { WebSocketService, UserService } from '../services'; -import { mdToHtml, getUnixTime } from '../utils'; +import { mdToHtml, getUnixTime, canMod, isMod } from '../utils'; import { MomentTime } from './moment-time'; import { CommentForm } from './comment-form'; import { CommentNodes } from './comment-nodes'; +enum BanType {Community, Site}; + interface CommentNodeState { showReply: boolean; showEdit: boolean; @@ -15,6 +17,7 @@ interface CommentNodeState { showBanDialog: boolean; banReason: string; banExpires: string; + banType: BanType; } interface CommentNodeProps { @@ -23,6 +26,7 @@ interface CommentNodeProps { viewOnly?: boolean; locked?: boolean; moderators: Array; + admins: Array; } export class CommentNode extends Component { @@ -35,6 +39,7 @@ export class CommentNode extends Component { showBanDialog: false, banReason: null, banExpires: null, + banType: BanType.Community } constructor(props: any, context: any) { @@ -60,6 +65,12 @@ export class CommentNode extends Component {
  • {node.comment.creator_name}
  • + {this.isMod && +
  • mod
  • + } + {this.isAdmin && +
  • admin
  • + }
  • ( +{node.comment.upvotes} @@ -77,47 +88,70 @@ export class CommentNode extends Component {
      - {!this.props.viewOnly && - + {UserService.Instance.user && !this.props.viewOnly && + <>
    • reply
    • +
    • + {node.comment.saved ? 'unsave' : 'save'} +
    • {this.myComment && <> -
    • - edit -
    • -
    • - delete -
    • - +
    • + edit +
    • +
    • + delete +
    • + } - {this.canMod && - <> + {/* Admins and mods can remove comments */} + {this.canMod &&
    • {!this.props.node.comment.removed ? remove : restore }
    • - {!this.isMod && - <> + } + {/* Mods can ban from community, and appoint as mods to community */} + {this.canMod && + <> + {!this.isMod && +
    • + {!this.props.node.comment.banned_from_community ? + ban : + unban + } +
    • + } + {!this.props.node.comment.banned_from_community && +
    • + {`${this.isMod ? 'remove' : 'appoint'} as mod`} +
    • + } + + } + {/* Admins can ban from all, and appoint other admins */} + {this.canAdmin && + <> + {!this.isAdmin &&
    • {!this.props.node.comment.banned ? - ban : - unban + ban from site : + unban from site }
    • - - } - {!this.props.node.comment.banned && -
    • - {`${this.isMod ? 'remove' : 'appoint'} as mod`} -
    • - } - + } + {!this.props.node.comment.banned && +
    • + {`${this.isAdmin ? 'remove' : 'appoint'} as admin`} +
    • + } + } -
      + }
    • link @@ -133,22 +167,35 @@ export class CommentNode extends Component { } {this.state.showBanDialog && -
      -
      - - -
      -
      - - -
      -
      - -
      -
      +
      +
      + + +
      +
      + + +
      +
      + +
      +
      + } + {this.state.showReply && + + } + {this.props.node.children && + } - {this.state.showReply && } - {this.props.node.children && }
    ) } @@ -158,27 +205,22 @@ export class CommentNode extends Component { } get canMod(): boolean { + let adminsThenMods = this.props.admins.map(a => a.id) + .concat(this.props.moderators.map(m => m.user_id)); - // You can do moderator actions only on the mods added after you. - if (UserService.Instance.user) { - let modIds = this.props.moderators.map(m => m.user_id); - let yourIndex = modIds.findIndex(id => id == UserService.Instance.user.id); - if (yourIndex == -1) { - return false; - } else { - console.log(modIds); - modIds = modIds.slice(0, yourIndex+1); // +1 cause you cant mod yourself - console.log(modIds); - return !modIds.includes(this.props.node.comment.creator_id); - } - } else { - return false; - } - + return canMod(UserService.Instance.user, adminsThenMods, this.props.node.comment.creator_id); } get isMod(): boolean { - return this.props.moderators.map(m => m.user_id).includes(this.props.node.comment.creator_id); + return isMod(this.props.moderators.map(m => m.user_id), this.props.node.comment.creator_id); + } + + get isAdmin(): boolean { + return isMod(this.props.admins.map(a => a.id), this.props.node.comment.creator_id); + } + + get canAdmin(): boolean { + return canMod(UserService.Instance.user, this.props.admins.map(a => a.id), this.props.node.comment.creator_id); } handleReplyClick(i: CommentNode) { @@ -193,16 +235,27 @@ export class CommentNode extends Component { handleDeleteClick(i: CommentNode) { let deleteForm: CommentFormI = { - content: "*deleted*", + content: '*deleted*', edit_id: i.props.node.comment.id, creator_id: i.props.node.comment.creator_id, post_id: i.props.node.comment.post_id, parent_id: i.props.node.comment.parent_id, + removed: i.props.node.comment.removed, auth: null }; WebSocketService.Instance.editComment(deleteForm); } + handleSaveCommentClick(i: CommentNode) { + let saved = (i.props.node.comment.saved == undefined) ? true : !i.props.node.comment.saved; + let form: SaveCommentForm = { + comment_id: i.props.node.comment.id, + save: saved + }; + + WebSocketService.Instance.saveComment(form); + } + handleReplyCancel() { this.state.showReply = false; this.state.showEdit = false; @@ -257,8 +310,15 @@ export class CommentNode extends Component { i.setState(i.state); } + handleModBanFromCommunityShow(i: CommentNode) { + i.state.showBanDialog = true; + i.state.banType = BanType.Community; + i.setState(i.state); + } + handleModBanShow(i: CommentNode) { i.state.showBanDialog = true; + i.state.banType = BanType.Site; i.setState(i.state); } @@ -272,16 +332,42 @@ export class CommentNode extends Component { i.setState(i.state); } + handleModBanFromCommunitySubmit(i: CommentNode) { + i.state.banType = BanType.Community; + i.setState(i.state); + i.handleModBanBothSubmit(i); + } + handleModBanSubmit(i: CommentNode) { + i.state.banType = BanType.Site; + i.setState(i.state); + i.handleModBanBothSubmit(i); + } + + handleModBanBothSubmit(i: CommentNode) { event.preventDefault(); - let form: BanFromCommunityForm = { - user_id: i.props.node.comment.creator_id, - community_id: i.props.node.comment.community_id, - ban: !i.props.node.comment.banned, - reason: i.state.banReason, - expires: getUnixTime(i.state.banExpires), - }; - WebSocketService.Instance.banFromCommunity(form); + + console.log(BanType[i.state.banType]); + console.log(i.props.node.comment.banned); + + if (i.state.banType == BanType.Community) { + let form: BanFromCommunityForm = { + user_id: i.props.node.comment.creator_id, + community_id: i.props.node.comment.community_id, + ban: !i.props.node.comment.banned_from_community, + reason: i.state.banReason, + expires: getUnixTime(i.state.banExpires), + }; + WebSocketService.Instance.banFromCommunity(form); + } else { + let form: BanUserForm = { + user_id: i.props.node.comment.creator_id, + ban: !i.props.node.comment.banned, + reason: i.state.banReason, + expires: getUnixTime(i.state.banExpires), + }; + WebSocketService.Instance.banUser(form); + } i.state.showBanDialog = false; i.setState(i.state); @@ -296,4 +382,13 @@ export class CommentNode extends Component { WebSocketService.Instance.addModToCommunity(form); i.setState(i.state); } + + addAdmin(i: CommentNode) { + let form: AddAdminForm = { + user_id: i.props.node.comment.creator_id, + added: !i.isAdmin, + }; + WebSocketService.Instance.addAdmin(form); + i.setState(i.state); + } } diff --git a/ui/src/components/comment-nodes.tsx b/ui/src/components/comment-nodes.tsx index 498c69b8..abbb1719 100644 --- a/ui/src/components/comment-nodes.tsx +++ b/ui/src/components/comment-nodes.tsx @@ -1,5 +1,5 @@ import { Component } from 'inferno'; -import { CommentNode as CommentNodeI, CommunityUser } from '../interfaces'; +import { CommentNode as CommentNodeI, CommunityUser, UserView } from '../interfaces'; import { CommentNode } from './comment-node'; interface CommentNodesState { @@ -8,6 +8,7 @@ interface CommentNodesState { interface CommentNodesProps { nodes: Array; moderators?: Array; + admins?: Array; noIndent?: boolean; viewOnly?: boolean; locked?: boolean; @@ -27,7 +28,9 @@ export class CommentNodes extends Component + moderators={this.props.moderators} + admins={this.props.admins} + /> )}
    ) diff --git a/ui/src/components/communities.tsx b/ui/src/components/communities.tsx index 86800684..9145c1cd 100644 --- a/ui/src/components/communities.tsx +++ b/ui/src/components/communities.tsx @@ -53,9 +53,9 @@ export class Communities extends Component { return (
    {this.state.loading ? -

    : +
    :
    -

    Communities

    +
    Communities
    diff --git a/ui/src/components/community.tsx b/ui/src/components/community.tsx index cd95f991..6271bde5 100644 --- a/ui/src/components/community.tsx +++ b/ui/src/components/community.tsx @@ -60,14 +60,14 @@ export class Community extends Component { return (
    {this.state.loading ? -

    : +
    :
    -

    {this.state.community.title} +

    {this.state.community.title} {this.state.community.removed && removed } -
    +
    diff --git a/ui/src/components/create-community.tsx b/ui/src/components/create-community.tsx index 5f397411..0e806dbb 100644 --- a/ui/src/components/create-community.tsx +++ b/ui/src/components/create-community.tsx @@ -13,7 +13,7 @@ export class CreateCommunity extends Component {
    -

    Create Forum

    +
    Create Forum
    diff --git a/ui/src/components/create-post.tsx b/ui/src/components/create-post.tsx index 041ffd17..7d2f1dd4 100644 --- a/ui/src/components/create-post.tsx +++ b/ui/src/components/create-post.tsx @@ -13,7 +13,7 @@ export class CreatePost extends Component {
    -

    Create a Post

    +
    Create a Post
    diff --git a/ui/src/components/login.tsx b/ui/src/components/login.tsx index 4d0b22d0..6d15a382 100644 --- a/ui/src/components/login.tsx +++ b/ui/src/components/login.tsx @@ -67,7 +67,7 @@ export class Login extends Component { return (
    -

    Login

    +
    Login
    @@ -94,7 +94,7 @@ export class Login extends Component { registerForm() { return ( -

    Sign Up

    +
    Sign Up
    diff --git a/ui/src/components/main.tsx b/ui/src/components/main.tsx index 0b5923c0..01c70f94 100644 --- a/ui/src/components/main.tsx +++ b/ui/src/components/main.tsx @@ -78,12 +78,12 @@ export class Main extends Component {
    {this.state.loading ? -

    : +
    :
    {this.trendingCommunities()} {UserService.Instance.user && this.state.subscribedCommunities.length > 0 &&
    -

    Subscribed forums

    +
    Subscribed forums
      {this.state.subscribedCommunities.map(community =>
    • {community.community_name}
    • @@ -103,7 +103,7 @@ export class Main extends Component { trendingCommunities() { return (
      -

      Trending forums

      +
      Trending forums
        {this.state.trendingCommunities.map(community =>
      • {community.name}
      • @@ -116,7 +116,7 @@ export class Main extends Component { landing() { return (
        -

        {`${this.state.site.site.name}`}

        +
        {`${this.state.site.site.name}`}
        • {this.state.site.site.number_of_users} Users
        • {this.state.site.site.number_of_posts} Posts
        • @@ -136,10 +136,10 @@ export class Main extends Component {
        } -

        Welcome to +

        Welcome to LemmyBeta -
        +

        Lemmy is a link aggregator / reddit alternative, intended to work in the fediverse.

        Its self-hostable, has live-updating comment threads, and is tiny (~80kB). Federation into the ActivityPub network is on the roadmap.

        This is a very early beta version, and a lot of features are currently broken or missing.

        diff --git a/ui/src/components/modlog.tsx b/ui/src/components/modlog.tsx index 56b08a7e..7145b4f6 100644 --- a/ui/src/components/modlog.tsx +++ b/ui/src/components/modlog.tsx @@ -9,7 +9,7 @@ import { MomentTime } from './moment-time'; import * as moment from 'moment'; interface ModlogState { - combined: Array<{type_: string, data: ModRemovePost | ModLockPost | ModRemoveCommunity}>, + combined: Array<{type_: string, data: ModRemovePost | ModLockPost | ModRemoveCommunity | ModAdd | ModBan}>, communityId?: number, communityName?: string, page: number; @@ -51,6 +51,8 @@ export class Modlog extends Component { let removed_communities = addTypeInfo(res.removed_communities, "removed_communities"); let banned_from_community = addTypeInfo(res.banned_from_community, "banned_from_community"); let added_to_community = addTypeInfo(res.added_to_community, "added_to_community"); + let added = addTypeInfo(res.added, "added"); + let banned = addTypeInfo(res.banned, "banned"); this.state.combined = []; this.state.combined.push(...removed_posts); @@ -59,9 +61,11 @@ export class Modlog extends Component { this.state.combined.push(...removed_communities); this.state.combined.push(...banned_from_community); this.state.combined.push(...added_to_community); + this.state.combined.push(...added); + this.state.combined.push(...banned); if (this.state.communityId && this.state.combined.length > 0) { - this.state.communityName = this.state.combined[0].data.community_name; + this.state.communityName = (this.state.combined[0].data as ModRemovePost).community_name; } // Sort them by time @@ -95,13 +99,14 @@ export class Modlog extends Component { <> {(i.data as ModRemoveComment).removed? 'Removed' : 'Restored'} Comment {(i.data as ModRemoveComment).comment_content} + by {(i.data as ModRemoveComment).comment_user_name}
        {(i.data as ModRemoveComment).reason && ` reason: ${(i.data as ModRemoveComment).reason}`}
        } {i.type_ == 'removed_communities' && <> {(i.data as ModRemoveCommunity).removed ? 'Removed' : 'Restored'} - Community {i.data.community_name} + Community {(i.data as ModRemoveCommunity).community_name}
        {(i.data as ModRemoveCommunity).reason && ` reason: ${(i.data as ModRemoveCommunity).reason}`}
        {(i.data as ModRemoveCommunity).expires && ` expires: ${moment.utc((i.data as ModRemoveCommunity).expires).fromNow()}`}
        @@ -110,6 +115,8 @@ export class Modlog extends Component { <> {(i.data as ModBanFromCommunity).banned ? 'Banned ' : 'Unbanned '} {(i.data as ModBanFromCommunity).other_user_name} + from the community + {(i.data as ModBanFromCommunity).community_name}
        {(i.data as ModBanFromCommunity).reason && ` reason: ${(i.data as ModBanFromCommunity).reason}`}
        {(i.data as ModBanFromCommunity).expires && ` expires: ${moment.utc((i.data as ModBanFromCommunity).expires).fromNow()}`}
        @@ -119,12 +126,27 @@ export class Modlog extends Component { {(i.data as ModAddCommunity).removed ? 'Removed ' : 'Appointed '} {(i.data as ModAddCommunity).other_user_name} as a mod to the community - {i.data.community_name} + {(i.data as ModAddCommunity).community_name} + + } + {i.type_ == 'banned' && + <> + {(i.data as ModBan).banned ? 'Banned ' : 'Unbanned '} + {(i.data as ModBan).other_user_name} +
        {(i.data as ModBan).reason && ` reason: ${(i.data as ModBan).reason}`}
        +
        {(i.data as ModBan).expires && ` expires: ${moment.utc((i.data as ModBan).expires).fromNow()}`}
        + + } + {i.type_ == 'added' && + <> + {(i.data as ModAdd).removed ? 'Removed ' : 'Appointed '} + {(i.data as ModAdd).other_user_name} + as an admin } - ) + ) } @@ -136,12 +158,12 @@ export class Modlog extends Component { return (
        {this.state.loading ? -

        : +
        :
        -

        +

        {this.state.communityName && /f/{this.state.communityName} } Modlog -
        +
    @@ -183,7 +205,7 @@ export class Modlog extends Component { i.setState(i.state); i.refetch(); } - + refetch(){ let modlogForm: GetModlogForm = { community_id: this.state.communityId, diff --git a/ui/src/components/navbar.tsx b/ui/src/components/navbar.tsx index ae693825..be98912e 100644 --- a/ui/src/components/navbar.tsx +++ b/ui/src/components/navbar.tsx @@ -64,16 +64,22 @@ export class Navbar extends Component { diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index 1a52bf79..da375aee 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -1,7 +1,7 @@ import { Component, linkEvent } from 'inferno'; import { Link } from 'inferno-router'; import { WebSocketService, UserService } from '../services'; -import { Post, CreatePostLikeForm, PostForm as PostFormI } from '../interfaces'; +import { Post, CreatePostLikeForm, PostForm as PostFormI, SavePostForm } from '../interfaces'; import { MomentTime } from './moment-time'; import { PostForm } from './post-form'; import { mdToHtml } from '../utils'; @@ -60,17 +60,17 @@ export class PostListing extends Component {
    {post.score}
    â–¼
    -
    +
    {post.url ?
    -

    {post.name} +

    {post.name} {post.removed && removed } {post.locked && locked } -
    + {(new URL(post.url)).hostname} { !this.state.iframeExpanded ? + @@ -83,14 +83,14 @@ export class PostListing extends Component { }
    - :

    {post.name} + :

    {post.name} {post.removed && removed } {post.locked && locked } -
    + }
    @@ -120,17 +120,20 @@ export class PostListing extends Component { {post.number_of_comments} Comments - {this.props.editable && + {UserService.Instance.user && this.props.editable &&
      +
    • + {this.props.post.saved ? 'unsave' : 'save'} +
    • {this.myPost && - + <>
    • edit
    • delete
    • -
      + } {this.props.post.am_mod && @@ -204,11 +207,23 @@ export class PostListing extends Component { url: '', edit_id: i.props.post.id, creator_id: i.props.post.creator_id, + removed: !i.props.post.removed, + locked: !i.props.post.locked, auth: null }; WebSocketService.Instance.editPost(deleteForm); } + handleSavePostClick(i: PostListing) { + let saved = (i.props.post.saved == undefined) ? true : !i.props.post.saved; + let form: SavePostForm = { + post_id: i.props.post.id, + save: saved + }; + + WebSocketService.Instance.savePost(form); + } + handleModRemoveShow(i: PostListing) { i.state.showRemoveDialog = true; i.setState(i.state); @@ -227,6 +242,7 @@ export class PostListing extends Component { edit_id: i.props.post.id, creator_id: i.props.post.creator_id, removed: !i.props.post.removed, + locked: !i.props.post.locked, reason: i.state.removeReason, auth: null, }; @@ -242,6 +258,7 @@ export class PostListing extends Component { community_id: i.props.post.community_id, edit_id: i.props.post.id, creator_id: i.props.post.creator_id, + removed: !i.props.post.removed, locked: !i.props.post.locked, auth: null, }; diff --git a/ui/src/components/post-listings.tsx b/ui/src/components/post-listings.tsx index 8fc19b30..b1e48a61 100644 --- a/ui/src/components/post-listings.tsx +++ b/ui/src/components/post-listings.tsx @@ -61,7 +61,7 @@ export class PostListings extends Component {this.state.loading ? -

      : +
      :
      {this.selects()} {this.state.posts.length > 0 diff --git a/ui/src/components/post.tsx b/ui/src/components/post.tsx index d79a6c97..64f56d88 100644 --- a/ui/src/components/post.tsx +++ b/ui/src/components/post.tsx @@ -1,7 +1,7 @@ import { Component, linkEvent } from 'inferno'; import { Subscription } from "rxjs"; import { retryWhen, delay, take } from 'rxjs/operators'; -import { UserOperation, Community, Post as PostI, GetPostResponse, PostResponse, Comment, CommentResponse, CommentSortType, CreatePostLikeResponse, CommunityUser, CommunityResponse, CommentNode as CommentNodeI, BanFromCommunityResponse, AddModToCommunityResponse } from '../interfaces'; +import { UserOperation, Community, Post as PostI, GetPostResponse, PostResponse, Comment, CommentResponse, CommentSortType, CreatePostLikeResponse, CommunityUser, CommunityResponse, CommentNode as CommentNodeI, BanFromCommunityResponse, BanUserResponse, AddModToCommunityResponse, AddAdminResponse, UserView } from '../interfaces'; import { WebSocketService } from '../services'; import { msgOp, hotRank } from '../utils'; import { PostListing } from './post-listing'; @@ -17,6 +17,7 @@ interface PostState { commentSort: CommentSortType; community: Community; moderators: Array; + admins: Array; scrolled?: boolean; scrolled_comment_id?: number; loading: boolean; @@ -31,6 +32,7 @@ export class Post extends Component { commentSort: CommentSortType.Hot, community: null, moderators: [], + admins: [], scrolled: false, loading: true } @@ -77,7 +79,7 @@ export class Post extends Component { return (
      {this.state.loading ? -

      : +
      :
      @@ -123,9 +125,15 @@ export class Post extends Component { newComments() { return (
      -

      New Comments

      +
      New Comments
      {this.state.comments.map(comment => - + )}
      ) @@ -187,8 +195,13 @@ export class Post extends Component { commentsTree() { let nodes = this.buildCommentsTree(); return ( -
      - +
      +
      ); } @@ -202,9 +215,11 @@ export class Post extends Component { } else if (op == UserOperation.GetPost) { let res: GetPostResponse = msg; this.state.post = res.post; + this.state.post = res.post; this.state.comments = res.comments; this.state.community = res.community; this.state.moderators = res.moderators; + this.state.admins = res.admins; this.state.loading = false; this.setState(this.state); } else if (op == UserOperation.CreateComment) { @@ -222,8 +237,12 @@ export class Post extends Component { found.score = res.comment.score; this.setState(this.state); - } - else if (op == UserOperation.CreateCommentLike) { + } else if (op == UserOperation.SaveComment) { + let res: CommentResponse = msg; + let found = this.state.comments.find(c => c.id == res.comment.id); + found.saved = res.comment.saved; + this.setState(this.state); + } else if (op == UserOperation.CreateCommentLike) { let res: CommentResponse = msg; let found: Comment = this.state.comments.find(c => c.id === res.comment.id); found.score = res.comment.score; @@ -243,6 +262,10 @@ export class Post extends Component { let res: PostResponse = msg; this.state.post = res.post; this.setState(this.state); + } else if (op == UserOperation.SavePost) { + let res: PostResponse = msg; + this.state.post = res.post; + this.setState(this.state); } else if (op == UserOperation.EditCommunity) { let res: CommunityResponse = msg; this.state.community = res.community; @@ -257,12 +280,21 @@ export class Post extends Component { } else if (op == UserOperation.BanFromCommunity) { let res: BanFromCommunityResponse = msg; this.state.comments.filter(c => c.creator_id == res.user.id) - .forEach(c => c.banned = res.banned); + .forEach(c => c.banned_from_community = res.banned); this.setState(this.state); } else if (op == UserOperation.AddModToCommunity) { let res: AddModToCommunityResponse = msg; this.state.moderators = res.moderators; this.setState(this.state); + } else if (op == UserOperation.BanUser) { + let res: BanUserResponse = msg; + this.state.comments.filter(c => c.creator_id == res.user.id) + .forEach(c => c.banned = res.banned); + this.setState(this.state); + } else if (op == UserOperation.AddAdmin) { + let res: AddAdminResponse = msg; + this.state.admins = res.admins; + this.setState(this.state); } } diff --git a/ui/src/components/setup.tsx b/ui/src/components/setup.tsx index 9560a60d..9a671359 100644 --- a/ui/src/components/setup.tsx +++ b/ui/src/components/setup.tsx @@ -61,7 +61,7 @@ export class Setup extends Component { registerUser() { return ( -

      Set up Site Administrator

      +
      Set up Site Administrator
      diff --git a/ui/src/components/sidebar.tsx b/ui/src/components/sidebar.tsx index b0c0b7b9..2f231f9a 100644 --- a/ui/src/components/sidebar.tsx +++ b/ui/src/components/sidebar.tsx @@ -48,11 +48,11 @@ export class Sidebar extends Component { let community = this.props.community; return (
      -

      {community.title} +

      {community.title} {community.removed && removed } -
      + /f/{community.name} {community.am_mod &&
        diff --git a/ui/src/components/site-form.tsx b/ui/src/components/site-form.tsx index 7ca45b86..55da1667 100644 --- a/ui/src/components/site-form.tsx +++ b/ui/src/components/site-form.tsx @@ -33,7 +33,7 @@ export class SiteForm extends Component { render() { return ( -

        {`${this.props.site ? 'Edit' : 'Name'} your Site`}

        +
        {`${this.props.site ? 'Edit' : 'Name'} your Site`}
        diff --git a/ui/src/components/user.tsx b/ui/src/components/user.tsx index fdcd378e..8ebde48a 100644 --- a/ui/src/components/user.tsx +++ b/ui/src/components/user.tsx @@ -77,7 +77,7 @@ export class User extends Component {
        -

        /u/{this.state.user.name}

        +
        /u/{this.state.user.name}
        {this.selects()} {this.state.view == View.Overview && this.overview() @@ -88,6 +88,9 @@ export class User extends Component { {this.state.view == View.Posts && this.posts() } + {this.state.view == View.Saved && + this.overview() + } {this.paginator()}
        @@ -108,7 +111,7 @@ export class User extends Component { - {/* */} +
    @@ -200,7 +203,7 @@ export class User extends Component {
    {this.state.moderates.length > 0 &&
    -

    Moderates

    +
    Moderates
      {this.state.moderates.map(community =>
    • {community.community_name}
    • @@ -218,7 +221,7 @@ export class User extends Component { {this.state.follows.length > 0 &&

      -

      Subscribed

      +
      Subscribed
        {this.state.follows.map(community =>
      • {community.community_name}
      • @@ -257,6 +260,7 @@ export class User extends Component { let form: GetUserDetailsForm = { user_id: this.state.user_id, sort: SortType[this.state.sort], + saved_only: this.state.view == View.Saved, page: this.state.page, limit: fetchLimit, }; diff --git a/ui/src/index.html b/ui/src/index.html index 5b1f84af..efa5b969 100644 --- a/ui/src/index.html +++ b/ui/src/index.html @@ -7,9 +7,7 @@ Lemmy - - diff --git a/ui/src/index.tsx b/ui/src/index.tsx index cefcac02..d830bd3a 100644 --- a/ui/src/index.tsx +++ b/ui/src/index.tsx @@ -15,7 +15,8 @@ import { Modlog } from './components/modlog'; import { Setup } from './components/setup'; import { Symbols } from './components/symbols'; -import './main.css'; +import './css/bootstrap.min.css'; +import './css/main.css'; import { WebSocketService, UserService } from './services'; diff --git a/ui/src/interfaces.ts b/ui/src/interfaces.ts index 6affc0e1..4a4ee643 100644 --- a/ui/src/interfaces.ts +++ b/ui/src/interfaces.ts @@ -1,5 +1,5 @@ export enum UserOperation { - Login, Register, CreateCommunity, CreatePost, ListCommunities, ListCategories, GetPost, GetCommunity, CreateComment, EditComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, EditCommunity, FollowCommunity, GetFollowedCommunities, GetUserDetails, GetModlog, BanFromCommunity, AddModToCommunity, CreateSite, EditSite, GetSite, AddAdmin, BanUser + Login, Register, CreateCommunity, CreatePost, ListCommunities, ListCategories, GetPost, GetCommunity, CreateComment, EditComment, SaveComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, SavePost, EditCommunity, FollowCommunity, GetFollowedCommunities, GetUserDetails, GetModlog, BanFromCommunity, AddModToCommunity, CreateSite, EditSite, GetSite, AddAdmin, BanUser } export enum CommentSortType { @@ -41,65 +41,69 @@ export interface CommunityUser { } export interface Community { - user_id?: number; - subscribed?: boolean; - am_mod?: boolean; - removed?: boolean; id: number; name: string; title: string; description?: string; + category_id: number; creator_id: number; + removed: boolean; + published: string; + updated?: string; creator_name: string; - category_id: number; category_name: string; number_of_subscribers: number; number_of_posts: number; number_of_comments: number; - published: string; - updated?: string; + user_id?: number; + subscribed?: boolean; } export interface Post { - user_id?: number; - my_vote?: number; - am_mod?: boolean; - removed?: boolean; - locked?: boolean; id: number; name: string; url?: string; body?: string; creator_id: number; - creator_name: string; community_id: number; + removed: boolean; + locked: boolean; + published: string; + updated?: string; + creator_name: string; community_name: string; number_of_comments: number; score: number; upvotes: number; downvotes: number; hot_rank: number; - published: string; - updated?: string; + user_id?: number; + my_vote?: number; + subscribed?: boolean; + read?: boolean; + saved?: boolean; } export interface Comment { id: number; - content: string; creator_id: number; - creator_name: string; post_id: number, - community_id: number, parent_id?: number; + content: string; + removed: boolean; + read: boolean; published: string; updated?: string; + community_id: number, + banned: boolean; + banned_from_community: boolean; + creator_name: string; score: number; upvotes: number; downvotes: number; + user_id?: number; my_vote?: number; - am_mod?: boolean; - removed?: boolean; - banned?: boolean; + saved?: boolean; } export interface Category { @@ -137,7 +141,7 @@ export interface GetUserDetailsForm { page?: number; limit?: number; community_id?: number; - auth?: string; + saved_only: boolean; } export interface UserDetailsResponse { @@ -147,7 +151,6 @@ export interface UserDetailsResponse { moderates: Array; comments: Array; posts: Array; - saved?: Array; } export interface BanFromCommunityForm { @@ -324,7 +327,7 @@ export interface CommunityForm { description?: string, category_id: number, edit_id?: number; - removed?: boolean; + removed: boolean; reason?: string; expires?: number; auth?: string; @@ -367,9 +370,9 @@ export interface PostForm { updated?: number; edit_id?: number; creator_id: number; - removed?: boolean; + removed: boolean; + locked: boolean; reason?: string; - locked?: boolean; auth: string; } @@ -379,6 +382,13 @@ export interface GetPostResponse { comments: Array; community: Community; moderators: Array; + admins: Array; +} + +export interface SavePostForm { + post_id: number; + save: boolean; + auth?: string; } export interface PostResponse { @@ -392,11 +402,17 @@ export interface CommentForm { parent_id?: number; edit_id?: number; creator_id: number; - removed?: boolean; + removed: boolean; reason?: string; auth: string; } +export interface SaveCommentForm { + comment_id: number; + save: boolean; + auth?: string; +} + export interface CommentResponse { op: string; comment: Comment; diff --git a/ui/src/main.css b/ui/src/main.css deleted file mode 100644 index 3fbb6eff..00000000 --- a/ui/src/main.css +++ /dev/null @@ -1,87 +0,0 @@ -body { - font-family: 'Open Sans', sans-serif; -} - -.pointer { - cursor: pointer; -} - -.no-click { - pointer-events:none; - opacity: 0.65; -} - -.upvote:hover { - color: var(--info); -} - -.downvote:hover { - color: var(--danger); -} - -.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: #322a00; -} - -.md-div p { - margin-bottom: 0px; -} - -.md-div img { - max-width: 100%; - height: auto; -} - -.listing { - min-height: 61px; -} - -.icon { - display: inline-flex; - width: 1em; - height: 1em; - stroke-width: 0; - stroke: currentColor; - fill: currentColor; - vertical-align: middle; - align-self: center; -} - - -.spin { - animation: spins 2s linear infinite; -} - -@keyframes spins { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(359deg); } -} - -.dropdown-menu { - z-index: 2000; -} - -.navbar-bg { - background-color: #222; -} - -blockquote { - border-left: 3px solid #ccc; - margin: 0.5em 5px; - padding: 0.1em 5px; -} diff --git a/ui/src/services/WebSocketService.ts b/ui/src/services/WebSocketService.ts index 80555fd9..b2c2a9e0 100644 --- a/ui/src/services/WebSocketService.ts +++ b/ui/src/services/WebSocketService.ts @@ -1,5 +1,5 @@ import { wsUri } from '../env'; -import { LoginForm, RegisterForm, UserOperation, CommunityForm, PostForm, CommentForm, CommentLikeForm, GetPostsForm, CreatePostLikeForm, FollowCommunityForm, GetUserDetailsForm, ListCommunitiesForm, GetModlogForm, BanFromCommunityForm, AddModToCommunityForm, SiteForm, Site, UserView } from '../interfaces'; +import { LoginForm, RegisterForm, UserOperation, CommunityForm, PostForm, SavePostForm, CommentForm, SaveCommentForm, CommentLikeForm, GetPostsForm, CreatePostLikeForm, FollowCommunityForm, GetUserDetailsForm, ListCommunitiesForm, GetModlogForm, BanFromCommunityForm, AddModToCommunityForm, AddAdminForm, BanUserForm, SiteForm, Site, UserView } from '../interfaces'; import { webSocket } from 'rxjs/webSocket'; import { Subject } from 'rxjs'; import { retryWhen, delay, take } from 'rxjs/operators'; @@ -96,6 +96,11 @@ export class WebSocketService { this.subject.next(this.wsSendWrapper(UserOperation.CreateCommentLike, form)); } + public saveComment(form: SaveCommentForm) { + this.setAuth(form); + this.subject.next(this.wsSendWrapper(UserOperation.SaveComment, form)); + } + public getPosts(form: GetPostsForm) { this.setAuth(form, false); this.subject.next(this.wsSendWrapper(UserOperation.GetPosts, form)); @@ -111,6 +116,11 @@ export class WebSocketService { this.subject.next(this.wsSendWrapper(UserOperation.EditPost, postForm)); } + public savePost(form: SavePostForm) { + this.setAuth(form); + this.subject.next(this.wsSendWrapper(UserOperation.SavePost, form)); + } + public banFromCommunity(form: BanFromCommunityForm) { this.setAuth(form); this.subject.next(this.wsSendWrapper(UserOperation.BanFromCommunity, form)); @@ -121,8 +131,17 @@ export class WebSocketService { this.subject.next(this.wsSendWrapper(UserOperation.AddModToCommunity, form)); } + public banUser(form: BanUserForm) { + this.setAuth(form); + this.subject.next(this.wsSendWrapper(UserOperation.BanUser, form)); + } + + public addAdmin(form: AddAdminForm) { + this.setAuth(form); + this.subject.next(this.wsSendWrapper(UserOperation.AddAdmin, form)); + } + public getUserDetails(form: GetUserDetailsForm) { - this.setAuth(form, false); this.subject.next(this.wsSendWrapper(UserOperation.GetUserDetails, form)); } diff --git a/ui/src/utils.ts b/ui/src/utils.ts index c7f3bad8..61744e90 100644 --- a/ui/src/utils.ts +++ b/ui/src/utils.ts @@ -1,4 +1,4 @@ -import { UserOperation, Comment } from './interfaces'; +import { UserOperation, Comment, User } from './interfaces'; import * as markdown_it from 'markdown-it'; export let repoUrl = 'https://github.com/dessalines/lemmy'; @@ -40,4 +40,23 @@ export function addTypeInfo(arr: Array, name: string): Array<{type_: strin return arr.map(e => {return {type_: name, data: e}}); } +export function canMod(user: User, modIds: Array, creator_id: number): boolean { + // You can do moderator actions only on the mods added after you. + if (user) { + let yourIndex = modIds.findIndex(id => id == user.id); + if (yourIndex == -1) { + return false; + } else { + modIds = modIds.slice(0, yourIndex+1); // +1 cause you cant mod yourself + return !modIds.includes(creator_id); + } + } else { + return false; + } +} + +export function isMod(modIds: Array, creator_id: number): boolean { + return modIds.includes(creator_id); +} + export let fetchLimit: number = 20; -- cgit v1.2.3