diff options
author | Dessalines <tyhou13@gmx.com> | 2019-04-05 12:14:54 -0700 |
---|---|---|
committer | Dessalines <tyhou13@gmx.com> | 2019-04-05 12:14:54 -0700 |
commit | 38fa7969f95f49f20074ad8b982d8d92923071ad (patch) | |
tree | 2de5f93884ff6b315dcdab48a43662b85f1a0d85 | |
parent | 2c66d86e2686c09e56d7b65ab679ce929d499478 (diff) |
Adding a front page / fetching subscribed forums.
- Adding subscribed to post view. Fixes #25
-rw-r--r-- | server/Cargo.lock | 2 | ||||
-rw-r--r-- | server/migrations/2019-03-30-212058_create_post_view/up.sql | 8 | ||||
-rw-r--r-- | server/src/actions/community_view.rs | 22 | ||||
-rw-r--r-- | server/src/actions/post_view.rs | 9 | ||||
-rw-r--r-- | server/src/websocket_server/server.rs | 50 | ||||
-rw-r--r-- | ui/src/components/communities.tsx | 4 | ||||
-rw-r--r-- | ui/src/components/community.tsx | 70 | ||||
-rw-r--r-- | ui/src/components/home.tsx | 5 | ||||
-rw-r--r-- | ui/src/components/main.tsx | 85 | ||||
-rw-r--r-- | ui/src/components/post-listing.tsx | 8 | ||||
-rw-r--r-- | ui/src/components/post-listings.tsx | 167 | ||||
-rw-r--r-- | ui/src/index.html | 2 | ||||
-rw-r--r-- | ui/src/interfaces.ts | 7 | ||||
-rw-r--r-- | ui/src/services/WebSocketService.ts | 5 |
14 files changed, 362 insertions, 82 deletions
diff --git a/server/Cargo.lock b/server/Cargo.lock index 21594ccf..0527eae7 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -1,3 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. [[package]] name = "activitypub" version = "0.1.4" diff --git a/server/migrations/2019-03-30-212058_create_post_view/up.sql b/server/migrations/2019-03-30-212058_create_post_view/up.sql index c1848631..79084a47 100644 --- a/server/migrations/2019-03-30-212058_create_post_view/up.sql +++ b/server/migrations/2019-03-30-212058_create_post_view/up.sql @@ -14,7 +14,7 @@ with all_post as ( select p.*, - (select name from user_ where p.creator_id = user_.id) creator_name, + (select name from user_ where p.creator_id = user_.id) as creator_name, (select name from community where p.community_id = community.id) as community_name, (select count(*) from comment where comment.post_id = p.id) as number_of_comments, coalesce(sum(pl.score), 0) as score, @@ -29,7 +29,8 @@ with all_post as select ap.*, u.id as user_id, -coalesce(pl.score, 0) as my_vote +coalesce(pl.score, 0) as my_vote, +(select cf.id::bool from community_follower cf where u.id = cf.user_id and cf.community_id = ap.community_id) as subscribed from user_ u cross join all_post ap left join post_like pl on u.id = pl.user_id and ap.id = pl.post_id @@ -39,7 +40,8 @@ union all select ap.*, null as user_id, -null as my_vote +null as my_vote, +null as subscribed from all_post ap ; diff --git a/server/src/actions/community_view.rs b/server/src/actions/community_view.rs index 7eb07a16..185484bf 100644 --- a/server/src/actions/community_view.rs +++ b/server/src/actions/community_view.rs @@ -124,3 +124,25 @@ impl CommunityModeratorView { } } +#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize,QueryableByName,Clone)] +#[table_name="community_follower_view"] +pub struct CommunityFollowerView { + pub id: i32, + pub community_id: i32, + pub user_id: i32, + pub published: chrono::NaiveDateTime, + pub user_name : String, + pub community_name: String, +} + +impl CommunityFollowerView { + pub fn for_community(conn: &PgConnection, from_community_id: i32) -> Result<Vec<Self>, Error> { + use actions::community_view::community_follower_view::dsl::*; + community_follower_view.filter(community_id.eq(from_community_id)).load::<Self>(conn) + } + + pub fn for_user(conn: &PgConnection, from_user_id: i32) -> Result<Vec<Self>, Error> { + use actions::community_view::community_follower_view::dsl::*; + community_follower_view.filter(user_id.eq(from_user_id)).load::<Self>(conn) + } +} diff --git a/server/src/actions/post_view.rs b/server/src/actions/post_view.rs index c48c651e..b41a77ae 100644 --- a/server/src/actions/post_view.rs +++ b/server/src/actions/post_view.rs @@ -33,6 +33,7 @@ table! { hot_rank -> Int4, user_id -> Nullable<Int4>, my_vote -> Nullable<Int4>, + subscribed -> Nullable<Bool>, } } @@ -57,6 +58,7 @@ pub struct PostView { pub hot_rank: i32, pub user_id: Option<i32>, pub my_vote: Option<i32>, + pub subscribed: Option<bool>, } impl PostView { @@ -71,6 +73,13 @@ impl PostView { query = query.filter(community_id.eq(from_community_id)); }; + match type_ { + ListingType::Subscribed => { + query = query.filter(subscribed.eq(true)); + }, + _ => {} + }; + // 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)); diff --git a/server/src/websocket_server/server.rs b/server/src/websocket_server/server.rs index fe7cd0e6..6aae4f2f 100644 --- a/server/src/websocket_server/server.rs +++ b/server/src/websocket_server/server.rs @@ -22,7 +22,7 @@ use actions::community_view::*; #[derive(EnumString,ToString,Debug)] pub enum UserOperation { - Login, Register, CreateCommunity, CreatePost, ListCommunities, ListCategories, GetPost, GetCommunity, CreateComment, EditComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, EditCommunity, FollowCommunity + Login, Register, CreateCommunity, CreatePost, ListCommunities, ListCategories, GetPost, GetCommunity, CreateComment, EditComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, EditCommunity, FollowCommunity, GetFollowedCommunities } #[derive(Serialize, Deserialize)] @@ -261,6 +261,18 @@ pub struct FollowCommunity { auth: String } +#[derive(Serialize, Deserialize)] +pub struct GetFollowedCommunities { + auth: String +} + +#[derive(Serialize, Deserialize)] +pub struct GetFollowedCommunitiesResponse { + op: String, + communities: Vec<CommunityFollowerView> +} + + /// `ChatServer` manages chat rooms and responsible for coordinating chat /// session. implementation is super primitive pub struct ChatServer { @@ -450,6 +462,10 @@ impl Handler<StandardMessage> for ChatServer { let follow_community: FollowCommunity = serde_json::from_str(&data.to_string()).unwrap(); follow_community.perform(self, msg.id) }, + UserOperation::GetFollowedCommunities => { + let followed_communities: GetFollowedCommunities = serde_json::from_str(&data.to_string()).unwrap(); + followed_communities.perform(self, msg.id) + }, _ => { let e = ErrorMessage { op: "Unknown".to_string(), @@ -1081,8 +1097,6 @@ impl Perform for GetPosts { let conn = establish_connection(); - println!("{:?}", self.auth); - let user_id: Option<i32> = match &self.auth { Some(auth) => { match Claims::decode(&auth) { @@ -1367,6 +1381,36 @@ impl Perform for FollowCommunity { } } +impl Perform for GetFollowedCommunities { + fn op_type(&self) -> UserOperation { + UserOperation::GetFollowedCommunities + } + + fn perform(&self, _chat: &mut ChatServer, _addr: usize) -> String { + + let conn = establish_connection(); + + let claims = match Claims::decode(&self.auth) { + Ok(claims) => claims.claims, + Err(_e) => { + return self.error("Not logged in."); + } + }; + + let user_id = claims.id; + + let communities: Vec<CommunityFollowerView> = CommunityFollowerView::for_user(&conn, user_id).unwrap(); + + // Return the jwt + serde_json::to_string( + &GetFollowedCommunitiesResponse { + op: self.op_type().to_string(), + communities: communities + } + ) + .unwrap() + } +} // impl Handler<Login> for ChatServer { diff --git a/ui/src/components/communities.tsx b/ui/src/components/communities.tsx index e8158a36..c3cde177 100644 --- a/ui/src/components/communities.tsx +++ b/ui/src/components/communities.tsx @@ -42,8 +42,8 @@ export class Communities extends Component<any, CommunitiesState> { <div class="container-fluid"> <h4>Communities</h4> <div class="table-responsive"> - <table id="community_table" class="table table-sm table-hover" data-sortable> - <thead> + <table id="community_table" class="table table-sm table-hover"> + <thead class="pointer"> <tr> <th>Name</th> <th>Title</th> diff --git a/ui/src/components/community.tsx b/ui/src/components/community.tsx index 726055ba..0d6d353d 100644 --- a/ui/src/components/community.tsx +++ b/ui/src/components/community.tsx @@ -5,15 +5,14 @@ import { retryWhen, delay, take } from 'rxjs/operators'; import { UserOperation, Community as CommunityI, GetCommunityResponse, CommunityResponse, Post, GetPostsForm, ListingSortType, ListingType, GetPostsResponse, CreatePostLikeForm, CreatePostLikeResponse, CommunityUser} from '../interfaces'; import { WebSocketService, UserService } from '../services'; import { MomentTime } from './moment-time'; -import { PostListing } from './post-listing'; +import { PostListings } from './post-listings'; import { Sidebar } from './sidebar'; import { msgOp, mdToHtml } from '../utils'; interface State { community: CommunityI; + communityId: number; moderators: Array<CommunityUser>; - posts: Array<Post>; - sortType: ListingSortType; } export class Community extends Component<any, State> { @@ -34,8 +33,7 @@ export class Community extends Component<any, State> { published: null }, moderators: [], - posts: [], - sortType: ListingSortType.Hot, + communityId: Number(this.props.match.params.id) } constructor(props, context) { @@ -51,16 +49,7 @@ export class Community extends Component<any, State> { () => console.log('complete') ); - let communityId = Number(this.props.match.params.id); - WebSocketService.Instance.getCommunity(communityId); - - let getPostsForm: GetPostsForm = { - community_id: communityId, - limit: 10, - sort: ListingSortType[ListingSortType.Hot], - type_: ListingType[ListingType.Community] - } - WebSocketService.Instance.getPosts(getPostsForm); + WebSocketService.Instance.getCommunity(this.state.communityId); } componentWillUnmount() { @@ -73,12 +62,7 @@ export class Community extends Component<any, State> { <div class="row"> <div class="col-12 col-lg-9"> <h4>/f/{this.state.community.name}</h4> - <div>{this.selects()}</div> - {this.state.posts.length > 0 - ? this.state.posts.map(post => - <PostListing post={post} />) - : <div>no listings</div> - } + <PostListings communityId={this.state.communityId} /> </div> <div class="col-12 col-lg-3"> <Sidebar community={this.state.community} moderators={this.state.moderators} /> @@ -88,37 +72,6 @@ export class Community extends Component<any, State> { ) } - selects() { - return ( - <div className="mb-2"> - <select value={this.state.sortType} onChange={linkEvent(this, this.handleSortChange)} class="custom-select w-auto"> - <option disabled>Sort Type</option> - <option value={ListingSortType.Hot}>Hot</option> - <option value={ListingSortType.New}>New</option> - <option disabled>──────────</option> - <option value={ListingSortType.TopDay}>Top Day</option> - <option value={ListingSortType.TopWeek}>Week</option> - <option value={ListingSortType.TopMonth}>Month</option> - <option value={ListingSortType.TopYear}>Year</option> - <option value={ListingSortType.TopAll}>All</option> - </select> - </div> - ) - - } - - handleSortChange(i: Community, event) { - i.state.sortType = Number(event.target.value); - i.setState(i.state); - - let getPostsForm: GetPostsForm = { - community_id: i.state.community.id, - limit: 10, - sort: ListingSortType[i.state.sortType], - type_: ListingType[ListingType.Community] - } - WebSocketService.Instance.getPosts(getPostsForm); - } parseMessage(msg: any) { console.log(msg); @@ -131,18 +84,6 @@ export class Community extends Component<any, State> { this.state.community = res.community; this.state.moderators = res.moderators; this.setState(this.state); - } else if (op == UserOperation.GetPosts) { - let res: GetPostsResponse = msg; - this.state.posts = res.posts; - this.setState(this.state); - } else if (op == UserOperation.CreatePostLike) { - let res: CreatePostLikeResponse = msg; - let found = this.state.posts.find(c => c.id == res.post.id); - found.my_vote = res.post.my_vote; - found.score = res.post.score; - found.upvotes = res.post.upvotes; - found.downvotes = res.post.downvotes; - this.setState(this.state); } else if (op == UserOperation.EditCommunity) { let res: CommunityResponse = msg; this.state.community = res.community; @@ -156,4 +97,3 @@ export class Community extends Component<any, State> { } } - diff --git a/ui/src/components/home.tsx b/ui/src/components/home.tsx index 07cb94f5..356534f7 100644 --- a/ui/src/components/home.tsx +++ b/ui/src/components/home.tsx @@ -1,13 +1,12 @@ import { Component } from 'inferno'; import { repoUrl } from '../utils'; +import { Main } from './main'; export class Home extends Component<any, any> { render() { return ( - <div class="container"> - hola this is me. - </div> + <Main /> ) } diff --git a/ui/src/components/main.tsx b/ui/src/components/main.tsx new file mode 100644 index 00000000..b7b0a562 --- /dev/null +++ b/ui/src/components/main.tsx @@ -0,0 +1,85 @@ +import { Component, linkEvent } from 'inferno'; +import { Link } from 'inferno-router'; +import { Subscription } from "rxjs"; +import { retryWhen, delay, take } from 'rxjs/operators'; +import { UserOperation, Community as CommunityI, GetCommunityResponse, CommunityResponse, Post, GetPostsForm, ListingSortType, ListingType, GetPostsResponse, CreatePostLikeForm, CreatePostLikeResponse, CommunityUser, GetFollowedCommunitiesResponse } from '../interfaces'; +import { WebSocketService, UserService } from '../services'; +import { MomentTime } from './moment-time'; +import { PostListings } from './post-listings'; +import { Sidebar } from './sidebar'; +import { msgOp, mdToHtml } from '../utils'; + +interface State { + subscribedCommunities: Array<CommunityUser>; +} + +export class Main extends Component<any, State> { + + private subscription: Subscription; + private emptyState: State = { + subscribedCommunities: [] + } + + constructor(props, context) { + super(props, context); + + this.state = this.emptyState; + + this.subscription = WebSocketService.Instance.subject + .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10)))) + .subscribe( + (msg) => this.parseMessage(msg), + (err) => console.error(err), + () => console.log('complete') + ); + + if (UserService.Instance.loggedIn) { + WebSocketService.Instance.getFollowedCommunities(); + } + } + + componentWillUnmount() { + this.subscription.unsubscribe(); + } + + render() { + return ( + <div class="container"> + <div class="row"> + <div class="col-12 col-lg-9"> + <PostListings /> + </div> + <div class="col-12 col-lg-3"> + <h4>A Landing message</h4> + {UserService.Instance.loggedIn && + <div> + <hr /> + <h4>Subscribed forums</h4> + <ul class="list-unstyled"> + {this.state.subscribedCommunities.map(community => + <li><Link to={`/community/${community.community_id}`}>{community.community_name}</Link></li> + )} + </ul> + </div> + } + </div> + </div> + </div> + ) + } + + + parseMessage(msg: any) { + console.log(msg); + let op: UserOperation = msgOp(msg); + if (msg.error) { + alert(msg.error); + return; + } else if (op == UserOperation.GetFollowedCommunities) { + let res: GetFollowedCommunitiesResponse = msg; + this.state.subscribedCommunities = res.communities; + this.setState(this.state); + } + } +} + diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx index 516baad3..c5052efb 100644 --- a/ui/src/components/post-listing.tsx +++ b/ui/src/components/post-listing.tsx @@ -59,8 +59,8 @@ export class PostListing extends Component<PostListingProps, PostListingState> { </div> <div className="ml-4"> {post.url - ? <h4 className="mb-0"> - <a className="text-white" href={post.url}>{post.name}</a> + ? <div className="mb-0"> + <h4 className="d-inline"><a className="text-white" href={post.url}>{post.name}</a></h4> <small><a className="ml-2 text-muted font-italic" href={post.url}>{(new URL(post.url)).hostname}</a></small> { !this.state.iframeExpanded ? <span class="pointer ml-2 text-muted small" title="Expand here" onClick={linkEvent(this, this.handleIframeExpandClick)}>+</span> @@ -72,7 +72,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> { </div> </span> } - </h4> + </div> : <h4 className="mb-0"><Link className="text-white" to={`/post/${post.id}`}>{post.name}</Link></h4> } </div> @@ -80,7 +80,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> { <ul class="list-inline mb-0 text-muted small"> <li className="list-inline-item"> <span>by </span> - <a href={post.creator_id.toString()}>{post.creator_name}</a> + <Link to={`/user/${post.creator_id}`}>{post.creator_name}</Link> {this.props.showCommunity && <span> <span> to </span> diff --git a/ui/src/components/post-listings.tsx b/ui/src/components/post-listings.tsx new file mode 100644 index 00000000..fcc41cf5 --- /dev/null +++ b/ui/src/components/post-listings.tsx @@ -0,0 +1,167 @@ +import { Component, linkEvent } from 'inferno'; +import { Link } from 'inferno-router'; +import { Subscription } from "rxjs"; +import { retryWhen, delay, take } from 'rxjs/operators'; +import { UserOperation, Community as CommunityI, GetCommunityResponse, CommunityResponse, Post, GetPostsForm, ListingSortType, ListingType, GetPostsResponse, CreatePostLikeForm, CreatePostLikeResponse, CommunityUser} from '../interfaces'; +import { WebSocketService, UserService } from '../services'; +import { MomentTime } from './moment-time'; +import { PostListing } from './post-listing'; +import { Sidebar } from './sidebar'; +import { msgOp, mdToHtml } from '../utils'; + + +interface PostListingsProps { + communityId?: number; +} + +interface PostListingsState { + community: CommunityI; + moderators: Array<CommunityUser>; + posts: Array<Post>; + sortType: ListingSortType; + type_: ListingType; +} + +export class PostListings extends Component<PostListingsProps, PostListingsState> { + + private subscription: Subscription; + private emptyState: PostListingsState = { + community: { + id: null, + name: null, + title: null, + category_id: null, + category_name: null, + creator_id: null, + creator_name: null, + number_of_subscribers: null, + number_of_posts: null, + number_of_comments: null, + published: null + }, + moderators: [], + posts: [], + sortType: ListingSortType.Hot, + type_: this.props.communityId + ? ListingType.Community + : UserService.Instance.loggedIn + ? ListingType.Subscribed + : ListingType.All + } + + constructor(props, context) { + super(props, context); + + + this.state = this.emptyState; + + this.subscription = WebSocketService.Instance.subject + .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10)))) + .subscribe( + (msg) => this.parseMessage(msg), + (err) => console.error(err), + () => console.log('complete') + ); + + let getPostsForm: GetPostsForm = { + type_: ListingType[this.state.type_], + community_id: this.props.communityId, + limit: 10, + sort: ListingSortType[ListingSortType.Hot], + } + WebSocketService.Instance.getPosts(getPostsForm); + } + + componentWillUnmount() { + this.subscription.unsubscribe(); + } + + render() { + return ( + <div> + <div>{this.selects()}</div> + {this.state.posts.length > 0 + ? this.state.posts.map(post => + <PostListing post={post} showCommunity={!this.props.communityId}/>) + : <div>No Listings</div> + } + </div> + ) + } + + selects() { + return ( + <div className="mb-2"> + <select value={this.state.sortType} onChange={linkEvent(this, this.handleSortChange)} class="custom-select w-auto"> + <option disabled>Sort Type</option> + <option value={ListingSortType.Hot}>Hot</option> + <option value={ListingSortType.New}>New</option> + <option disabled>──────────</option> + <option value={ListingSortType.TopDay}>Top Day</option> + <option value={ListingSortType.TopWeek}>Week</option> + <option value={ListingSortType.TopMonth}>Month</option> + <option value={ListingSortType.TopYear}>Year</option> + <option value={ListingSortType.TopAll}>All</option> + </select> + {!this.props.communityId && + UserService.Instance.loggedIn && + <select value={this.state.type_} onChange={linkEvent(this, this.handleTypeChange)} class="ml-2 custom-select w-auto"> + <option disabled>Type</option> + <option value={ListingType.All}>All</option> + <option value={ListingType.Subscribed}>Subscribed</option> + </select> + + } + </div> + ) + + } + + handleSortChange(i: PostListings, event) { + i.state.sortType = Number(event.target.value); + i.setState(i.state); + + let getPostsForm: GetPostsForm = { + community_id: i.state.community.id, + limit: 10, + sort: ListingSortType[i.state.sortType], + type_: ListingType[ListingType.Community] + } + WebSocketService.Instance.getPosts(getPostsForm); + } + + handleTypeChange(i: PostListings, event) { + i.state.type_ = Number(event.target.value); + i.setState(i.state); + + let getPostsForm: GetPostsForm = { + limit: 10, + sort: ListingSortType[i.state.sortType], + type_: ListingType[i.state.type_] + } + WebSocketService.Instance.getPosts(getPostsForm); + } + + parseMessage(msg: any) { + console.log(msg); + let op: UserOperation = msgOp(msg); + if (msg.error) { + alert(msg.error); + return; + } else if (op == UserOperation.GetPosts) { + let res: GetPostsResponse = msg; + this.state.posts = res.posts; + this.setState(this.state); + } else if (op == UserOperation.CreatePostLike) { + let res: CreatePostLikeResponse = msg; + let found = this.state.posts.find(c => c.id == res.post.id); + found.my_vote = res.post.my_vote; + found.score = res.post.score; + found.upvotes = res.post.upvotes; + found.downvotes = res.post.downvotes; + this.setState(this.state); + } + } +} + + diff --git a/ui/src/index.html b/ui/src/index.html index 8b6fccf2..2b79ac1c 100644 --- a/ui/src/index.html +++ b/ui/src/index.html @@ -10,7 +10,7 @@ <link rel="stylesheet" href="https://bootswatch.com/4/darkly/bootstrap.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/balloon-css/0.5.0/balloon.min.css"> <link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,400i,700,800" rel="stylesheet"> - <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/sortable/0.8.0/css/sortable-theme-minimal.min.css" /> + <!-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/sortable/0.8.0/css/sortable-theme-minimal.min.css" /> --> <script src="https://cdnjs.cloudflare.com/ajax/libs/sortable/0.8.0/js/sortable.min.js"></script> </head> diff --git a/ui/src/interfaces.ts b/ui/src/interfaces.ts index f8007cba..6d314c62 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 + Login, Register, CreateCommunity, CreatePost, ListCommunities, ListCategories, GetPost, GetCommunity, CreateComment, EditComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, EditCommunity, FollowCommunity, GetFollowedCommunities } export interface User { @@ -179,6 +179,11 @@ export interface FollowCommunityForm { auth?: string; } +export interface GetFollowedCommunitiesResponse { + op: string; + communities: Array<CommunityUser>; +} + export interface LoginForm { username_or_email: string; password: string; diff --git a/ui/src/services/WebSocketService.ts b/ui/src/services/WebSocketService.ts index c8cc9557..79f6750a 100644 --- a/ui/src/services/WebSocketService.ts +++ b/ui/src/services/WebSocketService.ts @@ -52,6 +52,11 @@ export class WebSocketService { this.subject.next(this.wsSendWrapper(UserOperation.ListCommunities, data)); } + public getFollowedCommunities() { + let data = {auth: UserService.Instance.auth }; + this.subject.next(this.wsSendWrapper(UserOperation.GetFollowedCommunities, data)); + } + public listCategories() { this.subject.next(this.wsSendWrapper(UserOperation.ListCategories, undefined)); } |