From e09af8fcb9e3f3c0bbffab98bd60717f8ae5d1ef Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Mon, 23 Sep 2024 13:32:40 +0200 Subject: [PATCH] Chore: remove obsolete documentation, add explanation --- Docs/Architecture.md | 221 ------------------------------ src/Logic/Osm/ChangesetHandler.ts | 2 +- 2 files changed, 1 insertion(+), 222 deletions(-) delete mode 100644 Docs/Architecture.md diff --git a/Docs/Architecture.md b/Docs/Architecture.md deleted file mode 100644 index 94ac0d25a..000000000 --- a/Docs/Architecture.md +++ /dev/null @@ -1,221 +0,0 @@ -Architecture -============ - -This document aims to give an architectural overview of how MapComplete is built. It should give some feeling on how -everything fits together. - -Servers? --------- - -There are no servers for MapComplete, all services are configured by third parties. - -Minimal HTML - Minimal CSS --------------------------- - -There is quasi no HTML. Most of the components are generated by TypeScript and attached dynamically. The HTML is a -barebones skeleton which serves every theme. - - -The UIEventSource ------------------ - -Most (but not all) objects in MapComplete get all the state they need as a parameter in the constructor. However, as is -the case with most graphical applications, there are quite some dynamical values. - -All values which change regularly are wrapped into -a [`UIEventSource`](../Logic/UIEventSource.ts). A `UIEventSource` is a -wrapper containing a value and offers the possibility to add a callback function which is called every time the value is -changed (with `setData`) - -Furthermore, there are various helper functions, the most widely used one being `map` - generating a new event source -with the new value applied. Note that `map` will also absorb some changes, -e.g. `const someEventSource : UIEventSource = ... ; someEventSource.map(list = list.length)` will only trigger -when the length of the list has changed. - -An object which receives a `UIEventSource` is responsible of responding to changes of this object. This is especially -true for UI-components. - -UI --- -```typescript - -export default class MyComponent { - - constructor(neededParameters, neededUIEventSources) { - - } - -} - -``` - -The Graphical User Interface is composed of various UI-elements. For every UI-element, there is a `BaseUIElement` which creates the actual `HTMLElement` when needed. - -There are some basic elements, such as: - -- `FixedUIElement` which shows a fixed, unchangeable element -- `Img` to show an image -- `Combine` which wraps everything given (strings and other elements) in a div -- `List` - -There is one special component: the `VariableUIElement` -The `VariableUIElement` takes a `UIEventSource` and will dynamically show whatever the `UIEventSource` contains at the moment. - -For example: - -```typescript - -const src : UIEventSource = ... // E.g. user input, data that will be updated... new VariableUIElement(src) -.AttachTo('some-id') // attach it to the html - -``` - -Note that every component offers support for `onClick( someCallBack)` - -### Translations - -To add a translation: - -1. Open `langs/en.json` -2. Find a correct spot for your translation in the tree -3. run `npm run generate:translations` -4. `import Translations` -5. `Translations.t..Clone()` is the `UIElement` offering your translation - -### Input elements - -Input elements are a special kind of BaseElement which offer a piece of a form to the user, e.g. a TextField, a Radio button, a dropdown, ... - -The constructor will ask all the parameters to configure them. The actual value can be obtained via `inputElement.GetValue()`, which is a `UIEventSource` that will be triggered every time the user changes the input. - -### Advanced elements - -There are some components which offer useful functionality: - - -- The `subtleButton` which is a friendly, big button -- The Toggle: `const t = new Toggle( componentA, componentB, source)` is a `UIEventSource` which shows `componentA` as long as `source` contains `true` and will show `componentB` otherwise. - - -### Styling - -Styling is done as much as possible with [TailwindCSS](https://tailwindcss.com/). It contains a ton of utility classes, each of them containing a few rules. - -For example: ` someBaseUIElement.SetClass("flex flex-col border border-black rounded-full")` will set the component to be a flex object, as column, with a black border and pill-shaped. - -If Tailwind is not enough, use `baseUiElement.SetStyle("background: red; someOtherCssRule: abc;")`. - -### An example - -For example: the user should input whether or not a shop is closed during public holidays. There are three options: - -1. closed -2. opened as usual -3. opened with different hours as usual - -In the case of different hours, input hours should be too. - -This can be constructed as following: - - -```typescript - - // We construct the dropdown element with values and labelshttps://tailwindcss.com/ - const isOpened = new Dropdown(Translations.t.is_this_shop_opened_during_holidays, - [ - { value: "closed", Translation.t.shop_closed_during_holidays.Clone()}, - { value: "open", Translations.t.shop_opened_as_usual.Clone()}, - { value: "hours", Translations.t.shop_opened_with_other_hours.Clone()} - ] ) - - const startHour = new DateInput(...)drop - const endHour = new DateInput( ... ) - // We construct a toggle which'll only show the extra questions if needed - const extraQuestion = new Toggle( - new Combine([Translations.t.openFrom, startHour, Translations.t.openTill, endHour]), - undefined, - isOpened.GetValue().map(isopened => isopened === "hours") - ) - - return new Combine([isOpened, extraQuestion]) - -``` - -### Constructing a special class - -If you make a specialized class to offer a certain functionality, you can organize it as following: - -1. Create a new class: - -```typescript - -export default class MyComponent { - - constructor(neededParameters, neededUIEventSources) { - - } - -} - -``` - -2. Construct the needed UI in the constructor - - -```typescript - -export default class MyComponent { - - constructor(neededParameters, neededUIEventSources) { - - - const component = ... - const toggle = ... - ... other components ... - - toggle.GetValue.AddCallbackAndRun(isSelected => { .. some actions ... } - - new Combine([everything, ...] ) - } - -} - -``` - -3. You'll notice that you'll end up with one certain component (in this example the combine) to wrap it all together. Change the class to extend this type of component and use `super()` to wrap it all up: - - -```typescript - -export default class MyComponent extends Combine { - - constructor(...) { - - ... - super([everything, ...]) - - } - -} - -``` - -Assets ------- - -### Themes - -Theme and layer configuration files go into `assets/layers` and `assets/themes`. - -### Images - -Other files (mostly images that are part of the core of MapComplete) go into `assets/svg` and are usable with `Svg.image_file_ui()`. Run `npm run generate:images` if you added a new image. - - -Logic ------ - -The last part is the business logic of the application, found in the directory [Logic](../Logic). Actors are small objects which react to `UIEventSources` to update other eventSources. - -`State.state` is a big singleton object containing a lot of the state of the entire application. That one is a bit of a mess. - diff --git a/src/Logic/Osm/ChangesetHandler.ts b/src/Logic/Osm/ChangesetHandler.ts index 370146817..7add28b64 100644 --- a/src/Logic/Osm/ChangesetHandler.ts +++ b/src/Logic/Osm/ChangesetHandler.ts @@ -392,7 +392,7 @@ export class ChangesetHandler { return [ ["created_by", `MapComplete ${Constants.vNumber}`], ["locale", Locale.language.data], - ["host", `${window.location.origin}${window.location.pathname}`], + ["host", `${window.location.origin}${window.location.pathname}`], // Note: deferred changes might give a different hostpath then the theme with which the changes were made ["source", setSourceAsSurvey ? "survey" : undefined], ["imagery", this.changes.state["backgroundLayer"]?.data?.id], ].map(([key, value]) => ({