Browse Source

start of community dropdown

unoptim
DashEightMate 2 years ago
parent
commit
1fb8b0628f
  1. 3
      package.json
  2. 219
      src/components/community-dropdown.tsx
  3. 22
      src/components/navbar.tsx
  4. 3
      src/components/symbols.tsx
  5. 120
      src/custom.css
  6. 909
      yarn.lock

3
package.json

@ -23,10 +23,13 @@
"@types/markdown-it-container": "^2.0.2",
"@types/node": "^13.11.1",
"autosize": "^4.0.2",
"body-scroll-lock": "^3.0.3",
"bootswatch": "^4.3.1",
"browser-sync": "^2.26.12",
"choices.js": "^9.0.1",
"classcat": "^4.0.2",
"emoji-short-name": "^1.0.0",
"global": "^4.4.0",
"husky": "^4.2.5",
"i18next": "^19.4.1",
"inferno": "^7.4.2",

219
src/components/community-dropdown.tsx

@ -0,0 +1,219 @@
import {
Community,
ListCommunitiesForm,
SortType,
GetUserDetailsForm,
WebSocketJsonResponse,
UserOperation,
ListCommunitiesResponse,
UserDetailsResponse,
CommunityUser,
} from '../interfaces';
import { Component, linkEvent } from 'inferno';
import { Subscription } from 'rxjs';
import { WebSocketService, UserService } from '../services';
import { retryWhen, delay, take } from 'rxjs/operators';
import { wsJsonToRes, toast } from '../utils';
import { i18n } from '../i18next';
import { Link } from 'inferno-router';
import {
disableBodyScroll,
enableBodyScroll,
clearAllBodyScrollLocks,
} from 'body-scroll-lock';
interface CommunityDropdownState {
favorites: Array<Community> /*not used right now */;
subscriptions: Array<CommunityUser>;
communities: Array<Community>;
filter: string;
page: number;
loading: boolean;
}
interface CommunityDropdownProps {
removeDropdown(): any;
}
export class CommunityDropdown extends Component<
CommunityDropdownProps,
CommunityDropdownState
> {
private maxLoad = 100;
private mainElement;
private subscription: Subscription;
private emptyState: CommunityDropdownState = {
favorites: null,
subscriptions: null,
communities: null,
filter: '',
page: 1,
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),
err => console.error(err),
() => console.log('complete')
);
this.fetch();
this.mainElement = document.querySelector('floating-container');
disableBodyScroll(this.mainElement);
}
componentWillUnmount() {
clearAllBodyScrollLocks();
}
render() {
return (
<>
<div class="dropdown-block"></div>
<div class="floating-container" id="floating-container">
{!this.state.loading && (
<div class="dropdown-content">
<div style="display:flex">
<input
class="dropdown-filter form-control"
placeholder="Filter"
onInput={linkEvent(this, this.handleFilterChange)}
></input>
<button
class="dropdown-exit btn"
onClick={linkEvent(this, this.handleDropdownClose)}
>
<svg class="icon icon-inline">
<use xlinkHref="#icon-cancel"></use>
</svg>
</button>
</div>
<div class="dropdown-categories">
<div class="dropdown-category">
<h6>Subscribed</h6>
{this.state.subscriptions
.filter(community =>
community.community_name.startsWith(this.state.filter)
)
.sort(undefined)
.map(community => (
<>
<div class="community-listing">
<span
class="community-icon"
style={
'background: ' +
this.generateColor(community.community_name)
}
></span>
<Link
class="community-listing-title"
to={`/c/${community.community_name}`}
onClick={linkEvent(this, this.handleDropdownClose)}
>
{community.community_name}
</Link>
</div>
</>
))}
</div>
<div class="dropdown-category">
<h6>Communities</h6>
{this.state.communities
.filter(community =>
community.name.startsWith(this.state.filter)
)
.map(community => (
<>
<div class="community-listing">
<span
class="community-icon"
style={
'background: ' +
this.generateColor(community.name)
}
></span>
<Link
class="community-listing-title"
to={`/c/${community.name}`}
onClick={linkEvent(this, this.handleDropdownClose)}
>
{community.name}
</Link>
</div>
</>
))}
</div>
</div>
</div>
)}
</div>
</>
);
}
fetch() {
let listCommunitiesForm: ListCommunitiesForm = {
sort: SortType[SortType.TopAll],
limit: this.maxLoad,
page: this.state.page,
};
let getUserDetailsForm: GetUserDetailsForm = {
user_id: UserService.Instance.user.id,
sort: SortType[0],
saved_only: false,
page: 1,
limit: 1,
};
WebSocketService.Instance.listCommunities(listCommunitiesForm);
WebSocketService.Instance.getUserDetails(getUserDetailsForm);
}
generateColor(str: string): string {
var hash = 0;
for (var i = 0; i < str.length; i++) {
hash = str.charCodeAt(i) + ((hash << 5) - hash);
}
var color = '#';
for (i = 0; i < 3; i++) {
var value = (hash >> (i * 8)) & 0xff;
color += ('00' + value.toString(16)).substr(-2);
}
return color;
}
handleDropdownClose(i: CommunityDropdown, event: any) {
clearAllBodyScrollLocks();
i.props.removeDropdown();
}
handleFilterChange(i: CommunityDropdown, event: any) {
i.state.filter = event.target.value;
console.log('filter changed to ' + i.state.filter);
i.setState(i.state);
}
parseMessage(msg: WebSocketJsonResponse) {
console.log(msg);
let res = wsJsonToRes(msg);
if (msg.error) {
toast(i18n.t(msg.error), 'danger');
return;
} else if (res.op == UserOperation.ListCommunities) {
let data = res.data as ListCommunitiesResponse;
this.state.communities = data.communities;
this.setState(this.state);
} else if (res.op == UserOperation.GetUserDetails) {
let data = res.data as UserDetailsResponse;
this.state.subscriptions = data.follows;
this.state.loading = false;
this.setState(this.state);
}
}
}

