diff --git a/package-lock.json b/package-lock.json
index f1719f05..6aa40f76 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11310,6 +11310,24 @@
"@babel/runtime": "^7.3.1"
}
},
+ "i18next-browser-languagedetector": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-6.1.2.tgz",
+ "integrity": "sha512-YDzIGHhMRvr7M+c8B3EQUKyiMBhfqox4o1qkFvt4QXuu5V2cxf74+NCr+VEkUuU0y+RwcupA238eeolW1Yn80g==",
+ "requires": {
+ "@babel/runtime": "^7.14.6"
+ },
+ "dependencies": {
+ "@babel/runtime": {
+ "version": "7.14.6",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.6.tgz",
+ "integrity": "sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg==",
+ "requires": {
+ "regenerator-runtime": "^0.13.4"
+ }
+ }
+ }
+ },
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@@ -14533,6 +14551,26 @@
"integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==",
"dev": true
},
+ "opening_hours": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/opening_hours/-/opening_hours-3.6.0.tgz",
+ "integrity": "sha512-ETHEchqvpZxJiLznNSdYHiGyeIMikJVfYEjMjYe0oRAxcQejilyXWWGjJBcIOXLwgU6LaATeFb6LRTgEguz0yw==",
+ "requires": {
+ "i18next": "^20.2.1",
+ "i18next-browser-languagedetector": "^6.1.0",
+ "suncalc": "^1.8.0"
+ },
+ "dependencies": {
+ "i18next": {
+ "version": "20.3.2",
+ "resolved": "https://registry.npmjs.org/i18next/-/i18next-20.3.2.tgz",
+ "integrity": "sha512-e8CML2R9Ng2sSQOM80wb/PrM2j8mDm84o/T4Amzn9ArVyNX5/ENWxxAXkRpZdTQNDaxKImF93Wep4mAoozFrKw==",
+ "requires": {
+ "@babel/runtime": "^7.12.0"
+ }
+ }
+ }
+ },
"opn": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz",
@@ -18613,6 +18651,11 @@
}
}
},
+ "suncalc": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/suncalc/-/suncalc-1.8.0.tgz",
+ "integrity": "sha1-HZiYEJVjB4dQ9JlKlZ5lTYdqy/U="
+ },
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
diff --git a/package.json b/package.json
index a3cc0ad3..688a0aee 100644
--- a/package.json
+++ b/package.json
@@ -89,6 +89,7 @@
"ngx-logger": "4.1.9",
"ngx-markdown": "9.1.1",
"ngx-moment": "5.0.0",
+ "opening_hours": "3.6.0",
"rxjs": "6.6.3",
"tslib": "1.14.1",
"zone.js": "0.11.2"
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 24e6b13e..64f20a7a 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -12,12 +12,7 @@
* You should have received a copy of the GNU General Public License along with
* this program. If not, see .
*/
-import {
- CommonModule,
- HashLocationStrategy,
- LocationStrategy,
- registerLocaleData,
-} from '@angular/common';
+import {CommonModule, LocationStrategy, PathLocationStrategy, registerLocaleData} from '@angular/common';
import {HttpClient} from '@angular/common/http';
import localeDe from '@angular/common/locales/de';
import {APP_INITIALIZER, NgModule, Provider} from '@angular/core';
@@ -116,7 +111,7 @@ const providers: Provider[] = [
},
{
provide: LocationStrategy,
- useClass: HashLocationStrategy,
+ useClass: PathLocationStrategy,
},
{
provide: APP_INITIALIZER,
diff --git a/src/app/modules/data/detail/data-detail-content.html b/src/app/modules/data/detail/data-detail-content.html
index 39210555..62e427ef 100644
--- a/src/app/modules/data/detail/data-detail-content.html
+++ b/src/app/modules/data/detail/data-detail-content.html
@@ -1,19 +1,36 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{item.name}}
+ {{item.type}}
+
+
+
+
+
+
+
+
diff --git a/src/app/modules/data/types/date-series/date-series-list-item.html b/src/app/modules/data/types/date-series/date-series-list-item.html
index 2514889d..3c29b41a 100644
--- a/src/app/modules/data/types/date-series/date-series-list-item.html
+++ b/src/app/modules/data/types/date-series/date-series-list-item.html
@@ -5,7 +5,7 @@
{{ 'name' | thingTranslate: item }}
-
+
{{ item.repeatFrequency | amDuration }},
{{ item.dates[0] | dateFormat: 'weekday:long' }}
diff --git a/src/app/modules/data/types/place/place-list-item.html b/src/app/modules/data/types/place/place-list-item.html
index 7b9e7af1..2131cf74 100644
--- a/src/app/modules/data/types/place/place-list-item.html
+++ b/src/app/modules/data/types/place/place-list-item.html
@@ -2,9 +2,26 @@
-
{{ 'name' | thingTranslate: item }}
+
{{'name' | thingTranslate: item}}
+
+
+
+ {{item.openingHours | openingHours}}
+
+
+
+
+ {{'categories' | thingTranslate: item | join:', ' | titlecase }}
+
+
+
+
+ {{'type' | thingTranslate: item}}
+
+
+
- {{ 'description' | thingTranslate: item }}
+ {{'description' | thingTranslate: item}}
-
-
- {{ 'name' | thingTranslate: item.inPlace }}
-
-
+
+
+ {{'name' | thingTranslate: item.inPlace}}
+
+
diff --git a/src/app/modules/data/types/place/special/mensa/place-mensa-detail.component.ts b/src/app/modules/data/types/place/special/mensa/place-mensa-detail.component.ts
index 8f2f1ed0..8200a291 100644
--- a/src/app/modules/data/types/place/special/mensa/place-mensa-detail.component.ts
+++ b/src/app/modules/data/types/place/special/mensa/place-mensa-detail.component.ts
@@ -39,7 +39,7 @@ export class PlaceMensaDetailComponent implements AfterViewInit {
/**
* number of days to display mensa menus for
*/
- @Input() displayRange = 5;
+ @Input() displayRange = 7;
/**
* TODO
diff --git a/src/app/translation/common-string-pipes.ts b/src/app/translation/common-string-pipes.ts
index 1787b362..3c736226 100644
--- a/src/app/translation/common-string-pipes.ts
+++ b/src/app/translation/common-string-pipes.ts
@@ -15,9 +15,15 @@
import {Injectable, OnDestroy, Pipe, PipeTransform} from '@angular/core';
import {LangChangeEvent, TranslateService} from '@ngx-translate/core';
+import moment from 'moment';
import {Subscription} from 'rxjs';
import {logger} from '../_helpers/ts-logger';
+// tslint:disable-next-line: no-var-requires
+const openingHoursFn = require('opening_hours');
+
+// tslint:disable: completed-docs
+
@Injectable()
@Pipe({
name: 'join',
@@ -89,6 +95,78 @@ export class StringSplitPipe implements PipeTransform {
}
}
+@Injectable()
+@Pipe({
+ name: 'openingHours',
+ pure: false, // required to update the value when the promise is resolved
+})
+export class OpeningHoursPipe implements PipeTransform {
+ locale: string;
+ onLangChange?: Subscription;
+ value = '';
+
+ constructor(private readonly translate: TranslateService) {
+ this.locale = translate.currentLang;
+ }
+
+ private _dispose(): void {
+ if (this.onLangChange?.closed === false) {
+ this.onLangChange?.unsubscribe();
+ }
+ }
+
+
+ transform(aString: string | unknown): string {
+ this.updateValue(aString);
+ this._dispose();
+ if (this.onLangChange?.closed === true) {
+ this.onLangChange = this.translate.onLangChange.subscribe((event: LangChangeEvent) => {
+ this.locale = event.lang;
+ this.updateValue(aString);
+ });
+ }
+
+ return this.value;
+ }
+
+ updateValue(aString: string | unknown) {
+ if (typeof aString !== 'string'){
+ logger.warn(`openingHours pipe unable to parse input: ${aString}`);
+
+ return;
+ }
+ const openingHours = new openingHoursFn(aString);
+
+ if ((openingHours.getWarnings() as string[]).length > 0){
+ logger.warn((openingHours.getWarnings() as string[]).join('. '));
+
+ return;
+ }
+
+ const isOpen: boolean = openingHours.getState();
+ const nextChange: Date = openingHours.getNextChange();
+
+ let prefixKey = isOpen ?
+ 'common.openingHours.open_until' :
+ 'common.openingHours.closed_until';
+
+ let formattedCalender = moment(nextChange)
+ .calendar();
+
+ if (moment(nextChange)
+ .isBefore(moment()
+ .add(1, 'hours'))) {
+ prefixKey= isOpen ?
+ 'common.openingHours.closing_soon' :
+ 'common.openingHours.opening_soon';
+ formattedCalender = formattedCalender.substr(0,1)
+ .toUpperCase() + formattedCalender.substr(1);
+ }
+ this.value = `${this.translate.instant(prefixKey)} ${formattedCalender}`;
+ }
+}
+
+
@Injectable()
@Pipe({
name: 'numberLocalized',
diff --git a/src/app/translation/thing-translate.module.ts b/src/app/translation/thing-translate.module.ts
index 3434fe47..bb8b72b1 100644
--- a/src/app/translation/thing-translate.module.ts
+++ b/src/app/translation/thing-translate.module.ts
@@ -14,7 +14,7 @@
*/
import {ModuleWithProviders, NgModule, Provider} from '@angular/core';
-import {ArrayJoinPipe, DateLocalizedFormatPipe, NumberLocalizedPipe, SentenceCasePipe, StringSplitPipe} from './common-string-pipes';
+import {ArrayJoinPipe, DateLocalizedFormatPipe, NumberLocalizedPipe, SentenceCasePipe, StringSplitPipe, OpeningHoursPipe} from './common-string-pipes';
import {ThingTranslateDefaultParser, ThingTranslateParser} from './thing-translate.parser';
import {ThingPropertyNameTranslatePipe, ThingTranslatePipe} from './thing-translate.pipe';
import {ThingTranslateService} from './thing-translate.service';
@@ -31,6 +31,7 @@ export interface ThingTranslateModuleConfig {
ThingPropertyNameTranslatePipe,
ThingTranslatePipe,
DateLocalizedFormatPipe,
+ OpeningHoursPipe,
SentenceCasePipe,
],
exports: [
@@ -40,6 +41,7 @@ export interface ThingTranslateModuleConfig {
ThingPropertyNameTranslatePipe,
ThingTranslatePipe,
DateLocalizedFormatPipe,
+ OpeningHoursPipe,
SentenceCasePipe,
],
})
diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json
index b2bcc5f5..96b5598e 100644
--- a/src/assets/i18n/de.json
+++ b/src/assets/i18n/de.json
@@ -9,6 +9,14 @@
"UNKNOWN": "Unbekannter Fehler"
}
},
+ "common": {
+ "openingHours": {
+ "closed_until": "Geschlossen bis",
+ "closing_soon": "Schließt bald!",
+ "open_until": "Geöffnet bis",
+ "opening_soon": "Öffnet"
+ }
+ },
"data": {
"REFRESH_ACTION": "Aktualisieren",
"REFRESHING": "Aktualisierung läuft...",
diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json
index 320c2a06..6d7c86ba 100644
--- a/src/assets/i18n/en.json
+++ b/src/assets/i18n/en.json
@@ -9,6 +9,14 @@
"UNKNOWN": "Unknown problem"
}
},
+ "common": {
+ "openingHours": {
+ "closed_until": "Closed until",
+ "closing_soon": "Closing soon!",
+ "open_until": "Open until",
+ "opening_soon": "Opens"
+ }
+ },
"data": {
"REFRESH_ACTION": "Refresh",
"REFRESHING": "Refreshing...",