diff --git a/AllTranslationAssets.ts b/AllTranslationAssets.ts
index 3ef54ca..bd826a6 100644
--- a/AllTranslationAssets.ts
+++ b/AllTranslationAssets.ts
@@ -123,5 +123,8 @@ export default class AllTranslationAssets {
favourite: { panelIntro: new Translation( {"en":"
Your personal theme
Activate your favourite layers from all the official themes","ca":"
La teva interfície personal
Activa les teves capes favorites de totes les interfícies oficials","es":"
Tu interficie personal
Activa tus capas favoritas de todas las interficies oficiales","gl":"
O teu tema personalizado
Activa as túas capas favoritas de todos os temas oficiais","de":"
Ihr persönliches Thema
Aktivieren Sie Ihre Lieblingsebenen aus allen offiziellen Themen"} ),
loginNeeded: new Translation( {"en":"
Log in
A personal layout is only available for OpenStreetMap users","es":"
Entrar
El diseño personalizado sólo está disponible para los usuarios de OpenstreetMap","ca":"
Entrar
El disseny personalizat només està disponible pels usuaris d' OpenstreetMap","gl":"
Iniciar a sesión
O deseño personalizado só está dispoñíbel para os usuarios do OpenstreetMap","de":"
Anmelden
Ein persönliches Layout ist nur für OpenStreetMap-Benutzer verfügbar"} ),
reload: new Translation( {"en":"Reload the data","es":"Recargar datos","ca":"Recarregar dades","gl":"Recargar os datos","de":"Daten neu laden"} ),
+},
+ reviews: { title: new Translation( {"en":"Reviews","nl":"Beoordelingen"} ),
+ attribution: new Translation( {"en":"Reviews are powered by Mangrove Reviews and are available under CC-BY 4.0"} ),
},
}}
\ No newline at end of file
diff --git a/Customizations/JSON/TagRenderingConfig.ts b/Customizations/JSON/TagRenderingConfig.ts
index 24fdd0b..d6e9a92 100644
--- a/Customizations/JSON/TagRenderingConfig.ts
+++ b/Customizations/JSON/TagRenderingConfig.ts
@@ -26,7 +26,7 @@ export default class TagRenderingConfig {
mappings?: {
if: TagsFilter,
then: Translation
- hideInAnswer: boolean
+ hideInAnswer: boolean | TagsFilter
}[]
constructor(json: string | TagRenderingConfigJson, context?: string) {
@@ -62,10 +62,16 @@ export default class TagRenderingConfig {
if (mapping.then === undefined) {
throw "Invalid mapping: if without body"
}
+ let hideInAnswer : boolean | TagsFilter = false;
+ if(typeof mapping.hideInAnswer === "boolean"){
+ hideInAnswer = mapping.hideInAnswer;
+ }else{
+ hideInAnswer = FromJSON.Tag(mapping.hideInAnswer);
+ }
return {
if: FromJSON.Tag(mapping.if, `${context}.mapping[${i}]`),
then: Translations.T(mapping.then),
- hideInAnswer: mapping.hideInAnswer ?? false
+ hideInAnswer: hideInAnswer
};
});
}
diff --git a/Logic/Web/MangroveReviews.ts b/Logic/Web/MangroveReviews.ts
new file mode 100644
index 0000000..1d8f45b
--- /dev/null
+++ b/Logic/Web/MangroveReviews.ts
@@ -0,0 +1,50 @@
+import * as mangrove from 'mangrove-reviews'
+import {UIEventSource} from "../UIEventSource";
+
+export default class MangroveReviews {
+
+ constructor() {
+ }
+
+ /**
+ * Gives a UIEVentsource with all reviews.
+ * Note: rating is between 1 and 100
+ */
+ public static GetReviewsFor(lon: number, lat: number, name: string): UIEventSource<{
+ comment?: string,
+ author: string,
+ date: Date,
+ rating: number
+ }[]> {
+
+ let uri = `geo:${lat},${lon}?u=50`;
+ if (name !== undefined && name !== null) {
+ uri += "&q=" + name;
+ }
+ const reviewsSource : UIEventSource< {
+ comment?: string,
+ author: string,
+ date: Date,
+ rating: number
+ }[]> = new UIEventSource([]);
+
+ mangrove.getReviews({sub: uri}).then(
+ (data) => {
+ const reviews = [];
+ for (const review of data.reviews) {
+ const r = review.payload;
+ reviews.push({
+ date: new Date(r.iat * 1000),
+ comment: r.opinion,
+ author: r.metadata.nickname,
+ rating: r.rating // percentage points
+ })
+ }
+ reviewsSource.setData(reviews)
+ }
+ );
+ return reviewsSource;
+ }
+
+
+}
\ No newline at end of file
diff --git a/Svg.ts b/Svg.ts
index e49ccaa..5b6bafd 100644
--- a/Svg.ts
+++ b/Svg.ts
@@ -239,6 +239,11 @@ export default class Svg {
public static star_svg() { return new FixedUiElement(Svg.star);}
public static star_ui() { return new FixedUiElement(Svg.star_img);}
+ public static star_half = " "
+ public static star_half_img = Img.AsImageElement(Svg.star_half)
+ public static star_half_svg() { return new FixedUiElement(Svg.star_half);}
+ public static star_half_ui() { return new FixedUiElement(Svg.star_half_img);}
+
public static statistics = " "
public static statistics_img = Img.AsImageElement(Svg.statistics)
public static statistics_svg() { return new FixedUiElement(Svg.statistics);}
@@ -264,4 +269,4 @@ export default class Svg {
public static wikipedia_svg() { return new FixedUiElement(Svg.wikipedia);}
public static wikipedia_ui() { return new FixedUiElement(Svg.wikipedia_img);}
-public static All = {"add.svg": Svg.add,"addSmall.svg": Svg.addSmall,"ampersand.svg": Svg.ampersand,"arrow-left-smooth.svg": Svg.arrow_left_smooth,"arrow-right-smooth.svg": Svg.arrow_right_smooth,"bug.svg": Svg.bug,"camera-plus.svg": Svg.camera_plus,"checkmark.svg": Svg.checkmark,"circle.svg": Svg.circle,"clock.svg": Svg.clock,"close.svg": Svg.close,"compass.svg": Svg.compass,"cross_bottom_right.svg": Svg.cross_bottom_right,"crosshair-blue-center.svg": Svg.crosshair_blue_center,"crosshair-blue.svg": Svg.crosshair_blue,"crosshair.svg": Svg.crosshair,"delete_icon.svg": Svg.delete_icon,"direction.svg": Svg.direction,"direction_gradient.svg": Svg.direction_gradient,"down.svg": Svg.down,"envelope.svg": Svg.envelope,"floppy.svg": Svg.floppy,"gear.svg": Svg.gear,"help.svg": Svg.help,"home.svg": Svg.home,"home_white_bg.svg": Svg.home_white_bg,"josm_logo.svg": Svg.josm_logo,"layers.svg": Svg.layers,"layersAdd.svg": Svg.layersAdd,"logo.svg": Svg.logo,"logout.svg": Svg.logout,"mapillary.svg": Svg.mapillary,"no_checkmark.svg": Svg.no_checkmark,"or.svg": Svg.or,"osm-logo-us.svg": Svg.osm_logo_us,"osm-logo.svg": Svg.osm_logo,"pencil.svg": Svg.pencil,"phone.svg": Svg.phone,"pin.svg": Svg.pin,"pop-out.svg": Svg.pop_out,"reload.svg": Svg.reload,"ring.svg": Svg.ring,"search.svg": Svg.search,"send_email.svg": Svg.send_email,"share.svg": Svg.share,"square.svg": Svg.square,"star.svg": Svg.star,"statistics.svg": Svg.statistics,"up.svg": Svg.up,"wikidata.svg": Svg.wikidata,"wikimedia-commons-white.svg": Svg.wikimedia_commons_white,"wikipedia.svg": Svg.wikipedia};}
+public static All = {"add.svg": Svg.add,"addSmall.svg": Svg.addSmall,"ampersand.svg": Svg.ampersand,"arrow-left-smooth.svg": Svg.arrow_left_smooth,"arrow-right-smooth.svg": Svg.arrow_right_smooth,"bug.svg": Svg.bug,"camera-plus.svg": Svg.camera_plus,"checkmark.svg": Svg.checkmark,"circle.svg": Svg.circle,"clock.svg": Svg.clock,"close.svg": Svg.close,"compass.svg": Svg.compass,"cross_bottom_right.svg": Svg.cross_bottom_right,"crosshair-blue-center.svg": Svg.crosshair_blue_center,"crosshair-blue.svg": Svg.crosshair_blue,"crosshair.svg": Svg.crosshair,"delete_icon.svg": Svg.delete_icon,"direction.svg": Svg.direction,"direction_gradient.svg": Svg.direction_gradient,"down.svg": Svg.down,"envelope.svg": Svg.envelope,"floppy.svg": Svg.floppy,"gear.svg": Svg.gear,"help.svg": Svg.help,"home.svg": Svg.home,"home_white_bg.svg": Svg.home_white_bg,"josm_logo.svg": Svg.josm_logo,"layers.svg": Svg.layers,"layersAdd.svg": Svg.layersAdd,"logo.svg": Svg.logo,"logout.svg": Svg.logout,"mapillary.svg": Svg.mapillary,"no_checkmark.svg": Svg.no_checkmark,"or.svg": Svg.or,"osm-logo-us.svg": Svg.osm_logo_us,"osm-logo.svg": Svg.osm_logo,"pencil.svg": Svg.pencil,"phone.svg": Svg.phone,"pin.svg": Svg.pin,"pop-out.svg": Svg.pop_out,"reload.svg": Svg.reload,"ring.svg": Svg.ring,"search.svg": Svg.search,"send_email.svg": Svg.send_email,"share.svg": Svg.share,"square.svg": Svg.square,"star.svg": Svg.star,"star_half.svg": Svg.star_half,"statistics.svg": Svg.statistics,"up.svg": Svg.up,"wikidata.svg": Svg.wikidata,"wikimedia-commons-white.svg": Svg.wikimedia_commons_white,"wikipedia.svg": Svg.wikipedia};}
diff --git a/UI/Popup/TagRenderingQuestion.ts b/UI/Popup/TagRenderingQuestion.ts
index 2bf52d7..1cdd805 100644
--- a/UI/Popup/TagRenderingQuestion.ts
+++ b/UI/Popup/TagRenderingQuestion.ts
@@ -198,9 +198,12 @@ export default class TagRenderingQuestion extends UIElement {
private GenerateMappingElement(mapping: {
if: TagsFilter,
then: Translation,
- hideInAnswer: boolean
+ hideInAnswer: boolean | TagsFilter
}): InputElement {
- if (mapping.hideInAnswer) {
+ if (mapping.hideInAnswer === true) {
+ return undefined;
+ }
+ if(typeof(mapping.hideInAnswer) !== "boolean" && mapping.hideInAnswer.matches(this._tags.data)){
return undefined;
}
return new FixedInputElement(
diff --git a/UI/ReviewElement.ts b/UI/ReviewElement.ts
new file mode 100644
index 0000000..858c239
--- /dev/null
+++ b/UI/ReviewElement.ts
@@ -0,0 +1,65 @@
+import {UIElement} from "./UIElement";
+import {UIEventSource} from "../Logic/UIEventSource";
+import Translations from "./i18n/Translations";
+import Combine from "./Base/Combine";
+import {FixedUiElement} from "./Base/FixedUiElement";
+import {Utils} from "../Utils";
+
+/**
+ * Shows the reviews and scoring base on mangrove.reviesw
+ */
+export default class ReviewElement extends UIElement {
+ private _reviews: UIEventSource<{ comment?: string; author: string; date: Date; rating: number }[]>;
+
+ constructor(reviews: UIEventSource<{
+ comment?: string,
+ author: string,
+ date: Date,
+ rating: number
+ }[]>) {
+ super(reviews);
+ this._reviews = reviews;
+ }
+
+ InnerRender(): string {
+
+ const elements = [];
+
+ elements.push(Translations.t.reviews.title.SetClass("review-title"));
+
+ elements.push(...this._reviews.data.map(review => {
+ const stars = Math.round(review.rating / 10)
+ const fullStars = Math.floor(stars / 2);
+ const d = review.date;
+
+ return new Combine(
+ [
+ new Combine([
+
+ new Combine([
+ "".repeat(fullStars),
+ stars % 2 == 1 ? "" : ""
+ ]).SetClass("review-rating"),
+ new FixedUiElement(`${d.getFullYear()}-${Utils.TwoDigits(d.getMonth() + 1)}-${Utils.TwoDigits(d.getDate())} ${Utils.TwoDigits(d.getHours())}:${Utils.TwoDigits(d.getMinutes())}`)
+ .SetClass("review-date"),
+ ]).SetClass("review-stars-date"),
+
+ new FixedUiElement(review.comment).SetClass("review-comment"),
+ " ",
+ new FixedUiElement(review.author).SetClass("review-author"),
+
+ ]
+ ).SetClass("review-element")
+ }));
+ elements.push(
+ new Combine([
+ Translations.t.reviews.attribution,
+ ""
+ ])
+
+ .SetClass("review-attribution"))
+
+ return new Combine(elements).SetClass("review").Render();
+ }
+
+}
\ No newline at end of file
diff --git a/assets/mangrove_logo.png b/assets/mangrove_logo.png
new file mode 100644
index 0000000..38f39f8
Binary files /dev/null and b/assets/mangrove_logo.png differ
diff --git a/assets/svg/star_half.svg b/assets/svg/star_half.svg
new file mode 100644
index 0000000..db57d41
--- /dev/null
+++ b/assets/svg/star_half.svg
@@ -0,0 +1,63 @@
+
+
diff --git a/assets/translations.json b/assets/translations.json
index d5ce8bf..ae6b47d 100644
--- a/assets/translations.json
+++ b/assets/translations.json
@@ -907,5 +907,19 @@
"gl": "Recargar os datos",
"de": "Daten neu laden"
}
+ },
+ "reviews": {
+ "title": {
+ "en": "Reviews",
+ "nl": "Beoordelingen"
+ },
+ "no_reviews_yet": {
+ "en": "There are no reviews yet. Be the first to write one and help open data and the business!",
+ "nl": "Er zijn nog geen beoordelingen. Wees de eerste om een beoordeling te schrijven en help open data en het bedrijf"
+ },
+ "attribution": {
+ "en": "Reviews are powered by Mangrove Reviews and are available under CC-BY 4.0",
+ "nl": "De beoordelingen worden voorzien door Mangrove Reviews en zijn beschikbaar onder deCC-BY 4.0-licentie "
+ }
}
}
\ No newline at end of file
diff --git a/css/ReviewElement.css b/css/ReviewElement.css
new file mode 100644
index 0000000..9389fc5
--- /dev/null
+++ b/css/ReviewElement.css
@@ -0,0 +1,56 @@
+.review-rating img {
+ max-width: 1em;
+ height: 1em;
+}
+
+.review-rating {
+ display: flex;
+ flex-direction: row;
+}
+
+.review-date {
+ color: var(--subtle-detail-color-light-contrast);
+}
+
+.review-stars-date {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 0.5em;
+}
+
+.review-author {
+ font-weight: bold;
+ display: flex;
+ justify-content: flex-end;
+}
+
+.review-element {
+ padding: 1em;
+ margin: 0.5em;
+ display: block;
+ border-radius: 1em;
+ background-color: var(--subtle-detail-color);
+ color: var(--subtle-detail-color-contrast);
+}
+
+.review-attribution {
+ display: flex;
+ color: var(--subtle-detail-color-light-contrast);
+ justify-content: flex-end;
+ margin-right: 1em;
+}
+
+.review-attribution span {
+ width: calc(65% - 3em);
+ text-align: right;
+ max-width: 20em;
+}
+
+.review-attribution img {
+ height: 3em;
+ margin-left: 0.5em;
+}
+
+.review-title {
+ font-size: x-large;
+}
\ No newline at end of file
diff --git a/customGenerator.html b/customGenerator.html
index a87a8d4..ed8dec7 100644
--- a/customGenerator.html
+++ b/customGenerator.html
@@ -91,7 +91,8 @@
- 'maindiv' not attached
+ Loading the MapComplete custom theme builder...
+ If this message persists, make sure javascript is enabled and no script blocker is blocking this.