@ -29,8 +29,9 @@ import {
toast ,
messageToastify ,
md ,
setTheme ,
} from '../utils' ;
import { i18n } from '../i18next' ;
import { i18n , i18nextSetup } from '../i18next' ;
interface NavbarState {
isLoggedIn : boolean ;
@ -44,14 +45,16 @@ interface NavbarState {
admins : Array < UserView > ;
searchParam : string ;
toggleSearch : boolean ;
siteLoading : boolean ;
}
export class Navbar extends Component < any , NavbarState > {
private wsSub : Subscription ;
private userSub : Subscription ;
private unreadCountSub : Subscription ;
private searchTextField : RefObject < HTMLInputElement > ;
emptyState : NavbarState = {
isLoggedIn : UserService.Instance.user !== undefined ,
isLoggedIn : false ,
unreadCount : 0 ,
replies : [ ] ,
mentions : [ ] ,
@ -62,22 +65,13 @@ export class Navbar extends Component<any, NavbarState> {
admins : [ ] ,
searchParam : '' ,
toggleSearch : false ,
siteLoading : true ,
} ;
constructor ( props : any , context : any ) {
super ( props , context ) ;
this . state = this . emptyState ;
// Subscribe to user changes
this . userSub = UserService . Instance . sub . subscribe ( user = > {
this . state . isLoggedIn = user . user !== undefined ;
if ( this . state . isLoggedIn ) {
this . state . unreadCount = user . user . unreadCount ;
this . requestNotificationPermission ( ) ;
}
this . setState ( this . state ) ;
} ) ;
this . wsSub = WebSocketService . Instance . subject
. pipe ( retryWhen ( errors = > errors . pipe ( delay ( 3000 ) , take ( 10 ) ) ) )
. subscribe (
@ -86,17 +80,30 @@ export class Navbar extends Component<any, NavbarState> {
( ) = > console . log ( 'complete' )
) ;
if ( this . state . isLoggedIn ) {
this . requestNotificationPermission ( ) ;
// TODO couldn't get re-logging in to re-fetch unreads
this . fetchUnreads ( ) ;
}
WebSocketService . Instance . getSite ( ) ;
this . searchTextField = createRef ( ) ;
}
componentDidMount() {
// Subscribe to jwt changes
this . userSub = UserService . Instance . jwtSub . subscribe ( res = > {
// A login
if ( res !== undefined ) {
this . requestNotificationPermission ( ) ;
} else {
this . state . isLoggedIn = false ;
}
WebSocketService . Instance . getSite ( ) ;
this . setState ( this . state ) ;
} ) ;
// Subscribe to unread count changes
this . unreadCountSub = UserService . Instance . unreadCountSub . subscribe ( res = > {
this . setState ( { unreadCount : res } ) ;
} ) ;
}
handleSearchParam ( i : Navbar , event : any ) {
i . state . searchParam = event . target . value ;
i . setState ( i . state ) ;
@ -145,6 +152,7 @@ export class Navbar extends Component<any, NavbarState> {
componentWillUnmount() {
this . wsSub . unsubscribe ( ) ;
this . userSub . unsubscribe ( ) ;
this . unreadCountSub . unsubscribe ( ) ;
}
// TODO class active corresponding to current page
@ -152,9 +160,17 @@ export class Navbar extends Component<any, NavbarState> {
return (
< nav class = "navbar navbar-expand-lg navbar-light shadow-sm p-0 px-3" >
< div class = "container" >
< Link title = { this . state . version } class = "navbar-brand" to = "/" >
{ this . state . siteName }
< / Link >
{ ! this . state . siteLoading ? (
< Link title = { this . state . version } class = "navbar-brand" to = "/" >
{ this . state . siteName }
< / Link >
) : (
< div class = "navbar-item" >
< svg class = "icon icon-spinner spin" >
< use xlinkHref = "#icon-spinner" > < / use >
< / svg >
< / div >
) }
{ this . state . isLoggedIn && (
< Link
class = "ml-auto p-0 navbar-toggler nav-link border-0"
@ -180,151 +196,160 @@ export class Navbar extends Component<any, NavbarState> {
>
< span class = "navbar-toggler-icon" > < / span >
< / button >
< div
className = { ` ${ ! this . state . expanded && 'collapse' } navbar-collapse ` }
>
< ul class = "navbar-nav my-2 mr-auto" >
< li class = "nav-item" >
< Link
class = "nav-link"
to = "/communities"
title = { i18n . t ( 'communities' ) }
>
{ i18n . t ( 'communities' ) }
< / Link >
< / li >
< li class = "nav-item" >
< Link
class = "nav-link"
to = { {
pathname : '/create_post' ,
state : { prevPath : this.currentLocation } ,
} }
title = { i18n . t ( 'create_post' ) }
>
{ i18n . t ( 'create_post' ) }
< / Link >
< / li >
< li class = "nav-item" >
< Link
class = "nav-link"
to = "/create_community"
title = { i18n . t ( 'create_community' ) }
>
{ i18n . t ( 'create_community' ) }
< / Link >
< / li >
< li className = "nav-item" >
< Link
class = "nav-link"
to = "/sponsors"
title = { i18n . t ( 'donate_to_lemmy' ) }
>
< svg class = "icon" >
< use xlinkHref = "#icon-coffee" > < / use >
< / svg >
< / Link >
< / li >
< / ul >
{ ! this . context . router . history . location . pathname . match (
/^\/search/
) && (
< form
class = "form-inline"
onSubmit = { linkEvent ( this , this . handleSearchSubmit ) }
>
< input
class = { ` form-control mr-0 search-input ${
this . state . toggleSearch ? 'show-input' : 'hide-input'
} ` }
onInput = { linkEvent ( this , this . handleSearchParam ) }
value = { this . state . searchParam }
ref = { this . searchTextField }
type = "text"
placeholder = { i18n . t ( 'search' ) }
onBlur = { linkEvent ( this , this . handleSearchBlur ) }
> < / input >
< button
name = "search-btn"
onClick = { linkEvent ( this , this . handleSearchBtn ) }
class = "btn btn-link"
style = "color: var(--gray)"
>
< svg class = "icon" >
< use xlinkHref = "#icon-search" > < / use >
< / svg >
< / button >
< / form >
) }
< ul class = "navbar-nav my-2" >
{ this . canAdmin && (
{ ! this . state . siteLoading && (
< div
className = { ` ${
! this . state . expanded && 'collapse'
} navbar - collapse ` }
>
< ul class = "navbar-nav my-2 mr-auto" >
< li class = "nav-item" >
< Link
class = "nav-link"
to = "/communities"
title = { i18n . t ( 'communities' ) }
>
{ i18n . t ( 'communities' ) }
< / Link >
< / li >
< li class = "nav-item" >
< Link
class = "nav-link"
to = { {
pathname : '/create_post' ,
state : { prevPath : this.currentLocation } ,
} }
title = { i18n . t ( 'create_post' ) }
>
{ i18n . t ( 'create_post' ) }
< / Link >
< / li >
< li class = "nav-item" >
< Link
class = "nav-link"
to = "/create_community"
title = { i18n . t ( 'create_community' ) }
>
{ i18n . t ( 'create_community' ) }
< / Link >
< / li >
< li className = "nav-item" >
< Link
class = "nav-link"
to = { ` /admin ` }
title = { i18n . t ( 'admin_settings ' ) }
to = "/sponsors"
title = { i18n . t ( 'donate_to_lemmy' ) }
>
< svg class = "icon" >
< use xlinkHref = "#icon-settings " > < / use >
< use xlinkHref = "#icon-coffee" > < / use >
< / svg >
< / Link >
< / li >
< / ul >
{ ! this . context . router . history . location . pathname . match (
/^\/search/
) && (
< form
class = "form-inline"
onSubmit = { linkEvent ( this , this . handleSearchSubmit ) }
>
< input
class = { ` form-control mr-0 search-input ${
this . state . toggleSearch ? 'show-input' : 'hide-input'
} ` }
onInput = { linkEvent ( this , this . handleSearchParam ) }
value = { this . state . searchParam }
ref = { this . searchTextField }
type = "text"
placeholder = { i18n . t ( 'search' ) }
onBlur = { linkEvent ( this , this . handleSearchBlur ) }
> < / input >
< button
name = "search-btn"
onClick = { linkEvent ( this , this . handleSearchBtn ) }
class = "btn btn-link"
style = "color: var(--gray)"
>
< svg class = "icon" >
< use xlinkHref = "#icon-search" > < / use >
< / svg >
< / button >
< / form >
) }
< / ul >
{ this . state . isLoggedIn ? (
< >
< ul class = "navbar-nav my-2" >
< ul class = "navbar-nav my-2" >
{ this . canAdmin && (
< li className = "nav-item" >
< Link class = "nav-link" to = "/inbox" title = { i18n . t ( 'inbox' ) } >
< Link
class = "nav-link"
to = { ` /admin ` }
title = { i18n . t ( 'admin_settings' ) }
>
< svg class = "icon" >
< use xlinkHref = "#icon-bell" > < / use >
< use xlinkHref = "#icon-settings " > < / use >
< / svg >
{ this . state . unreadCount > 0 && (
< span class = "ml-1 badge badge-light" >
{ this . state . unreadCount }
< / span >
) }
< / Link >
< / li >
< / ul >
< ul class = "navbar-nav" >
) }
< / ul >
{ this . state . isLoggedIn ? (
< >
< ul class = "navbar-nav my-2" >
< li className = "nav-item" >
< Link
class = "nav-link"
to = "/inbox"
title = { i18n . t ( 'inbox' ) }
>
< svg class = "icon" >
< use xlinkHref = "#icon-bell" > < / use >
< / svg >
{ this . state . unreadCount > 0 && (
< span class = "ml-1 badge badge-light" >
{ this . state . unreadCount }
< / span >
) }
< / Link >
< / li >
< / ul >
< ul class = "navbar-nav" >
< li className = "nav-item" >
< Link
class = "nav-link"
to = { ` /u/ ${ UserService . Instance . user . name } ` }
title = { i18n . t ( 'settings' ) }
>
< span >
{ UserService . Instance . user . avatar &&
showAvatars ( ) && (
< img
src = { pictrsAvatarThumbnail (
UserService . Instance . user . avatar
) }
height = "32"
width = "32"
class = "rounded-circle mr-2"
/ >
) }
{ UserService . Instance . user . name }
< / span >
< / Link >
< / li >
< / ul >
< / >
) : (
< ul class = "navbar-nav my-2" >
< li className = "nav-item" >
< Link
class = "nav-link"
to = { ` /u/ ${ UserService . Instance . user . username } ` }
title = { i18n . t ( 'settings' ) }
class = "btn btn-success "
to = "/login"
title = { i18n . t ( 'login_sign_up ' ) }
>
< span >
{ UserService . Instance . user . avatar && showAvatars ( ) && (
< img
src = { pictrsAvatarThumbnail (
UserService . Instance . user . avatar
) }
height = "32"
width = "32"
class = "rounded-circle mr-2"
/ >
) }
{ UserService . Instance . user . username }
< / span >
{ i18n . t ( 'login_sign_up' ) }
< / Link >
< / li >
< / ul >
< / >
) : (
< ul class = "navbar-nav my-2" >
< li className = "nav-item" >
< Link
class = "btn btn-success"
to = "/login"
title = { i18n . t ( 'login_sign_up' ) }
>
{ i18n . t ( 'login_sign_up' ) }
< / Link >
< / li >
< / ul >
) }
< / div >
) }
< / div >
) }
< / div >
< / nav >
) ;
@ -400,38 +425,53 @@ export class Navbar extends Component<any, NavbarState> {
this . state . siteName = data . site . name ;
this . state . version = data . version ;
this . state . admins = data . admins ;
this . setState ( this . state ) ;
}
}
}
fetchUnreads() {
if ( this . state . isLoggedIn ) {
let repliesForm : GetRepliesForm = {
sort : SortType [ SortType . New ] ,
unread_only : true ,
page : 1 ,
limit : fetchLimit ,
} ;
// The login
if ( data . my_user ) {
UserService . Instance . user = data . my_user ;
// On the first load, check the unreads
if ( this . state . isLoggedIn == false ) {
this . requestNotificationPermission ( ) ;
this . fetchUnreads ( ) ;
setTheme ( data . my_user . theme , true ) ;
}
this . state . isLoggedIn = true ;
}
let userMentionsForm : GetUserMentionsForm = {
sort : SortType [ SortType . New ] ,
unread_only : true ,
page : 1 ,
limit : fetchLimit ,
} ;
i18nextSetup ( ) ;
let privateMessagesForm : GetPrivateMessagesForm = {
unread_only : true ,
page : 1 ,
limit : fetchLimit ,
} ;
this . state . siteLoading = false ;
this . setState ( this . state ) ;
}
}
if ( this . currentLocation !== '/inbox' ) {
WebSocketService . Instance . getReplies ( repliesForm ) ;
WebSocketService . Instance . getUserMentions ( userMentionsForm ) ;
WebSocketService . Instance . getPrivateMessages ( privateMessagesForm ) ;
}
fetchUnreads() {
console . log ( 'Fetching unreads...' ) ;
let repliesForm : GetRepliesForm = {
sort : SortType [ SortType . New ] ,
unread_only : true ,
page : 1 ,
limit : fetchLimit ,
} ;
let userMentionsForm : GetUserMentionsForm = {
sort : SortType [ SortType . New ] ,
unread_only : true ,
page : 1 ,
limit : fetchLimit ,
} ;
let privateMessagesForm : GetPrivateMessagesForm = {
unread_only : true ,
page : 1 ,
limit : fetchLimit ,
} ;
if ( this . currentLocation !== '/inbox' ) {
WebSocketService . Instance . getReplies ( repliesForm ) ;
WebSocketService . Instance . getUserMentions ( userMentionsForm ) ;
WebSocketService . Instance . getPrivateMessages ( privateMessagesForm ) ;
}
}
@ -440,10 +480,7 @@ export class Navbar extends Component<any, NavbarState> {
}
sendUnreadCount() {
UserService . Instance . user . unreadCount = this . state . unreadCount ;
UserService . Instance . sub . next ( {
user : UserService.Instance.user ,
} ) ;
UserService . Instance . unreadCountSub . next ( this . state . unreadCount ) ;
}
calculateUnreadCount ( ) : number {