diff --git a/Logic/Actors/GeoLocationHandler.ts b/Logic/Actors/GeoLocationHandler.ts index 6f702b768..6752878bb 100644 --- a/Logic/Actors/GeoLocationHandler.ts +++ b/Logic/Actors/GeoLocationHandler.ts @@ -2,8 +2,10 @@ import { QueryParameters } from "../Web/QueryParameters" import { BBox } from "../BBox" import Constants from "../../Models/Constants" import { GeoLocationPointProperties, GeoLocationState } from "../State/GeoLocationState" -import State from "../../State" import { UIEventSource } from "../UIEventSource" +import Loc from "../../Models/Loc" +import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig" +import SimpleFeatureSource from "../FeatureSource/Sources/SimpleFeatureSource" /** * The geolocation-handler takes a map-location and a geolocation state. @@ -12,12 +14,24 @@ import { UIEventSource } from "../UIEventSource" */ export default class GeoLocationHandler { public readonly geolocationState: GeoLocationState - private readonly _state: State + private readonly _state: { + currentUserLocation: SimpleFeatureSource + layoutToUse: LayoutConfig + locationControl: UIEventSource + selectedElement: UIEventSource + leafletMap?: UIEventSource + } public readonly mapHasMoved: UIEventSource = new UIEventSource(false) constructor( geolocationState: GeoLocationState, - state: State // { locationControl: UIEventSource, selectedElement: UIEventSource, leafletMap?: UIEventSource }) + state: { + locationControl: UIEventSource + currentUserLocation: SimpleFeatureSource + layoutToUse: LayoutConfig + selectedElement: UIEventSource + leafletMap?: UIEventSource + } ) { this.geolocationState = geolocationState this._state = state diff --git a/Logic/GeoOperations.ts b/Logic/GeoOperations.ts index b5a34956a..6406b379a 100644 --- a/Logic/GeoOperations.ts +++ b/Logic/GeoOperations.ts @@ -209,7 +209,7 @@ export class GeoOperations { * GeoOperations.inside([1.42822265625, 48.61838518688487], multiPolygon) // => false * GeoOperations.inside([4.02099609375, 47.81315451752768], multiPolygon) // => false */ - public static inside(pointCoordinate, feature): boolean { + public static inside(pointCoordinate: [number, number] | Feature, feature): boolean { // ray-casting algorithm based on // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html @@ -217,8 +217,8 @@ export class GeoOperations { return false } - if (pointCoordinate.geometry !== undefined) { - pointCoordinate = pointCoordinate.geometry.coordinates + if (pointCoordinate["geometry"] !== undefined) { + pointCoordinate = pointCoordinate["geometry"].coordinates } const x: number = pointCoordinate[0] diff --git a/Logic/UIEventSource.ts b/Logic/UIEventSource.ts index f9d49b9a0..21498d047 100644 --- a/Logic/UIEventSource.ts +++ b/Logic/UIEventSource.ts @@ -1,4 +1,5 @@ import { Utils } from "../Utils" +import { Readable, Subscriber, Unsubscriber } from "svelte/store" /** * Various static utils @@ -88,7 +89,7 @@ export class Stores { } } -export abstract class Store { +export abstract class Store implements Readable { abstract readonly data: T /** @@ -113,6 +114,18 @@ export abstract class Store { abstract map(f: (t: T) => J): Store abstract map(f: (t: T) => J, extraStoresToWatch: Store[]): Store + public mapD(f: (t: T) => J, extraStoresToWatch: Store[]): Store { + return this.map((t) => { + if (t === undefined) { + return undefined + } + if (t === null) { + return null + } + return f(t) + }, extraStoresToWatch) + } + /** * Add a callback function which will run on future data changes */ @@ -258,6 +271,17 @@ export abstract class Store { } }) } + + /** + * Same as 'addCallbackAndRun', added to be compatible with Svelte + * @param run + * @param invalidate + */ + public subscribe(run: Subscriber & ((value: T) => void), invalidate?): Unsubscriber { + // We don't need to do anything with 'invalidate', see + // https://github.com/sveltejs/svelte/issues/3859 + return this.addCallbackAndRun(run) + } } export class ImmutableStore extends Store { diff --git a/UI/Base/SvelteUIElement.ts b/UI/Base/SvelteUIElement.ts index 929cd3299..67115f3c4 100644 --- a/UI/Base/SvelteUIElement.ts +++ b/UI/Base/SvelteUIElement.ts @@ -1,13 +1,26 @@ import BaseUIElement from "../BaseUIElement" +import { SvelteComponentTyped } from "svelte" + /** * The SvelteUIComponent serves as a translating class which which wraps a SvelteElement into the BaseUIElement framework. */ -export default class SvelteUIElement extends BaseUIElement { - private readonly _svelteComponent - private readonly _props: Record +export default class SvelteUIElement< + Props extends Record = any, + Events extends Record = any, + Slots extends Record = any +> extends BaseUIElement { + private readonly _svelteComponent: { + new (args: { + target: HTMLElement + props: Props + events?: Events + slots?: Slots + }): SvelteComponentTyped + } + private readonly _props: Props - constructor(svelteElement, props: Record) { + constructor(svelteElement, props: Props) { super() this._svelteComponent = svelteElement this._props = props diff --git a/UI/BigComponents/CommunityIndexView.svelte b/UI/BigComponents/CommunityIndexView.svelte new file mode 100644 index 000000000..61228990e --- /dev/null +++ b/UI/BigComponents/CommunityIndexView.svelte @@ -0,0 +1,14 @@ + diff --git a/UI/BigComponents/ContactLink.svelte b/UI/BigComponents/ContactLink.svelte new file mode 100644 index 000000000..88b81308b --- /dev/null +++ b/UI/BigComponents/ContactLink.svelte @@ -0,0 +1,31 @@ + + + +
+ {#if $country?.nameEn} +

{$country?.nameEn}

+ {/if} + {#each $resources as resource} + + {/each} +
diff --git a/assets/community_index_global_resources.json b/assets/community_index_global_resources.json new file mode 100644 index 000000000..6978a956a --- /dev/null +++ b/assets/community_index_global_resources.json @@ -0,0 +1,355 @@ +{ + "OSM-Discord": { + "id": "OSM-Discord", + "type": "discord", + "account": "openstreetmap", + "locationSet": { + "include": [ + "001" + ] + }, + "languageCodes": [ + "de", + "en", + "es", + "fr", + "it", + "pt-BR", + "ro", + "tr" + ], + "order": 6, + "strings": { + "name": "OpenStreetMap World Discord" + }, + "contacts": [ + { + "name": "Austin Harrison", + "email": "jaustinharrison@gmail.com" + } + ], + "resolved": { + "name": "OpenStreetMap World Discord", + "url": "https://discord.gg/openstreetmap", + "description": "Get in touch with other mappers on Discord", + "nameHTML": "OpenStreetMap World Discord", + "urlHTML": "https://discord.gg/openstreetmap", + "descriptionHTML": "Get in touch with other mappers on Discord" + } + }, + "OSM-Discourse": { + "id": "OSM-Discourse", + "type": "discourse", + "locationSet": { + "include": [ + "001" + ] + }, + "languageCodes": [ + "de", + "en", + "es", + "nl", + "pl", + "pt-BR" + ], + "order": 7, + "strings": { + "name": "OpenStreetMap Discourse", + "description": "A shared place for conversations about OpenStreetMap", + "url": "https://community.openstreetmap.org/" + }, + "contacts": [ + { + "name": "Grant Slater", + "email": "osmfuture@firefishy.com" + }, + { + "name": "Rubén Martín", + "email": "nukeador@protonmail.com" + } + ], + "resolved": { + "name": "OpenStreetMap Discourse", + "url": "https://community.openstreetmap.org/", + "description": "A shared place for conversations about OpenStreetMap", + "nameHTML": "OpenStreetMap Discourse", + "urlHTML": "https://community.openstreetmap.org/", + "descriptionHTML": "A shared place for conversations about OpenStreetMap" + } + }, + "OSM-Facebook": { + "id": "OSM-Facebook", + "type": "facebook", + "account": "OpenStreetMap", + "locationSet": { + "include": [ + "001" + ] + }, + "languageCodes": [ + "en" + ], + "order": 3, + "strings": { + "community": "OpenStreetMap", + "communityID": "openstreetmap", + "description": "Like us on Facebook for news and updates about OpenStreetMap." + }, + "contacts": [ + { + "name": "Harry Wood", + "email": "mail@harrywood.co.uk" + } + ], + "resolved": { + "name": "OpenStreetMap on Facebook", + "url": "https://www.facebook.com/OpenStreetMap", + "description": "Like us on Facebook for news and updates about OpenStreetMap.", + "nameHTML": "OpenStreetMap on Facebook", + "urlHTML": "https://www.facebook.com/OpenStreetMap", + "descriptionHTML": "Like us on Facebook for news and updates about OpenStreetMap." + } + }, + "OSM-help": { + "id": "OSM-help", + "type": "forum", + "locationSet": { + "include": [ + "001" + ] + }, + "languageCodes": [ + "en" + ], + "order": -2, + "strings": { + "name": "OpenStreetMap Help", + "description": "Ask a question and get answers on OSM's community-driven question and answer site.", + "extendedDescription": "{url} is for everyone who needs help with OpenStreetMap. Whether you are a beginner mapper or have a technical question, we're here to help!", + "url": "https://help.openstreetmap.org/" + }, + "contacts": [ + { + "name": "OSMF Operations", + "email": "operations@osmfoundation.org" + } + ], + "resolved": { + "name": "OpenStreetMap Help", + "url": "https://help.openstreetmap.org/", + "description": "Ask a question and get answers on OSM's community-driven question and answer site.", + "extendedDescription": "https://help.openstreetmap.org/ is for everyone who needs help with OpenStreetMap. Whether you are a beginner mapper or have a technical question, we're here to help!", + "nameHTML": "OpenStreetMap Help", + "urlHTML": "https://help.openstreetmap.org/", + "descriptionHTML": "Ask a question and get answers on OSM's community-driven question and answer site.", + "extendedDescriptionHTML": "https://help.openstreetmap.org/ is for everyone who needs help with OpenStreetMap. Whether you are a beginner mapper or have a technical question, we're here to help!" + } + }, + "OSM-IRC": { + "id": "OSM-IRC", + "type": "irc", + "account": "osm", + "locationSet": { + "include": [ + "001" + ] + }, + "languageCodes": [ + "en" + ], + "order": -4, + "strings": { + "community": "OpenStreetMap", + "communityID": "openstreetmap" + }, + "contacts": [ + { + "name": "Harry Wood", + "email": "mail@harrywood.co.uk" + } + ], + "resolved": { + "name": "OpenStreetMap on IRC", + "url": "https://webchat.oftc.net/?channels=osm", + "description": "Join #osm on irc.oftc.net (port 6667)", + "nameHTML": "OpenStreetMap on IRC", + "urlHTML": "https://webchat.oftc.net/?channels=osm", + "descriptionHTML": "Join #osm on irc.oftc.net (port 6667)" + } + }, + "OSM-Mastodon": { + "id": "OSM-Mastodon", + "type": "mastodon", + "account": "openstreetmap", + "locationSet": { + "include": [ + "001" + ] + }, + "languageCodes": [ + "en" + ], + "order": 3, + "strings": { + "community": "OpenStreetMap", + "communityID": "openstreetmap", + "url": "https://en.osm.town/@openstreetmap" + }, + "contacts": [ + { + "name": "Harry Wood", + "email": "mail@harrywood.co.uk" + } + ], + "resolved": { + "name": "OpenStreetMap Mastodon Account", + "url": "https://en.osm.town/@openstreetmap", + "description": "The official Mastodon account for OpenStreetMap", + "nameHTML": "OpenStreetMap Mastodon Account", + "urlHTML": "https://en.osm.town/@openstreetmap", + "descriptionHTML": "The official Mastodon account for OpenStreetMap" + } + }, + "OSM-Reddit": { + "id": "OSM-Reddit", + "type": "reddit", + "account": "openstreetmap", + "locationSet": { + "include": [ + "001" + ] + }, + "languageCodes": [ + "en" + ], + "order": 2, + "strings": { + "community": "OpenStreetMap", + "communityID": "openstreetmap", + "description": "/r/{account} is a great place to learn more about OpenStreetMap. Ask us anything!" + }, + "contacts": [ + { + "name": "Serge Wroclawski", + "email": "emacsen@gmail.com" + } + ], + "resolved": { + "name": "OpenStreetMap on Reddit", + "url": "https://www.reddit.com/r/openstreetmap", + "description": "/r/openstreetmap is a great place to learn more about OpenStreetMap. Ask us anything!", + "nameHTML": "OpenStreetMap on Reddit", + "urlHTML": "https://www.reddit.com/r/openstreetmap", + "descriptionHTML": "/r/openstreetmap is a great place to learn more about OpenStreetMap. Ask us anything!" + } + }, + "OSM-Telegram": { + "id": "OSM-Telegram", + "type": "telegram", + "account": "OpenStreetMapOrg", + "locationSet": { + "include": [ + "001" + ] + }, + "languageCodes": [ + "en" + ], + "order": 5, + "strings": { + "community": "OpenStreetMap", + "communityID": "openstreetmap", + "description": "Join the OpenStreetMap Telegram global supergroup at {url}" + }, + "contacts": [ + { + "name": "Max N", + "email": "abonnements@revolwear.com" + } + ], + "resolved": { + "name": "OpenStreetMap Telegram", + "url": "https://t.me/OpenStreetMapOrg", + "description": "Join the OpenStreetMap Telegram global supergroup at https://t.me/OpenStreetMapOrg", + "nameHTML": "OpenStreetMap Telegram", + "urlHTML": "https://t.me/OpenStreetMapOrg", + "descriptionHTML": "Join the OpenStreetMap Telegram global supergroup at https://t.me/OpenStreetMapOrg" + } + }, + "OSM-Twitter": { + "id": "OSM-Twitter", + "type": "twitter", + "account": "openstreetmap", + "locationSet": { + "include": [ + "001" + ] + }, + "languageCodes": [ + "en" + ], + "order": 4, + "strings": { + "community": "OpenStreetMap", + "communityID": "openstreetmap" + }, + "contacts": [ + { + "name": "Harry Wood", + "email": "mail@harrywood.co.uk" + } + ], + "resolved": { + "name": "OpenStreetMap on Twitter", + "url": "https://twitter.com/openstreetmap", + "description": "Follow us on Twitter", + "nameHTML": "OpenStreetMap on Twitter", + "urlHTML": "https://twitter.com/openstreetmap", + "descriptionHTML": "Follow us on Twitter" + } + }, + "OSMF": { + "id": "OSMF", + "type": "osm-lc", + "locationSet": { + "include": [ + "001" + ] + }, + "languageCodes": [ + "en", + "fr", + "it", + "ja", + "nl", + "ru" + ], + "order": 10, + "strings": { + "name": "OpenStreetMap Foundation", + "description": "OSMF is a UK-based not-for-profit that supports the OpenStreetMap Project", + "extendedDescription": "OSMF supports the OpenStreetMap project by fundraising, maintaining the servers which power OSM, organizing the annual State of the Map conference, and coordinating the volunteers who keep OSM running. You can show your support and have a voice in the direction of OpenStreetMap by joining as an OSMF member here: {signupUrl}", + "signupUrl": "https://join.osmfoundation.org/", + "url": "https://wiki.osmfoundation.org/wiki/Main_Page" + }, + "contacts": [ + { + "name": "OSMF Board", + "email": "board@osmfoundation.org" + } + ], + "resolved": { + "name": "OpenStreetMap Foundation", + "url": "https://wiki.osmfoundation.org/wiki/Main_Page", + "signupUrl": "https://join.osmfoundation.org/", + "description": "OSMF is a UK-based not-for-profit that supports the OpenStreetMap Project", + "extendedDescription": "OSMF supports the OpenStreetMap project by fundraising, maintaining the servers which power OSM, organizing the annual State of the Map conference, and coordinating the volunteers who keep OSM running. You can show your support and have a voice in the direction of OpenStreetMap by joining as an OSMF member here: https://join.osmfoundation.org/", + "nameHTML": "OpenStreetMap Foundation", + "urlHTML": "https://wiki.osmfoundation.org/wiki/Main_Page", + "signupUrlHTML": "https://join.osmfoundation.org/", + "descriptionHTML": "OSMF is a UK-based not-for-profit that supports the OpenStreetMap Project", + "extendedDescriptionHTML": "OSMF supports the OpenStreetMap project by fundraising, maintaining the servers which power OSM, organizing the annual State of the Map conference, and coordinating the volunteers who keep OSM running. You can show your support and have a voice in the direction of OpenStreetMap by joining as an OSMF member here: https://join.osmfoundation.org/" + } + } +} diff --git a/package-lock.json b/package-lock.json index 35cd58c8d..47dcea3c6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -72,7 +72,7 @@ "dependency-cruiser": "^10.4.0", "fs": "0.0.1-security", "mocha": "^9.2.2", - "prettier": "2.7.1", + "prettier": "^2.7.1", "prettier-plugin-svelte": "^2.9.0", "read-file": "^0.2.0", "sass": "^1.57.1", diff --git a/package.json b/package.json index f3d750437..f52ef5bb4 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "generate": "mkdir -p ./assets/generated; npm run generate:licenses; npm run generate:images; npm run generate:charging-stations; npm run generate:translations; npm run reset:layeroverview; npm run generate:service-worker", "generate:charging-stations": "cd ./assets/layers/charging_station && ts-node csvToJson.ts && cd -", "prepare-deploy": "npm run generate:service-worker && ./scripts/build.sh", - "format": "npx prettier --write '**/*.ts'", + "format": "npx prettier --write --svelte-bracket-new-line=false --html-whitespace-sensitivity=ignore '**/*.ts' '**/*.svelte'", "clean:tests": "(find . -type f -name \"*.doctest.ts\" | xargs rm)", "clean": "rm -rf .cache/ && (find *.html | grep -v \"^\\(404\\|index\\|land\\|test\\|preferences\\|customGenerator\\|professional\\|automaton\\|import_helper\\|import_viewer\\|theme\\).html\" | xargs rm) && (ls | grep \"^index_[a-zA-Z_-]\\+\\.ts$\" | xargs rm) && (ls | grep \".*.webmanifest$\" | grep -v \"manifest.webmanifest\" | xargs rm)", "generate:dependency-graph": "node_modules/.bin/depcruise --exclude \"^node_modules\" --output-type dot Logic/State/MapState.ts > dependencies.dot && dot dependencies.dot -T svg -o dependencies.svg && rm dependencies.dot", @@ -127,7 +127,7 @@ "dependency-cruiser": "^10.4.0", "fs": "0.0.1-security", "mocha": "^9.2.2", - "prettier": "2.7.1", + "prettier": "^2.7.1", "prettier-plugin-svelte": "^2.9.0", "read-file": "^0.2.0", "sass": "^1.57.1", diff --git a/test.ts b/test.ts index 909cd17fa..7c1a0d9be 100644 --- a/test.ts +++ b/test.ts @@ -1,90 +1,25 @@ -import MangroveReviewsOfFeature, { MangroveIdentity } from "./Logic/Web/MangroveReviews" -import { Feature, Point } from "geojson" -import { OsmTags } from "./Models/OsmFeature" -import { VariableUiElement } from "./UI/Base/VariableUIElement" +import ContactLink from "./UI/BigComponents/ContactLink.svelte" +import SvelteUIElement from "./UI/Base/SvelteUIElement" +import { Utils } from "./Utils" import List from "./UI/Base/List" -import { UIEventSource } from "./Logic/UIEventSource" -import UserRelatedState from "./Logic/State/UserRelatedState" +import { GeoOperations } from "./Logic/GeoOperations" +import { Tiles } from "./Models/TileRange" +import { Stores } from "./Logic/UIEventSource" -const feature: Feature = { - type: "Feature", - id: "node/6739848322", - properties: { - "addr:city": "San Diego", - "addr:housenumber": "2816", - "addr:postcode": "92106", - "addr:street": "Historic Decatur Road", - "addr:unit": "116", - amenity: "restaurant", - cuisine: "burger", - delivery: "yes", - "diet:halal": "no", - "diet:vegetarian": "yes", - dog: "yes", - image: "https://i.imgur.com/AQlGNHQ.jpg", - internet_access: "wlan", - "internet_access:fee": "no", - "internet_access:ssid": "Public-stinebrewingCo", - microbrewery: "yes", - name: "Stone Brewing World Bistro & Gardens", - opening_hours: "Mo-Fr, Su 11:30-21:00; Sa 11:30-22:00", - organic: "no", - "payment:cards": "yes", - "payment:cash": "yes", - "service:electricity": "ask", - takeaway: "yes", - website: "https://www.stonebrewing.com/visit/bistros/liberty-station", - wheelchair: "designated", - "_last_edit:contributor": "Drew Dowling", - "_last_edit:timestamp": "2023-01-11T23:22:28Z", - id: "node/6739848322", - timestamp: "2023-01-11T23:22:28Z", - user: "Drew Dowling", - _backend: "https://www.openstreetmap.org", - _lat: "32.7404614", - _lon: "-117.211684", - _layer: "food", - _length: "0", - "_length:km": "0.0", - "_now:date": "2023-01-20", - "_now:datetime": "2023-01-20 17:46:54", - "_loaded:date": "2023-01-20", - "_loaded:datetime": "2023-01-20 17:46:54", - "_geometry:type": "Point", - _surface: "0", - "_surface:ha": "0", - _country: "us", - }, - geometry: { - type: "Point", - coordinates: [0, 0], - }, -} -const state = new UserRelatedState(undefined) - -state.allElements.addOrGetElement(feature) - -const reviews = MangroveReviewsOfFeature.construct(feature, state) - -reviews.reviews.addCallbackAndRun((r) => { - console.log("Reviews are:", r) -}) -window.setTimeout(async () => { - await reviews.createReview({ - opinion: "Cool bar", - rating: 90, - metadata: { - nickname: "Pietervdvn", - }, - }) - console.log("Submitted review") -}, 1000) - -new VariableUiElement( - reviews.reviews.map( - (reviews) => - new List( - reviews.map((r) => r.rating + "% " + r.opinion + " (" + r.metadata.nickname + ")") - ) +async function main() { + const location: [number, number] = [3.21, 51.2] + const t = Tiles.embedded_tile(location[1], location[0], 6) + const url = `https://raw.githubusercontent.com/pietervdvn/MapComplete-data/main/community_index/tile_${t.z}_${t.x}_${t.y}.geojson` + const be = Stores.FromPromise(Utils.downloadJson(url)).mapD( + (data) => data.features.find((f) => GeoOperations.inside(location, f)).properties ) -).AttachTo("maindiv") + new SvelteUIElement(ContactLink, { country: be }).AttachTo("maindiv") + /* + const links = data.features + .filter((f) => GeoOperations.inside(location, f)) + .map((f) => new SvelteUIElement(ContactLink, { country: f.properties })) + new List(links).AttachTo("maindiv") + //*/ +} + +main().then((_) => {})