From 8036474ddad2f20c27a2fb023395306d6b9e577d Mon Sep 17 00:00:00 2001 From: Dessalines Date: Fri, 31 Jan 2020 20:02:20 -0500 Subject: Starting to work on user message scope. --- ui/src/components/community.tsx | 55 ++++++++++++++----- ui/src/components/inbox.tsx | 1 + ui/src/components/main.tsx | 105 ++++++++++++++++++++++++++---------- ui/src/components/navbar.tsx | 53 +++++++----------- ui/src/components/post.tsx | 11 +++- ui/src/components/sidebar.tsx | 17 ++++-- ui/src/interfaces.ts | 11 ++++ ui/src/services/WebSocketService.ts | 5 +- 8 files changed, 175 insertions(+), 83 deletions(-) (limited to 'ui/src') diff --git a/ui/src/components/community.tsx b/ui/src/components/community.tsx index 3c5f6890..a88ec952 100644 --- a/ui/src/components/community.tsx +++ b/ui/src/components/community.tsx @@ -14,20 +14,17 @@ import { GetCommunityForm, ListingType, GetPostsResponse, + PostResponse, CreatePostLikeResponse, + AddModToCommunityResponse, + BanFromCommunityResponse, WebSocketJsonResponse, } from '../interfaces'; import { WebSocketService, UserService } from '../services'; import { PostListings } from './post-listings'; import { SortSelect } from './sort-select'; import { Sidebar } from './sidebar'; -import { - wsJsonToRes, - routeSortTypeToEnum, - fetchLimit, - postRefetchSeconds, - toast, -} from '../utils'; +import { wsJsonToRes, routeSortTypeToEnum, fetchLimit, toast } from '../utils'; import { T } from 'inferno-i18next'; import { i18n } from '../i18next'; @@ -37,6 +34,7 @@ interface State { communityName: string; moderators: Array; admins: Array; + online: number; loading: boolean; posts: Array; sort: SortType; @@ -67,6 +65,7 @@ export class Community extends Component { admins: [], communityId: Number(this.props.match.params.id), communityName: this.props.match.params.name, + online: null, loading: true, posts: [], sort: this.getSortTypeFromProps(this.props), @@ -158,6 +157,7 @@ export class Community extends Component { community={this.state.community} moderators={this.state.moderators} admins={this.state.admins} + online={this.state.online} /> @@ -240,11 +240,6 @@ export class Community extends Component { ); } - keepFetchingPosts() { - this.fetchPosts(); - this.postFetcher = setInterval(() => this.fetchPosts(), postRefetchSeconds); - } - fetchPosts() { let getPostsForm: GetPostsForm = { page: this.state.page, @@ -268,9 +263,10 @@ export class Community extends Component { this.state.community = data.community; this.state.moderators = data.moderators; this.state.admins = data.admins; + this.state.online = data.online; document.title = `/c/${this.state.community.name} - ${WebSocketService.Instance.site.name}`; this.setState(this.state); - this.keepFetchingPosts(); + this.fetchPosts(); } else if (res.op == UserOperation.EditCommunity) { let data = res.data as CommunityResponse; this.state.community = data.community; @@ -286,13 +282,44 @@ export class Community extends Component { this.state.posts = data.posts; this.state.loading = false; this.setState(this.state); + } else if (res.op == UserOperation.EditPost) { + let data = res.data as PostResponse; + let found = this.state.posts.find(c => c.id == data.post.id); + + found.url = data.post.url; + found.name = data.post.name; + found.nsfw = data.post.nsfw; + + this.setState(this.state); + } else if (res.op == UserOperation.CreatePost) { + let data = res.data as PostResponse; + this.state.posts.unshift(data.post); + this.setState(this.state); } else if (res.op == UserOperation.CreatePostLike) { let data = res.data as CreatePostLikeResponse; let found = this.state.posts.find(c => c.id == data.post.id); - found.my_vote = data.post.my_vote; + found.score = data.post.score; found.upvotes = data.post.upvotes; found.downvotes = data.post.downvotes; + if (data.post.my_vote !== null) { + found.my_vote = data.post.my_vote; + found.upvoteLoading = false; + found.downvoteLoading = false; + } + + this.setState(this.state); + } else if (res.op == UserOperation.AddModToCommunity) { + let data = res.data as AddModToCommunityResponse; + this.state.moderators = data.moderators; + this.setState(this.state); + } else if (res.op == UserOperation.BanFromCommunity) { + let data = res.data as BanFromCommunityResponse; + + this.state.posts + .filter(p => p.creator_id == data.user.id) + .forEach(p => (p.banned = data.banned)); + this.setState(this.state); } } diff --git a/ui/src/components/inbox.tsx b/ui/src/components/inbox.tsx index 41c1ce60..5585d84f 100644 --- a/ui/src/components/inbox.tsx +++ b/ui/src/components/inbox.tsx @@ -422,6 +422,7 @@ export class Inbox extends Component { this.setState(this.state); } else if (res.op == UserOperation.CreateComment) { // let res: CommentResponse = msg; + // TODO gotta remove this toast(i18n.t('reply_sent')); // this.state.replies.unshift(res.comment); // TODO do this right // this.setState(this.state); diff --git a/ui/src/components/main.tsx b/ui/src/components/main.tsx index 6bf4164f..9ff6af44 100644 --- a/ui/src/components/main.tsx +++ b/ui/src/components/main.tsx @@ -15,8 +15,11 @@ import { SiteResponse, GetPostsResponse, CreatePostLikeResponse, + PostResponse, Post, GetPostsForm, + AddAdminResponse, + BanUserResponse, WebSocketJsonResponse, } from '../interfaces'; import { WebSocketService, UserService } from '../services'; @@ -31,7 +34,6 @@ import { fetchLimit, routeSortTypeToEnum, routeListingTypeToEnum, - postRefetchSeconds, pictshareAvatarThumbnail, showAvatars, toast, @@ -42,7 +44,7 @@ import { T } from 'inferno-i18next'; interface MainState { subscribedCommunities: Array; trendingCommunities: Array; - site: GetSiteResponse; + siteRes: GetSiteResponse; showEditSite: boolean; loading: boolean; posts: Array; @@ -53,11 +55,10 @@ interface MainState { export class Main extends Component { private subscription: Subscription; - private postFetcher: any; private emptyState: MainState = { subscribedCommunities: [], trendingCommunities: [], - site: { + siteRes: { site: { id: null, name: null, @@ -133,12 +134,11 @@ export class Main extends Component { WebSocketService.Instance.listCommunities(listCommunitiesForm); - this.keepFetchingPosts(); + this.fetchPosts(); } componentWillUnmount() { this.subscription.unsubscribe(); - clearInterval(this.postFetcher); } // Necessary for back button for some reason @@ -241,7 +241,7 @@ export class Main extends Component { this.siteInfo() ) : ( )} @@ -262,7 +262,7 @@ export class Main extends Component {
-
{`${this.state.site.site.name}`}
+
{`${this.state.siteRes.site.name}`}
{this.canAdmin && (
  • @@ -279,7 +279,7 @@ export class Main extends Component {
  • # @@ -288,7 +288,7 @@ export class Main extends Component { # @@ -298,7 +298,7 @@ export class Main extends Component { # @@ -308,7 +308,7 @@ export class Main extends Component { # @@ -318,7 +318,7 @@ export class Main extends Component { # @@ -337,7 +337,7 @@ export class Main extends Component { :
  • - {this.state.site.admins.map(admin => ( + {this.state.siteRes.admins.map(admin => (
  • {admin.avatar && showAvatars() && ( @@ -355,13 +355,13 @@ export class Main extends Component {
- {this.state.site.site.description && ( + {this.state.siteRes.site.description && (
@@ -494,7 +494,7 @@ export class Main extends Component { get canAdmin(): boolean { return ( UserService.Instance.user && - this.state.site.admins + this.state.siteRes.admins .map(a => a.id) .includes(UserService.Instance.user.id) ); @@ -548,11 +548,6 @@ export class Main extends Component { window.scrollTo(0, 0); } - keepFetchingPosts() { - this.fetchPosts(); - this.postFetcher = setInterval(() => this.fetchPosts(), postRefetchSeconds); - } - fetchPosts() { let getPostsForm: GetPostsForm = { page: this.state.page, @@ -584,29 +579,83 @@ export class Main extends Component { if (!data.site) { this.context.router.history.push('/setup'); } - this.state.site.admins = data.admins; - this.state.site.site = data.site; - this.state.site.banned = data.banned; - this.state.site.online = data.online; + this.state.siteRes.admins = data.admins; + this.state.siteRes.site = data.site; + this.state.siteRes.banned = data.banned; + this.state.siteRes.online = data.online; this.setState(this.state); document.title = `${WebSocketService.Instance.site.name}`; } else if (res.op == UserOperation.EditSite) { let data = res.data as SiteResponse; - this.state.site.site = data.site; + this.state.siteRes.site = data.site; this.state.showEditSite = false; this.setState(this.state); } else if (res.op == UserOperation.GetPosts) { let data = res.data as GetPostsResponse; this.state.posts = data.posts; this.state.loading = false; + this.setState(this.state); + } else if (res.op == UserOperation.CreatePost) { + let data = res.data as PostResponse; + + // If you're on subscribed, only push it if you're subscribed. + if (this.state.type_ == ListingType.Subscribed) { + if ( + this.state.subscribedCommunities + .map(c => c.community_id) + .includes(data.post.community_id) + ) { + this.state.posts.unshift(data.post); + } + } else { + this.state.posts.unshift(data.post); + } + + this.setState(this.state); + } else if (res.op == UserOperation.EditPost) { + let data = res.data as PostResponse; + let found = this.state.posts.find(c => c.id == data.post.id); + + found.url = data.post.url; + found.name = data.post.name; + found.nsfw = data.post.nsfw; + this.setState(this.state); } else if (res.op == UserOperation.CreatePostLike) { let data = res.data as CreatePostLikeResponse; let found = this.state.posts.find(c => c.id == data.post.id); - found.my_vote = data.post.my_vote; + found.score = data.post.score; found.upvotes = data.post.upvotes; found.downvotes = data.post.downvotes; + if (data.post.my_vote !== null) { + found.my_vote = data.post.my_vote; + found.upvoteLoading = false; + found.downvoteLoading = false; + } + + this.setState(this.state); + } else if (res.op == UserOperation.AddAdmin) { + let data = res.data as AddAdminResponse; + this.state.siteRes.admins = data.admins; + this.setState(this.state); + } else if (res.op == UserOperation.BanUser) { + let data = res.data as BanUserResponse; + let found = this.state.siteRes.banned.find(u => (u.id = data.user.id)); + + // Remove the banned if its found in the list, and the action is an unban + if (found && !data.banned) { + this.state.siteRes.banned = this.state.siteRes.banned.filter( + i => i.id !== data.user.id + ); + } else { + this.state.siteRes.banned.push(data.user); + } + + this.state.posts + .filter(p => p.creator_id == data.user.id) + .forEach(p => (p.banned = data.banned)); + this.setState(this.state); } } diff --git a/ui/src/components/navbar.tsx b/ui/src/components/navbar.tsx index 1828fce9..d433ad1f 100644 --- a/ui/src/components/navbar.tsx +++ b/ui/src/components/navbar.tsx @@ -14,6 +14,7 @@ import { SortType, GetSiteResponse, Comment, + CommentResponse, PrivateMessage, WebSocketJsonResponse, } from '../interfaces'; @@ -58,7 +59,7 @@ export class Navbar extends Component { super(props, context); this.state = this.emptyState; - this.keepFetchingUnreads(); + this.fetchUnreads(); // Subscribe to user changes this.userSub = UserService.Instance.sub.subscribe(user => { @@ -211,13 +212,6 @@ export class Navbar extends Component { } else if (res.op == UserOperation.GetReplies) { let data = res.data as GetRepliesResponse; let unreadReplies = data.replies.filter(r => !r.read); - if ( - unreadReplies.length > 0 && - this.state.fetchCount > 1 && - JSON.stringify(this.state.replies) !== JSON.stringify(unreadReplies) - ) { - this.notify(unreadReplies); - } this.state.replies = unreadReplies; this.setState(this.state); @@ -225,13 +219,6 @@ export class Navbar extends Component { } else if (res.op == UserOperation.GetUserMentions) { let data = res.data as GetUserMentionsResponse; let unreadMentions = data.mentions.filter(r => !r.read); - if ( - unreadMentions.length > 0 && - this.state.fetchCount > 1 && - JSON.stringify(this.state.mentions) !== JSON.stringify(unreadMentions) - ) { - this.notify(unreadMentions); - } this.state.mentions = unreadMentions; this.setState(this.state); @@ -239,17 +226,19 @@ export class Navbar extends Component { } else if (res.op == UserOperation.GetPrivateMessages) { let data = res.data as PrivateMessagesResponse; let unreadMessages = data.messages.filter(r => !r.read); - if ( - unreadMessages.length > 0 && - this.state.fetchCount > 1 && - JSON.stringify(this.state.messages) !== JSON.stringify(unreadMessages) - ) { - this.notify(unreadMessages); - } this.state.messages = unreadMessages; this.setState(this.state); this.sendUnreadCount(); + } else if (res.op == UserOperation.CreateComment) { + // TODO do private messages too + let data = res.data as CommentResponse; + + if (UserService.Instance.user) { + if (data.recipient_ids.includes(UserService.Instance.user.id)) { + this.notify(data.comment); + } + } } else if (res.op == UserOperation.GetSite) { let data = res.data as GetSiteResponse; @@ -261,11 +250,6 @@ export class Navbar extends Component { } } - keepFetchingUnreads() { - this.fetchUnreads(); - setInterval(() => this.fetchUnreads(), 15000); - } - fetchUnreads() { if (this.state.isLoggedIn) { let repliesForm: GetRepliesForm = { @@ -330,24 +314,23 @@ export class Navbar extends Component { } } - notify(replies: Array) { - let recentReply = replies[0]; + notify(reply: Comment | PrivateMessage) { if (Notification.permission !== 'granted') Notification.requestPermission(); else { var notification = new Notification( - `${replies.length} ${i18n.t('unread_messages')}`, + `${this.state.unreadCount} ${i18n.t('unread_messages')}`, { - icon: recentReply.creator_avatar - ? recentReply.creator_avatar + icon: reply.creator_avatar + ? reply.creator_avatar : `${window.location.protocol}//${window.location.host}/static/assets/apple-touch-icon.png`, - body: `${recentReply.creator_name}: ${recentReply.content}`, + body: `${reply.creator_name}: ${reply.content}`, } ); notification.onclick = () => { this.context.router.history.push( - isCommentType(recentReply) - ? `/post/${recentReply.post_id}/comment/${recentReply.id}` + isCommentType(reply) + ? `/post/${reply.post_id}/comment/${reply.id}` : `/inbox` ); }; diff --git a/ui/src/components/post.tsx b/ui/src/components/post.tsx index 36621248..99a20b46 100644 --- a/ui/src/components/post.tsx +++ b/ui/src/components/post.tsx @@ -47,6 +47,7 @@ interface PostState { community: Community; moderators: Array; admins: Array; + online: number; scrolled?: boolean; scrolled_comment_id?: number; loading: boolean; @@ -62,6 +63,7 @@ export class Post extends Component { community: null, moderators: [], admins: [], + online: null, scrolled: false, loading: true, crossPosts: [], @@ -280,6 +282,7 @@ export class Post extends Component { community={this.state.community} moderators={this.state.moderators} admins={this.state.admins} + online={this.state.online} />
); @@ -378,6 +381,7 @@ export class Post extends Component { this.state.community = data.community; this.state.moderators = data.moderators; this.state.admins = data.admins; + this.state.online = data.online; this.state.loading = false; document.title = `${this.state.post.name} - ${WebSocketService.Instance.site.name}`; @@ -432,10 +436,15 @@ export class Post extends Component { this.setState(this.state); } else if (res.op == UserOperation.CreatePostLike) { let data = res.data as CreatePostLikeResponse; - this.state.post.my_vote = data.post.my_vote; this.state.post.score = data.post.score; this.state.post.upvotes = data.post.upvotes; this.state.post.downvotes = data.post.downvotes; + if (data.post.my_vote !== null) { + this.state.post.my_vote = data.post.my_vote; + this.state.post.upvoteLoading = false; + this.state.post.downvoteLoading = false; + } + this.setState(this.state); } else if (res.op == UserOperation.EditPost) { let data = res.data as PostResponse; diff --git a/ui/src/components/sidebar.tsx b/ui/src/components/sidebar.tsx index 089dd558..0e6b68e6 100644 --- a/ui/src/components/sidebar.tsx +++ b/ui/src/components/sidebar.tsx @@ -22,6 +22,7 @@ interface SidebarProps { community: Community; moderators: Array; admins: Array; + online: number; } interface SidebarState { @@ -156,10 +157,13 @@ export class Sidebar extends Component { )}
    -
  • - - {community.category_name} - +
  • + + # +
  • { #
  • +
  • + + {community.category_name} + +
  • ; admins: Array; + online: number; } export interface CommunityResponse { @@ -595,6 +596,7 @@ export interface GetPostResponse { community: Community; moderators: Array; admins: Array; + online: number; } export interface SavePostForm { @@ -628,6 +630,7 @@ export interface SaveCommentForm { export interface CommentResponse { comment: Comment; + recipient_ids: Array; } export interface CommentLikeForm { @@ -776,6 +779,14 @@ export interface PrivateMessageResponse { message: PrivateMessage; } +export interface UserJoinForm { + auth: string; +} + +export interface UserJoinResponse { + user_id: number; +} + export type MessageType = | EditPrivateMessageForm | LoginForm diff --git a/ui/src/services/WebSocketService.ts b/ui/src/services/WebSocketService.ts index 3ad0de44..f3d40149 100644 --- a/ui/src/services/WebSocketService.ts +++ b/ui/src/services/WebSocketService.ts @@ -38,6 +38,7 @@ import { PrivateMessageForm, EditPrivateMessageForm, GetPrivateMessagesForm, + UserJoinForm, MessageType, } from '../interfaces'; import { webSocket } from 'rxjs/webSocket'; @@ -73,6 +74,7 @@ export class WebSocketService { console.log(`Connected to ${wsUri}`); if (UserService.Instance.user) { + this.userJoin(); } } @@ -81,7 +83,8 @@ export class WebSocketService { } public userJoin() { - this.wsSendWrapper(UserOperation.ListCategories, undefined); + let form: UserJoinForm = { auth: UserService.Instance.auth }; + this.subject.next(this.wsSendWrapper(UserOperation.UserJoin, form)); } public login(loginForm: LoginForm) { -- cgit v1.2.3