@@ -3,8 +3,7 @@ import PropTypes from 'prop-types';
33import React , { useState , useEffect , useRef } from 'react' ;
44import { matchShape } from 'found' ;
55import { Helmet } from 'react-helmet' ;
6- import SiteHeader from '@hsl-fi/site-header' ;
7- import { useIntl } from 'react-intl' ;
6+ import { SiteHeader , UserMenu , QuickSearch } from '@hsl-fi/site-header' ;
87import { favouriteShape , configShape } from '../util/shapes' ;
98import { clearOldSearches , clearFutureRoutes } from '../util/storeUtils' ;
109import { getJson } from '../util/xhrPromise' ;
@@ -18,7 +17,6 @@ const clearStorages = context => {
1817const notificationAPI = '/api/user/notifications' ;
1918
2019const AppBarHsl = ( { lang, user, favourites } , context ) => {
21- const intl = useIntl ( ) ;
2220 const { config, match } = context ;
2321 const { location } = match ;
2422
@@ -27,15 +25,102 @@ const AppBarHsl = ({ lang, user, favourites }, context) => {
2725 post : `${ notificationAPI } ?language=${ lang } ` ,
2826 } ;
2927
30- const [ banners , setBanners ] = useState ( [ ] ) ;
28+ const [ searchQuery , setSearchQuery ] = useState ( '' ) ;
29+ const [ searchLoading , setSearchLoading ] = useState ( false ) ;
30+ const [ searchError , setSearchError ] = useState ( false ) ;
31+ const [ searchHits , setSearchHits ] = useState ( [ ] ) ;
32+ const [ searchHitsCount , setSearchHitsCount ] = useState ( 0 ) ;
33+ const [ userNotifications , setUserNotifications ] = useState ( {
34+ unreadCount : 0 ,
35+ loading : false ,
36+ error : null ,
37+ notifications : [ ] ,
38+ refetch : ( ) => { } ,
39+ onOpen : ( ) => { } ,
40+ } ) ;
3141
3242 useEffect ( ( ) => {
33- if ( config . URL . BANNERS && process . env . NODE_ENV !== 'test' ) {
34- getJson ( `${ config . URL . BANNERS } &language=${ lang } ` )
35- . then ( data => setBanners ( data ) )
36- . catch ( ( ) => setBanners ( [ ] ) ) ;
43+ if ( ! user . sub ) {
44+ return undefined ;
3745 }
38- } , [ lang ] ) ;
46+
47+ const markAsRead = ( ) => {
48+ fetch ( notificationApiUrls . post , {
49+ method : 'POST' ,
50+ headers : { 'content-type' : 'application/json' } ,
51+ } )
52+ . then ( ( ) => {
53+ setUserNotifications ( prev => ( { ...prev , unreadCount : 0 } ) ) ;
54+ } )
55+ . catch ( ( ) => { } ) ;
56+ } ;
57+
58+ const fetchNotifications = ( ) => {
59+ setUserNotifications ( prev => ( { ...prev , loading : true , error : null } ) ) ;
60+ getJson ( notificationApiUrls . get )
61+ . then ( data => {
62+ setUserNotifications ( {
63+ unreadCount : data ?. unreadCount || 0 ,
64+ loading : false ,
65+ error : null ,
66+ notifications : ( data ?. notifications || [ ] ) . map ( n => ( {
67+ ...n ,
68+ link : n . link || { } ,
69+ } ) ) ,
70+ refetch : fetchNotifications ,
71+ onOpen : markAsRead ,
72+ } ) ;
73+ } )
74+ . catch ( err => {
75+ setUserNotifications ( prev => ( {
76+ ...prev ,
77+ loading : false ,
78+ error : err ,
79+ } ) ) ;
80+ } ) ;
81+ } ;
82+
83+ fetchNotifications ( ) ;
84+ const interval = setInterval ( fetchNotifications , 60000 ) ;
85+ return ( ) => clearInterval ( interval ) ;
86+ } , [ user . sub , lang ] ) ;
87+
88+ useEffect ( ( ) => {
89+ if ( ! searchQuery || ! config . URL . HSL_FI_SUGGESTIONS ) {
90+ setSearchHits ( [ ] ) ;
91+ setSearchHitsCount ( 0 ) ;
92+ return undefined ;
93+ }
94+
95+ const timer = setTimeout ( ( ) => {
96+ setSearchLoading ( true ) ;
97+ setSearchError ( false ) ;
98+ getJson (
99+ `${
100+ config . URL . HSL_FI_SUGGESTIONS
101+ } ?language=${ lang } &take=5&query=${ encodeURIComponent ( searchQuery ) } `,
102+ )
103+ . then ( data => {
104+ const hits = ( data ?. hits || [ ] ) . map ( h => ( {
105+ id : h . id ,
106+ title : h . title ,
107+ type : h . type ,
108+ link : { href : h . url } ,
109+ } ) ) ;
110+ setSearchHits ( hits ) ;
111+ setSearchHitsCount (
112+ data ?. totalHits != null ? data . totalHits : hits . length ,
113+ ) ;
114+ setSearchLoading ( false ) ;
115+ } )
116+ . catch ( ( ) => {
117+ setSearchError ( true ) ;
118+ setSearchLoading ( false ) ;
119+ } ) ;
120+ } , 300 ) ;
121+
122+ return ( ) => clearTimeout ( timer ) ;
123+ } , [ searchQuery , lang ] ) ;
39124
40125 useEffect ( ( ) => {
41126 if ( config . URL . FONTCOUNTER && process . env . NODE_ENV === 'production' ) {
@@ -45,70 +130,63 @@ const AppBarHsl = ({ lang, user, favourites }, context) => {
45130 }
46131 } , [ ] ) ;
47132
48- const languages = [
49- {
50- name : 'fi' ,
51- url : `/fi${ location . pathname } ${ location . search } ` ,
133+ const languages = {
134+ fi : {
135+ href : `/fi${ location . pathname } ${ location . search } ` ,
52136 } ,
53- {
54- name : 'sv' ,
55- url : `/sv${ location . pathname } ${ location . search } ` ,
137+ sv : {
138+ href : `/sv${ location . pathname } ${ location . search } ` ,
56139 } ,
57- {
58- name : 'en' ,
59- url : `/en${ location . pathname } ${ location . search } ` ,
140+ en : {
141+ href : `/en${ location . pathname } ${ location . search } ` ,
60142 } ,
61- ] ;
143+ } ;
62144
63145 const { given_name, family_name } = user ;
64146
65- const initials =
66- given_name && family_name
67- ? given_name . charAt ( 0 ) + family_name . charAt ( 0 )
68- : '' ; // Authenticated user's initials, will be shown next to Person-icon.
69-
70147 const url = encodeURI ( location . pathname ) ;
71148 const params = location . search && location . search . substring ( 1 ) ;
149+ const travelersAccountLink = config . URL . TRAVELERS_ACCOUNT
150+ ? { href : config . URL . TRAVELERS_ACCOUNT }
151+ : undefined ;
152+ const myStopsAndRoutesLink = config . favouriteLink
153+ ? { href : config . favouriteLink [ lang ] || config . favouriteLink . fi }
154+ : undefined ;
72155 const userMenu =
73- config . allowLogin && ( user . sub || user . notLogged )
74- ? {
75- userMenu : {
76- isLoading : false , // When fetching for login-information, `isLoading`-property can be set to true. Spinner will be shown.
77- isAuthenticated : ! ! user . sub , // If user is authenticated, set `isAuthenticated`-property to true.
78- isSelected : false ,
79- loginUrl : `/login?url=${ url } &${ params } ` , // Url that user will be redirect to when Person-icon is pressed and user is not logged in.
80- initials,
81- menuItems : [
82- {
83- name : intl . formatMessage ( {
84- id : 'userinfo' ,
85- defaultMessage : 'My information' ,
86- } ) ,
87- url : `${ config . URL . ROOTLINK } /omat-tiedot` ,
88- onClick : ( ) => { } ,
89- } ,
90- {
91- name : intl . formatMessage ( {
92- id : 'logout' ,
93- defaultMessage : 'Logout' ,
94- } ) ,
95- url : '/logout' ,
96- onClick : ( ) => clearStorages ( context ) ,
97- } ,
98- ] ,
99- } ,
100- }
101- : { } ;
102-
103- const siteHeaderRef = useRef ( null ) ;
156+ config . allowLogin && ( user . sub || user . notLogged ) ? (
157+ < UserMenu
158+ lang = { lang }
159+ loading = { false }
160+ authenticated = { ! ! user . sub }
161+ loginLink = { { href : `/login?url=${ url } &${ params } ` } }
162+ logoutLink = { { href : '/logout' , onClick : ( ) => clearStorages ( context ) } }
163+ name = { { givenName : given_name , familyName : family_name } }
164+ userNotifications = { userNotifications }
165+ travelersAccountLink = { travelersAccountLink }
166+ myStopsAndRoutesLink = { myStopsAndRoutesLink }
167+ />
168+ ) : null ;
169+
170+ const search = config . URL . HSL_FI_SUGGESTIONS ? (
171+ < QuickSearch
172+ searchPageLink = { { href : `${ config . URL . ROOTLINK } /${ lang } /haku` } }
173+ loading = { searchLoading }
174+ error = { searchError }
175+ query = { searchQuery }
176+ onQueryChange = { e => setSearchQuery ( e . target . value ) }
177+ hitsCount = { searchHitsCount }
178+ hits = { searchHits }
179+ lang = { lang }
180+ />
181+ ) : null ;
182+
104183 const notificationTime = useRef ( 0 ) ;
105184
106185 useEffect ( ( ) => {
107186 const now = Date . now ( ) ;
108187 // refresh only once per 5 seconds
109188 if ( now - notificationTime . current > 5000 ) {
110- // Refetch notifications
111- siteHeaderRef . current ?. fetchNotifications ( ) ;
189+ userNotifications . refetch ( ) ;
112190 notificationTime . current = now ;
113191 }
114192 } , [ favourites ] ) ;
@@ -126,17 +204,14 @@ const AppBarHsl = ({ lang, user, favourites }, context) => {
126204 />
127205 </ Helmet >
128206 ) }
129-
130207 { ! config . hideHeader && (
131208 < SiteHeader
132- ref = { siteHeaderRef }
133- hslFiUrl = { config . URL . ROOTLINK }
209+ baseUrl = { config . URL . ROOTLINK }
210+ staticAssetsUrl = "/static-assets"
134211 lang = { lang }
135- { ...userMenu }
136- languageMenu = { languages }
137- banners = { banners }
138- suggestionsApiUrl = { config . URL . HSL_FI_SUGGESTIONS }
139- notificationApiUrls = { notificationApiUrls }
212+ userMenu = { userMenu }
213+ langMenu = { languages }
214+ search = { search }
140215 />
141216 ) }
142217 </ >
0 commit comments