summaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
authorDessalines <tyhou13@gmx.com>2019-04-08 14:46:09 -0700
committerDessalines <tyhou13@gmx.com>2019-04-08 14:46:09 -0700
commit70ff6381039eba97a63035392f87056a023ea36b (patch)
tree62f4c367b3644ffa166c0a980ae2ec5fdb5bde95 /ui
parent7194289f14f2a4bb7d4419ae49541786425b5aa1 (diff)
Loading indicators.
- Fixes #10 - Adding SVG symbols component
Diffstat (limited to 'ui')
-rw-r--r--ui/src/components/communities.tsx80
-rw-r--r--ui/src/components/community-form.tsx23
-rw-r--r--ui/src/components/community.tsx16
-rw-r--r--ui/src/components/login.tsx30
-rw-r--r--ui/src/components/main.tsx32
-rw-r--r--ui/src/components/post-form.tsx23
-rw-r--r--ui/src/components/post-listings.tsx30
-rw-r--r--ui/src/components/post.tsx10
-rw-r--r--ui/src/components/symbols.tsx80
-rw-r--r--ui/src/index.tsx59
-rw-r--r--ui/src/main.css12
11 files changed, 255 insertions, 140 deletions
diff --git a/ui/src/components/communities.tsx b/ui/src/components/communities.tsx
index b81b11cd..cf42238e 100644
--- a/ui/src/components/communities.tsx
+++ b/ui/src/components/communities.tsx
@@ -10,24 +10,26 @@ declare const Sortable: any;
interface CommunitiesState {
communities: Array<Community>;
+ loading: boolean;
}
export class Communities extends Component<any, CommunitiesState> {
private subscription: Subscription;
private emptyState: CommunitiesState = {
- communities: []
+ communities: [],
+ loading: true
}
constructor(props: any, context: any) {
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),
+ .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
+ .subscribe(
+ (msg) => this.parseMessage(msg),
(err) => console.error(err),
() => console.log('complete')
- );
+ );
WebSocketService.Instance.listCommunities();
}
@@ -44,40 +46,45 @@ export class Communities extends Component<any, CommunitiesState> {
render() {
return (
<div class="container-fluid">
- <h4>Communities</h4>
- <div class="table-responsive">
- <table id="community_table" class="table table-sm table-hover">
- <thead class="pointer">
- <tr>
- <th>Name</th>
- <th>Title</th>
- <th>Category</th>
- <th class="text-right">Subscribers</th>
- <th class="text-right">Posts</th>
- <th class="text-right">Comments</th>
- <th></th>
- </tr>
- </thead>
- <tbody>
- {this.state.communities.map(community =>
+ {this.state.loading ?
+ <h4 class=""><svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg></h4> :
+ <div>
+ <h4>Communities</h4>
+ <div class="table-responsive">
+ <table id="community_table" class="table table-sm table-hover">
+ <thead class="pointer">
<tr>
- <td><Link to={`/community/${community.id}`}>{community.name}</Link></td>
- <td>{community.title}</td>
- <td>{community.category_name}</td>
- <td class="text-right">{community.number_of_subscribers}</td>
- <td class="text-right">{community.number_of_posts}</td>
- <td class="text-right">{community.number_of_comments}</td>
- <td class="text-right">
- {community.subscribed
- ? <button class="btn btn-sm btn-secondary" onClick={linkEvent(community.id, this.handleUnsubscribe)}>Unsubscribe</button>
- : <button class="btn btn-sm btn-secondary" onClick={linkEvent(community.id, this.handleSubscribe)}>Subscribe</button>
- }
- </td>
+ <th>Name</th>
+ <th>Title</th>
+ <th>Category</th>
+ <th class="text-right">Subscribers</th>
+ <th class="text-right">Posts</th>
+ <th class="text-right">Comments</th>
+ <th></th>
</tr>
- )}
- </tbody>
- </table>
+ </thead>
+ <tbody>
+ {this.state.communities.map(community =>
+ <tr>
+ <td><Link to={`/community/${community.id}`}>{community.name}</Link></td>
+ <td>{community.title}</td>
+ <td>{community.category_name}</td>
+ <td class="text-right">{community.number_of_subscribers}</td>
+ <td class="text-right">{community.number_of_posts}</td>
+ <td class="text-right">{community.number_of_comments}</td>
+ <td class="text-right">
+ {community.subscribed ?
+ <button class="btn btn-sm btn-secondary" onClick={linkEvent(community.id, this.handleUnsubscribe)}>Unsubscribe</button> :
+ <button class="btn btn-sm btn-secondary" onClick={linkEvent(community.id, this.handleSubscribe)}>Subscribe</button>
+ }
+ </td>
+ </tr>
+ )}
+ </tbody>
+ </table>
+ </div>
</div>
+ }
</div>
);
}
@@ -109,6 +116,7 @@ export class Communities extends Component<any, CommunitiesState> {
let res: ListCommunitiesResponse = msg;
this.state.communities = res.communities;
this.state.communities.sort((a, b) => b.number_of_subscribers - a.number_of_subscribers);
+ this.state.loading = false;
this.setState(this.state);
} else if (op == UserOperation.FollowCommunity) {
let res: CommunityResponse = msg;
diff --git a/ui/src/components/community-form.tsx b/ui/src/components/community-form.tsx
index 6250ef9c..b5b222c6 100644
--- a/ui/src/components/community-form.tsx
+++ b/ui/src/components/community-form.tsx
@@ -4,6 +4,7 @@ import { retryWhen, delay, take } from 'rxjs/operators';
import { CommunityForm as CommunityFormI, UserOperation, Category, ListCategoriesResponse, CommunityResponse } from '../interfaces';
import { WebSocketService } from '../services';
import { msgOp } from '../utils';
+import * as autosize from 'autosize';
import { Community } from '../interfaces';
@@ -17,6 +18,7 @@ interface CommunityFormProps {
interface CommunityFormState {
communityForm: CommunityFormI;
categories: Array<Category>;
+ loading: boolean;
}
export class CommunityForm extends Component<CommunityFormProps, CommunityFormState> {
@@ -28,7 +30,8 @@ export class CommunityForm extends Component<CommunityFormProps, CommunityFormSt
title: null,
category_id: null
},
- categories: []
+ categories: [],
+ loading: false
}
constructor(props: any, context: any) {
@@ -58,6 +61,10 @@ export class CommunityForm extends Component<CommunityFormProps, CommunityFormSt
WebSocketService.Instance.listCategories();
}
+ componentDidMount() {
+ autosize(document.querySelectorAll('textarea'));
+ }
+
componentWillUnmount() {
this.subscription.unsubscribe();
}
@@ -81,7 +88,7 @@ export class CommunityForm extends Component<CommunityFormProps, CommunityFormSt
<div class="form-group row">
<label class="col-12 col-form-label">Sidebar</label>
<div class="col-12">
- <textarea value={this.state.communityForm.description} onInput={linkEvent(this, this.handleCommunityDescriptionChange)} class="form-control" rows={6} />
+ <textarea value={this.state.communityForm.description} onInput={linkEvent(this, this.handleCommunityDescriptionChange)} class="form-control" rows={3} />
</div>
</div>
<div class="form-group row">
@@ -96,8 +103,11 @@ export class CommunityForm extends Component<CommunityFormProps, CommunityFormSt
</div>
<div class="form-group row">
<div class="col-12">
- <button type="submit" class="btn btn-secondary mr-2">{this.props.community ? 'Save' : 'Create'}</button>
- {this.props.community && <button type="button" class="btn btn-secondary" onClick={linkEvent(this, this.handleCancel)}>Cancel</button>}
+ <button type="submit" class="btn btn-secondary mr-2">
+ {this.state.loading ?
+ <svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg> :
+ this.props.community ? 'Save' : 'Create'}</button>
+ {this.props.community && <button type="button" class="btn btn-secondary" onClick={linkEvent(this, this.handleCancel)}>Cancel</button>}
</div>
</div>
</form>
@@ -106,11 +116,13 @@ export class CommunityForm extends Component<CommunityFormProps, CommunityFormSt
handleCreateCommunitySubmit(i: CommunityForm, event: any) {
event.preventDefault();
+ i.state.loading = true;
if (i.props.community) {
WebSocketService.Instance.editCommunity(i.state.communityForm);
} else {
WebSocketService.Instance.createCommunity(i.state.communityForm);
}
+ i.setState(i.state);
}
handleCommunityNameChange(i: CommunityForm, event: any) {
@@ -142,6 +154,7 @@ export class CommunityForm extends Component<CommunityFormProps, CommunityFormSt
console.log(msg);
if (msg.error) {
alert(msg.error);
+ this.state.loading = false;
return;
} else if (op == UserOperation.ListCategories){
let res: ListCategoriesResponse = msg;
@@ -150,9 +163,11 @@ export class CommunityForm extends Component<CommunityFormProps, CommunityFormSt
this.setState(this.state);
} else if (op == UserOperation.CreateCommunity) {
let res: CommunityResponse = msg;
+ this.state.loading = false;
this.props.onCreate(res.community.id);
} else if (op == UserOperation.EditCommunity) {
let res: CommunityResponse = msg;
+ this.state.loading = false;
this.props.onEdit(res.community);
}
}
diff --git a/ui/src/components/community.tsx b/ui/src/components/community.tsx
index ed693c55..1ca9a8af 100644
--- a/ui/src/components/community.tsx
+++ b/ui/src/components/community.tsx
@@ -11,6 +11,7 @@ interface State {
community: CommunityI;
communityId: number;
moderators: Array<CommunityUser>;
+ loading: boolean;
}
export class Community extends Component<any, State> {
@@ -31,7 +32,8 @@ export class Community extends Component<any, State> {
published: null
},
moderators: [],
- communityId: Number(this.props.match.params.id)
+ communityId: Number(this.props.match.params.id),
+ loading: true
}
constructor(props: any, context: any) {
@@ -40,12 +42,12 @@ export class Community extends Component<any, State> {
this.state = this.emptyState;
this.subscription = WebSocketService.Instance.subject
- .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
- .subscribe(
- (msg) => this.parseMessage(msg),
+ .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
+ .subscribe(
+ (msg) => this.parseMessage(msg),
(err) => console.error(err),
() => console.log('complete')
- );
+ );
WebSocketService.Instance.getCommunity(this.state.communityId);
}
@@ -57,6 +59,8 @@ export class Community extends Component<any, State> {
render() {
return (
<div class="container">
+ {this.state.loading ?
+ <h4><svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg></h4> :
<div class="row">
<div class="col-12 col-lg-9">
<h4>/f/{this.state.community.name}</h4>
@@ -66,6 +70,7 @@ export class Community extends Component<any, State> {
<Sidebar community={this.state.community} moderators={this.state.moderators} />
</div>
</div>
+ }
</div>
)
}
@@ -81,6 +86,7 @@ export class Community extends Component<any, State> {
let res: GetCommunityResponse = msg;
this.state.community = res.community;
this.state.moderators = res.moderators;
+ this.state.loading = false;
this.setState(this.state);
} else if (op == UserOperation.EditCommunity) {
let res: CommunityResponse = msg;
diff --git a/ui/src/components/login.tsx b/ui/src/components/login.tsx
index 0cb02616..2443369e 100644
--- a/ui/src/components/login.tsx
+++ b/ui/src/components/login.tsx
@@ -8,6 +8,8 @@ import { msgOp } from '../utils';
interface State {
loginForm: LoginForm;
registerForm: RegisterForm;
+ loginLoading: boolean;
+ registerLoading: boolean;
}
let emptyState: State = {
@@ -19,7 +21,9 @@ let emptyState: State = {
username: undefined,
password: undefined,
password_verify: undefined
- }
+ },
+ loginLoading: false,
+ registerLoading: false
}
export class Login extends Component<any, State> {
@@ -31,12 +35,12 @@ export class Login extends Component<any, State> {
this.state = emptyState;
this.subscription = WebSocketService.Instance.subject
- .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
- .subscribe(
- (msg) => this.parseMessage(msg),
+ .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
+ .subscribe(
+ (msg) => this.parseMessage(msg),
(err) => console.error(err),
() => console.log("complete")
- );
+ );
}
componentWillUnmount() {
@@ -77,7 +81,8 @@ export class Login extends Component<any, State> {
</div>
<div class="form-group row">
<div class="col-sm-10">
- <button type="submit" class="btn btn-secondary">Login</button>
+ <button type="submit" class="btn btn-secondary">{this.state.loginLoading ?
+ <svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg> : 'Login'}</button>
</div>
</div>
</form>
@@ -115,7 +120,9 @@ export class Login extends Component<any, State> {
</div>
<div class="form-group row">
<div class="col-sm-10">
- <button type="submit" class="btn btn-secondary">Sign Up</button>
+ <button type="submit" class="btn btn-secondary">{this.state.registerLoading ?
+ <svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg> : 'Sign Up'}</button>
+
</div>
</div>
</form>
@@ -124,6 +131,8 @@ export class Login extends Component<any, State> {
handleLoginSubmit(i: Login, event: any) {
event.preventDefault();
+ i.state.loginLoading = true;
+ i.setState(i.state);
WebSocketService.Instance.login(i.state.loginForm);
}
@@ -138,6 +147,8 @@ export class Login extends Component<any, State> {
}
handleRegisterSubmit(i: Login, event: any) {
+ i.state.registerLoading = true;
+ i.setState(i.state);
event.preventDefault();
WebSocketService.Instance.register(i.state.registerForm);
}
@@ -166,9 +177,14 @@ export class Login extends Component<any, State> {
let op: UserOperation = msgOp(msg);
if (msg.error) {
alert(msg.error);
+ this.state.loginLoading = false;
+ this.state.registerLoading = false;
+ this.setState(this.state);
return;
} else {
if (op == UserOperation.Register || op == UserOperation.Login) {
+ this.state.loginLoading = false;
+ this.state.registerLoading = false;
let res: LoginResponse = msg;
UserService.Instance.login(res);
this.props.history.push('/');
diff --git a/ui/src/components/main.tsx b/ui/src/components/main.tsx
index aa3c3a8c..54f185e2 100644
--- a/ui/src/components/main.tsx
+++ b/ui/src/components/main.tsx
@@ -9,13 +9,15 @@ import { msgOp } from '../utils';
interface State {
subscribedCommunities: Array<CommunityUser>;
+ loading: boolean;
}
export class Main extends Component<any, State> {
private subscription: Subscription;
private emptyState: State = {
- subscribedCommunities: []
+ subscribedCommunities: [],
+ loading: true
}
constructor(props: any, context: any) {
@@ -24,12 +26,12 @@ export class Main extends Component<any, State> {
this.state = this.emptyState;
this.subscription = WebSocketService.Instance.subject
- .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
- .subscribe(
- (msg) => this.parseMessage(msg),
+ .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();
@@ -51,13 +53,18 @@ export class Main extends Component<any, State> {
<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>
+ {this.state.loading ?
+ <h4 class="mt-3"><svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg></h4> :
+ <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>
@@ -76,6 +83,7 @@ export class Main extends Component<any, State> {
} else if (op == UserOperation.GetFollowedCommunities) {
let res: GetFollowedCommunitiesResponse = msg;
this.state.subscribedCommunities = res.communities;
+ this.state.loading = false;
this.setState(this.state);
}
}
diff --git a/ui/src/components/post-form.tsx b/ui/src/components/post-form.tsx
index 1f23cae0..9845a1b1 100644
--- a/ui/src/components/post-form.tsx
+++ b/ui/src/components/post-form.tsx
@@ -4,6 +4,7 @@ import { retryWhen, delay, take } from 'rxjs/operators';
import { PostForm as PostFormI, Post, PostResponse, UserOperation, Community, ListCommunitiesResponse } from '../interfaces';
import { WebSocketService } from '../services';
import { msgOp } from '../utils';
+import * as autosize from 'autosize';
interface PostFormProps {
post?: Post; // If a post is given, that means this is an edit
@@ -15,6 +16,7 @@ interface PostFormProps {
interface PostFormState {
postForm: PostFormI;
communities: Array<Community>;
+ loading: boolean;
}
export class PostForm extends Component<PostFormProps, PostFormState> {
@@ -26,7 +28,8 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
auth: null,
community_id: null
},
- communities: []
+ communities: [],
+ loading: false
}
constructor(props: any, context: any) {
@@ -56,6 +59,10 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
WebSocketService.Instance.listCommunities();
}
+ componentDidMount() {
+ autosize(document.querySelectorAll('textarea'));
+ }
+
componentWillUnmount() {
this.subscription.unsubscribe();
}
@@ -73,13 +80,13 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
<div class="form-group row">
<label class="col-sm-2 col-form-label">Title</label>
<div class="col-sm-10">
- <textarea value={this.state.postForm.name} onInput={linkEvent(this, this.handlePostNameChange)} class="form-control" required rows={3} />
+ <textarea value={this.state.postForm.name} onInput={linkEvent(this, this.handlePostNameChange)} class="form-control" required rows={2} />
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Body</label>
<div class="col-sm-10">
- <textarea value={this.state.postForm.body} onInput={linkEvent(this, this.handlePostBodyChange)} class="form-control" rows={6} />
+ <textarea value={this.state.postForm.body} onInput={linkEvent(this, this.handlePostBodyChange)} class="form-control" rows={4} />
</div>
</div>
<div class="form-group row">
@@ -94,7 +101,10 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
</div>
<div class="form-group row">
<div class="col-sm-10">
- <button type="submit" class="btn btn-secondary mr-2">{this.props.post ? 'Save' : 'Create'}</button>
+ <button type="submit" class="btn btn-secondary mr-2">
+ {this.state.loading ?
+ <svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg> :
+ this.props.post ? 'Save' : 'Create'}</button>
{this.props.post && <button type="button" class="btn btn-secondary" onClick={linkEvent(this, this.handleCancel)}>Cancel</button>}
</div>
</div>
@@ -110,6 +120,8 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
} else {
WebSocketService.Instance.createPost(i.state.postForm);
}
+ i.state.loading = true;
+ i.setState(i.state);
}
handlePostUrlChange(i: PostForm, event: any) {
@@ -139,6 +151,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
parseMessage(msg: any) {
let op: UserOperation = msgOp(msg);
if (msg.error) {
+ this.state.loading = false;
return;
} else if (op == UserOperation.ListCommunities) {
let res: ListCommunitiesResponse = msg;
@@ -150,9 +163,11 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
}
this.setState(this.state);
} else if (op == UserOperation.CreatePost) {
+ this.state.loading = false;
let res: PostResponse = msg;
this.props.onCreate(res.post.id);
} else if (op == UserOperation.EditPost) {
+ this.state.loading = false;
let res: PostResponse = msg;
this.props.onEdit(res.post);
}
diff --git a/ui/src/components/post-listings.tsx b/ui/src/components/post-listings.tsx
index bb36cabc..ce7833b2 100644
--- a/ui/src/components/post-listings.tsx
+++ b/ui/src/components/post-listings.tsx
@@ -18,6 +18,7 @@ interface PostListingsState {
posts: Array<Post>;
sortType: SortType;
type_: ListingType;
+ loading: boolean;
}
export class PostListings extends Component<PostListingsProps, PostListingsState> {
@@ -44,7 +45,8 @@ export class PostListings extends Component<PostListingsProps, PostListingsState
? ListingType.Community
: UserService.Instance.loggedIn
? ListingType.Subscribed
- : ListingType.All
+ : ListingType.All,
+ loading: true
}
constructor(props: any, context: any) {
@@ -77,11 +79,16 @@ export class PostListings extends Component<PostListingsProps, PostListingsState
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. Subscribe to some <Link to="/communities">forums</Link>.</div>
+ {this.state.loading ?
+ <h4><svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg></h4> :
+ <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. Subscribe to some <Link to="/communities">forums</Link>.</div>
+ }
+ </div>
}
</div>
)
@@ -103,11 +110,11 @@ export class PostListings extends Component<PostListingsProps, PostListingsState
</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>
+ <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>
@@ -149,6 +156,7 @@ export class PostListings extends Component<PostListingsProps, PostListingsState
} else if (op == UserOperation.GetPosts) {
let res: GetPostsResponse = msg;
this.state.posts = res.posts;
+ this.state.loading = false;
this.setState(this.state);
} else if (op == UserOperation.CreatePostLike) {
let res: CreatePostLikeResponse = msg;
diff --git a/ui/src/components/post.tsx b/ui/src/components/post.tsx
index e8308561..f36893f6 100644
--- a/ui/src/components/post.tsx
+++ b/ui/src/components/post.tsx
@@ -19,6 +19,7 @@ interface PostState {
moderators: Array<CommunityUser>;
scrolled?: boolean;
scrolled_comment_id?: number;
+ loading: boolean;
}
export class Post extends Component<any, PostState> {
@@ -30,7 +31,8 @@ export class Post extends Component<any, PostState> {
commentSort: CommentSortType.Hot,
community: null,
moderators: [],
- scrolled: false
+ scrolled: false,
+ loading: true
}
constructor(props: any, context: any) {
@@ -74,8 +76,9 @@ export class Post extends Component<any, PostState> {
render() {
return (
<div class="container">
- {this.state.post &&
- <div class="row">
+ {this.state.loading ?
+ <h4><svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg></h4> :
+ <div class="row">
<div class="col-12 col-sm-8 col-lg-7 mb-3">
<PostListing post={this.state.post} showBody showCommunity editable />
<div className="mb-2" />
@@ -202,6 +205,7 @@ export class Post extends Component<any, PostState> {
this.state.comments = res.comments;
this.state.community = res.community;
this.state.moderators = res.moderators;
+ this.state.loading = false;
this.setState(this.state);
} else if (op == UserOperation.CreateComment) {
let res: CommentResponse = msg;
diff --git a/ui/src/components/symbols.tsx b/ui/src/components/symbols.tsx
new file mode 100644
index 00000000..4f757dbb
--- /dev/null
+++ b/ui/src/components/symbols.tsx
@@ -0,0 +1,80 @@
+import { Component } from 'inferno';
+
+export class Symbols extends Component<any, any> {
+
+ constructor(props: any, context: any) {
+ super(props, context);
+ }
+
+ render() {
+ return (
+ <svg aria-hidden="true" style="position: absolute; width: 0; height: 0; overflow: hidden;" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+ <symbol id="icon-mouse" version="1.1" x="0px" y="0px"
+ viewBox="0 0 512 512">
+ <g>
+ <g>
+ <path d="M499.059,323.505l-7.52-32.532l-70.047,16.19c1.513-11.983,2.297-24.042,2.297-36.037c0-18.334-1.801-35.785-5.316-52.19
+ c29.365-12.101,55.143-28.885,69.372-45.529c17.524-20.498,25.985-46.568,23.822-73.406
+ c-2.163-26.862-14.706-51.268-35.316-68.724C433.879-4.694,369.917,0.439,333.774,42.718
+ c-9.546,11.168-18.318,27.381-25.379,46.649c-16.512-5.419-34.132-8.243-52.395-8.243s-35.885,2.824-52.395,8.243
+ c-7.06-19.267-15.832-35.481-25.379-46.649C142.082,0.44,78.123-4.695,35.648,31.277C15.038,48.733,2.494,73.141,0.332,100.001
+ c-2.161,26.838,6.297,52.907,23.822,73.406c14.229,16.644,40.006,33.427,69.372,45.529c-3.515,16.405-5.316,33.856-5.316,52.189
+ c0,11.995,0.785,24.053,2.297,36.037l-70.047-16.19l-7.52,32.532l84.337,19.492c4.349,17.217,10.201,33.953,17.421,49.752
+ L12.941,416.27l7.52,32.532l110.634-25.57c1.38,2.197,2.779,4.373,4.218,6.509c32.548,48.323,75.409,74.934,120.687,74.934
+ c45.278,0,88.138-26.612,120.687-74.934c1.439-2.136,2.839-4.313,4.218-6.509l110.634,25.57l7.52-32.532l-101.758-23.519
+ c7.221-15.799,13.072-32.535,17.421-49.752L499.059,323.505z M183.578,220.372c0-11.41,9.189-20.65,20.482-20.65
+ c11.306,0,20.494,9.24,20.494,20.65c0,11.408-9.188,20.656-20.494,20.656C192.768,241.028,183.578,231.78,183.578,220.372z
+ M256,413.29c-29.895,0-54.216-19.471-54.216-43.403c0-23.932,24.322-43.403,54.216-43.403s54.216,19.471,54.216,43.403
+ C310.216,393.819,285.895,413.29,256,413.29z M307.785,241.183c-11.402,0-20.65-9.317-20.65-20.81
+ c0-11.494,9.248-20.81,20.65-20.81c11.387,0,20.635,9.317,20.635,20.81C328.422,231.866,319.173,241.183,307.785,241.183z"/>
+ </g>
+ </g>
+ <g>
+ </g>
+ <g>
+ </g>
+ <g>
+ </g>
+ <g>
+ </g>
+ <g>
+ </g>
+ <g>
+ </g>
+ <g>
+ </g>
+ <g>
+ </g>
+ <g>
+ </g>
+ <g>
+ </g>
+ <g>
+ </g>
+ <g>
+ </g>
+ <g>
+ </g>
+ <g>
+ </g>
+ <g>
+ </g>
+ </symbol>
+ <symbol id="icon-search" viewBox="0 0 32 32">
+ <title>search</title>
+ <path d="M31.008 27.231l-7.58-6.447c-0.784-0.705-1.622-1.029-2.299-0.998 1.789-2.096 2.87-4.815 2.87-7.787 0-6.627-5.373-12-12-12s-12 5.373-12 12 5.373 12 12 12c2.972 0 5.691-1.081 7.787-2.87-0.031 0.677 0.293 1.515 0.998 2.299l6.447 7.58c1.104 1.226 2.907 1.33 4.007 0.23s0.997-2.903-0.23-4.007zM12 20c-4.418 0-8-3.582-8-8s3.582-8 8-8 8 3.582 8 8-3.582 8-8 8z"></path>
+ </symbol>
+ <symbol id="icon-github" viewBox="0 0 32 32">
+ <title>github</title>
+ <path d="M16 0.395c-8.836 0-16 7.163-16 16 0 7.069 4.585 13.067 10.942 15.182 0.8 0.148 1.094-0.347 1.094-0.77 0-0.381-0.015-1.642-0.022-2.979-4.452 0.968-5.391-1.888-5.391-1.888-0.728-1.849-1.776-2.341-1.776-2.341-1.452-0.993 0.11-0.973 0.11-0.973 1.606 0.113 2.452 1.649 2.452 1.649 1.427 2.446 3.743 1.739 4.656 1.33 0.143-1.034 0.558-1.74 1.016-2.14-3.554-0.404-7.29-1.777-7.29-7.907 0-1.747 0.625-3.174 1.649-4.295-0.166-0.403-0.714-2.030 0.155-4.234 0 0 1.344-0.43 4.401 1.64 1.276-0.355 2.645-0.532 4.005-0.539 1.359 0.006 2.729 0.184 4.008 0.539 3.054-2.070 4.395-1.64 4.395-1.64 0.871 2.204 0.323 3.831 0.157 4.234 1.026 1.12 1.647 2.548 1.647 4.295 0 6.145-3.743 7.498-7.306 7.895 0.574 0.497 1.085 1.47 1.085 2.963 0 2.141-0.019 3.864-0.019 4.391 0 0.426 0.288 0.925 1.099 0.768 6.354-2.118 10.933-8.113 10.933-15.18 0-8.837-7.164-16-16-16z"></path>
+ </symbol>
+ <symbol id="icon-spinner" viewBox="0 0 32 32">
+ <title>spinner</title>
+ <path d="M16 32c-4.274 0-8.292-1.664-11.314-4.686s-4.686-7.040-4.686-11.314c0-3.026 0.849-5.973 2.456-8.522 1.563-2.478 3.771-4.48 6.386-5.791l1.344 2.682c-2.126 1.065-3.922 2.693-5.192 4.708-1.305 2.069-1.994 4.462-1.994 6.922 0 7.168 5.832 13 13 13s13-5.832 13-13c0-2.459-0.69-4.853-1.994-6.922-1.271-2.015-3.066-3.643-5.192-4.708l1.344-2.682c2.615 1.31 4.824 3.313 6.386 5.791 1.607 2.549 2.456 5.495 2.456 8.522 0 4.274-1.664 8.292-4.686 11.314s-7.040 4.686-11.314 4.686z"></path>
+ </symbol>
+ </defs>
+ </svg>
+ );
+ }
+}
diff --git a/ui/src/index.tsx b/ui/src/index.tsx
index 45bae0f7..608e487a 100644
--- a/ui/src/index.tsx
+++ b/ui/src/index.tsx
@@ -10,6 +10,7 @@ import { Post } from './components/post';
import { Community } from './components/community';
import { Communities } from './components/communities';
import { User } from './components/user';
+import { Symbols } from './components/symbols';
import './main.css';
@@ -42,68 +43,12 @@ class Index extends Component<any, any> {
<Route path={`/user/:id/:heading`} component={User} />
<Route path={`/user/:id`} component={User} />
</Switch>
- {this.symbols()}
+ <Symbols />
</div>
</HashRouter>
);
}
- symbols() {
- return(
- <symbol id="icon-mouse" version="1.1" x="0px" y="0px"
- viewBox="0 0 512 512">
- <g>
- <g>
- <path d="M499.059,323.505l-7.52-32.532l-70.047,16.19c1.513-11.983,2.297-24.042,2.297-36.037c0-18.334-1.801-35.785-5.316-52.19
- c29.365-12.101,55.143-28.885,69.372-45.529c17.524-20.498,25.985-46.568,23.822-73.406
- c-2.163-26.862-14.706-51.268-35.316-68.724C433.879-4.694,369.917,0.439,333.774,42.718
- c-9.546,11.168-18.318,27.381-25.379,46.649c-16.512-5.419-34.132-8.243-52.395-8.243s-35.885,2.824-52.395,8.243
- c-7.06-19.267-15.832-35.481-25.379-46.649C142.082,0.44,78.123-4.695,35.648,31.277C15.038,48.733,2.494,73.141,0.332,100.001
- c-2.161,26.838,6.297,52.907,23.822,73.406c14.229,16.644,40.006,33.427,69.372,45.529c-3.515,16.405-5.316,33.856-5.316,52.189
- c0,11.995,0.785,24.053,2.297,36.037l-70.047-16.19l-7.52,32.532l84.337,19.492c4.349,17.217,10.201,33.953,17.421,49.752
- L12.941,416.27l7.52,32.532l110.634-25.57c1.38,2.197,2.779,4.373,4.218,6.509c32.548,48.323,75.409,74.934,120.687,74.934
- c45.278,0,88.138-26.612,120.687-74.934c1.439-2.136,2.839-4.313,4.218-6.509l110.634,25.57l7.52-32.532l-101.758-23.519
- c7.221-15.799,13.072-32.535,17.421-49.752L499.059,323.505z M183.578,220.372c0-11.41,9.189-20.65,20.482-20.65