22
src/components/navbar.tsx

@ -34,6 +34,7 @@ import {
import { version } from '../version';
import { i18n } from '../i18next';
import { User } from './user';
import { CommunityDropdown } from './community-dropdown';
interface NavbarState {
isLoggedIn: boolean;
@ -47,6 +48,7 @@ interface NavbarState {
searchParam: string;
toggleSearch: boolean;
creatingCommunitiesEnabled: boolean;
communityDropdownShown: boolean;
}
class UnwrappedNavbar extends Component<any, NavbarState> {
@ -65,6 +67,7 @@ class UnwrappedNavbar extends Component<any, NavbarState> {
searchParam: '',
toggleSearch: false,
creatingCommunitiesEnabled: false,
communityDropdownShown: false,
};
constructor(props: any, context: any) {
@ -207,13 +210,14 @@ class UnwrappedNavbar extends Component<any, NavbarState> {
>
<ul class="navbar-nav my-2 mr-auto">
<li class="nav-item">
<Link
class="nav-link"
to="/communities"
<button
class="nav-link btn btn-inline "
//to="/communities"
title={i18n.t('communities')}
onClick={linkEvent(this, this.showCommunityDropdown)}
>
{i18n.t('communities')}
</Link>
</button>
</li>
<li class="nav-item">
<Link
@ -355,6 +359,11 @@ class UnwrappedNavbar extends Component<any, NavbarState> {
</ul>
)}
</div>
{this.state.communityDropdownShown && (
<CommunityDropdown
removeDropdown={() => this.showCommunityDropdown(this)}
></CommunityDropdown>
)}
</nav>
);
}
@ -364,6 +373,11 @@ class UnwrappedNavbar extends Component<any, NavbarState> {
i.setState(i.state);
}
showCommunityDropdown(i: Navbar) {
i.state.communityDropdownShown = !i.state.communityDropdownShown;
i.setState(i.state);
}
parseMessage(msg: WebSocketJsonResponse) {
let res = wsJsonToRes(msg);
if (msg.error) {

3
src/components/symbols.tsx

@ -208,6 +208,9 @@ export class Symbols extends Component<any, any> {
<path d="M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z"></path>
<line x1="4" y1="22" x2="4" y2="15"></line>
</symbol>
<symbol id="icon-cancel" viewBox="0 0 24 24">
<path d="M6,19.25a1.21,1.21,0,0,1-.88-.37,1.24,1.24,0,0,1,0-1.76l12-12a1.24,1.24,0,0,1,1.76,1.76l-12,12A1.21,1.21,0,0,1,6,19.25Z" />
</symbol>
</defs>
</svg>
);

120
src/custom.css

@ -397,4 +397,124 @@ a.text-body {
background-color: #333333;
border-radius: 4px;
height: 50px
}
.dropdown-block {
display: none;
position: fixed;
left: 0px;
top: 0px;
width: 100%;
height: 100%;
background: #444;
z-index: 4;
opacity: 0.5;
}
.floating-container {
display: block;
z-index: 5;
position: absolute;
left: 123px;
top: 40px;
background-color: #2f2f37;
width: 230px;
border-radius: 7px;
}
.dropdown-content {
padding: 2px 5px 5px;
}
.dropdown-categories {
padding: 0px 10px 5px;
}
.community-icon {
float: left;
width: 20px;
height: 20px;
margin-right: 7px;
border-radius: 3px;
}
.dropdown-content h6{
font-size: 13px;
color: #AAA;
margin-bottom: 8px;
margin-top: 12px;
}
.community-listing-title {
color: #DDD;
margin-left: 2px;
}
.community-listing-title:hover {
color: #AAA;
text-decoration: none;
}
.community-listing {
display: flex;
align-items: center;
width: 100%;
height: 20px;
margin-bottom: 7px;
}
.dropdown-filter {
margin: 7px auto 0;
height: 35px;
background-color: #1c1c21;
border: 1px #42424c;
}
.dropdown-filter:focus {
background-color: #27272C;
}
.dropdown-filter::placeholder {
color: #555;
}
.dropdown-exit {
display: none;
float: right;
width: 10px;
margin-left: -12px;
}
@media only screen and (max-width: 728px) {
.dropdown-block{
display: block;
}
.dropdown-exit{
display: inline;
}
.floating-container {
left: 50%;
margin-left: -150px;
top: 100px;
width: 300px;
}
.dropdown-content {
padding: 4px;
}
.dropdown-filter {
height: 40px;
margin-left: 2px;
}
.dropdown-categories {
margin-top: 10px;
}
.community-listing {
margin-bottom: 13px;
}
}

909
yarn.lock
File diff suppressed because it is too large
View File

Loading…
Cancel
Save