diff --git a/package-lock.json b/package-lock.json index 4ce372f..1d2ff2a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,11 +38,13 @@ "react-image-gallery": "^1.3.0", "react-scripts": "5.0.1", "react-select": "^5.7.0", + "store": "^2.0.12", "typescript": "^4.8.3", "web-vitals": "^2.1.4" }, "devDependencies": { "@beyondtracks/spritezero-cli": "^2.3.1", + "@types/store": "^2.0.2", "@typescript-eslint/eslint-plugin": "^5.40.0", "@typescript-eslint/parser": "^5.40.0", "eslint": "^8.25.0", @@ -4372,6 +4374,12 @@ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" }, + "node_modules/@types/store": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/store/-/store-2.0.2.tgz", + "integrity": "sha512-ZPHnXkzmGMfk+pHqAGzTSpA9CbsHmJLgkvOl5w52LZ0XTxB1ZIHWZzQ7lEtjTNWScBbsQekg8TjApMXkMe4nkw==", + "dev": true + }, "node_modules/@types/testing-library__jest-dom": { "version": "5.14.5", "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.5.tgz", @@ -20892,6 +20900,12 @@ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" }, + "@types/store": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/store/-/store-2.0.2.tgz", + "integrity": "sha512-ZPHnXkzmGMfk+pHqAGzTSpA9CbsHmJLgkvOl5w52LZ0XTxB1ZIHWZzQ7lEtjTNWScBbsQekg8TjApMXkMe4nkw==", + "dev": true + }, "@types/testing-library__jest-dom": { "version": "5.14.5", "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.5.tgz", diff --git a/package.json b/package.json index fa5c9f8..9428860 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "react-image-gallery": "^1.3.0", "react-scripts": "5.0.1", "react-select": "^5.7.0", + "store": "^2.0.12", "typescript": "^4.8.3", "web-vitals": "^2.1.4" }, @@ -72,6 +73,7 @@ }, "devDependencies": { "@beyondtracks/spritezero-cli": "^2.3.1", + "@types/store": "^2.0.2", "@typescript-eslint/eslint-plugin": "^5.40.0", "@typescript-eslint/parser": "^5.40.0", "eslint": "^8.25.0", diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 99e1f18..845131e 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -92,7 +92,9 @@ "about_osm": "OpenStreetMap (OSM) is like a Wikipedia for maps. It's available to use for everyone and anybody can add objects to the map. You can use your OSM account to add AED locations directly from this site using a simple form.", "create_account": "You can contribute too! Just create a free account on ", "become_openaedmap_partner": "Do you want to become OpenAEDMap partner?", - "contact_us": "Contact us" + "contact_us": "Contact us", + "thanks_for_photo": "Thank you for uploading the photo. It should be visible soon.", + "thanks_for_report": "Thank you for sending a report. It will be reviewed by the site administrators." }, "partners": { "honorary_patronage": "Honorary patronage", @@ -104,5 +106,14 @@ }, "common": { "loading": "Loading..." + }, + "photo": { + "upload": "Upload photo", + "remove": "Remove photo", + "report": "Report photo", + "send_report": "Send report", + "report_long_text": "You can report the photo if it's bad quality, does not show the AEDs location or is inapropriate in any way. Administrators of the site will review the report.", + "license": "All AED photos hosted by us are licensed under Creative Commons Zero (CC0 v1.0). By uploading your photo you agree to this license terms.", + "license_short_description": "In short this means that the photo can be used by anyone for any purpose. No attribution is necessary." } } diff --git a/public/locales/pl/translation.json b/public/locales/pl/translation.json index 33568fe..b87aebb 100644 --- a/public/locales/pl/translation.json +++ b/public/locales/pl/translation.json @@ -92,7 +92,9 @@ "about_osm": "OpenStreetMap (OSM) jest jak Wikipedia dla map. Jest dostępny dla każdego i każdy może dodawać obiekty do mapy. Możesz użyć swojego konta OSM, aby dodać lokalizacje AED bezpośrednio z tej strony za pomocą prostego formularza.", "create_account": "Ty również możesz ją współtworzyć! Stwórz darmowe konto ", "become_openaedmap_partner": "Chcesz objąć patronatem projekt Mapy AED?", - "contact_us": "Skontaktuj się z nami" + "contact_us": "Skontaktuj się z nami", + "thanks_for_photo": "Dziękujemy za wysłanie zdjęcia. Niedługo powinno być widoczne na stronie.", + "thanks_for_report": "Dziękujemy za wysłanie zgłoszenia. Zostanieono przeanalizowane przez administratorów strony." }, "partners": { "honorary_patronage": "Patronat honorowy", @@ -104,5 +106,14 @@ }, "common": { "loading": "Ładowanie..." + }, + "photo": { + "upload": "Wyślij zdjęcie", + "remove": "Usuń zdjęcie", + "report": "Zgłoś zdjęcie", + "send_report": "Wyślij zgłoszenie", + "report_long_text": "Możesz zgłosić zdjęcie jeżeli ma słabą jakość, nie pokazuje położenia AEDa lub jest w jakikolwiek inny sposób niestosowne. Zgłoszenie zostanie przeanalizowane przez administratorów strony.", + "license": "Wszystkie udostępniane przez nas zdjęcia AEDów są na licencji Creative Commons Zero (CC0 v1.0). Wysyłając zdjęcie zgadzasz się na warunki tej licencji.", + "license_short_description": "W skrócie oznacza to, że zdjęcia mogą być potem wykorzystane przez kogokolwiek w dowolnym celu. Nie jest konieczne wskazanie autora." } } diff --git a/src/components/modal.tsx b/src/components/modal.tsx index 8fedf29..ce2116a 100644 --- a/src/components/modal.tsx +++ b/src/components/modal.tsx @@ -80,6 +80,22 @@ const ModalContent: FC<{}> = () => { return

{errorText}

; } case ModalType.Partners: return ; + case ModalType.ThanksForPhoto: + return ( +
+

+ {t("modal.thanks_for_photo")} +

+
+ ); + case ModalType.ThanksForReport: + return ( +
+

+ {t("modal.thanks_for_report")} +

+
+ ); default: return null; } diff --git a/src/components/sidebar.css b/src/components/sidebar.css index 5fcbed9..01c29f6 100644 --- a/src/components/sidebar.css +++ b/src/components/sidebar.css @@ -133,10 +133,6 @@ font-size: calc(100% - 1px); } -.image-gallery-slide { - max-height: 300px; -} - .image-gallery-custom-icon { color:#fff; /* transition:all .3s ease-out; */ diff --git a/src/components/sidebar/defibrillatorDetails.tsx b/src/components/sidebar/defibrillatorDetails.tsx index 70d0059..7f506ce 100644 --- a/src/components/sidebar/defibrillatorDetails.tsx +++ b/src/components/sidebar/defibrillatorDetails.tsx @@ -1,4 +1,4 @@ -import React, { FC, useRef } from "react"; +import React, { FC } from "react"; import { useTranslation } from "react-i18next"; import { Button, @@ -7,6 +7,7 @@ import { import { mdiAccountSupervisorOutline, mdiClockOutline, mdiHomeRoof, mdiInformationOutline, mdiMapMarkerOutline, mdiPhoneOutline, + mdiPlus, } from "@mdi/js"; import Icon from "@mdi/react"; import ImageGallery, { ReactImageGalleryItem } from "react-image-gallery"; @@ -38,11 +39,13 @@ function photoGallery(data: DefibrillatorData, closeSidebar: () => void) { images = [ { original: backendBaseUrl + data.photoRelativeUrl, + thumbnail: backendBaseUrl + data.photoRelativeUrl, }, ]; } if (images.length > 0) { - const refImg = useRef(null); + // ref would be used if there were more photos to see which one is selected + // const refImg = useRef(null); const renderCustomControls = () => ( ); @@ -61,7 +63,7 @@ function photoGallery(data: DefibrillatorData, closeSidebar: () => void) { return (
void) { return (

@@ -121,7 +126,7 @@ const DefibrillatorDetails: FC = (props) => { - + {photoGallery(data, closeSidebar)} diff --git a/src/components/sidebar/photoReporter.tsx b/src/components/sidebar/photoReporter.tsx index 8646dd9..34f3b0a 100644 --- a/src/components/sidebar/photoReporter.tsx +++ b/src/components/sidebar/photoReporter.tsx @@ -3,6 +3,9 @@ import { Button, Card, Image } from "react-bulma-components"; import { useTranslation } from "react-i18next"; import { DefibrillatorData } from "src/model/defibrillatorData"; import SidebarAction from "src/model/sidebarAction"; +import { initialModalState, ModalType } from "src/model/modal"; +import { mdiArrowLeftBold, mdiSend } from "@mdi/js"; +import Icon from "@mdi/react"; import { CloseSidebarButton } from "./buttons"; import { accessColourClass } from "./access"; import { useAppContext } from "../../appContext"; @@ -18,7 +21,7 @@ const PhotoReport: FC = (props) => { const { data, closeSidebar, } = props; - const { setSidebarAction } = useAppContext(); + const { setSidebarAction, setModalState } = useAppContext(); if (data === null) return null; const accessText = data.tags.access ? ` - ${t(`access.${data.tags.access}`)}` : ""; const sendReport = (photoId: string | undefined) => { @@ -34,9 +37,29 @@ const PhotoReport: FC = (props) => { "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8", }, }) - .then(() => console.log("uploaded")) // todo: add error handling - .catch((error) => console.log(error)); - closeSidebar(); + .then((response) => { + if (response.ok) { + closeSidebar(); + setModalState({ + ...initialModalState, + visible: true, + type: ModalType.ThanksForReport, + }); + } else { + const errorMessage = `${response}
status: ${response.status} ` + + `${response.statusText}
${response.body}`; + throw Error(errorMessage); + } + }) + .catch((error) => { + closeSidebar(); + setModalState({ + ...initialModalState, + visible: true, + type: ModalType.Error, + errorMessage: error, + }); + }); }; return ( @@ -58,8 +81,20 @@ const PhotoReport: FC = (props) => { - - +

+ {t("photo.report_long_text")} +

+ +
diff --git a/src/components/sidebar/photoUploader.tsx b/src/components/sidebar/photoUploader.tsx index f57b674..f5566be 100644 --- a/src/components/sidebar/photoUploader.tsx +++ b/src/components/sidebar/photoUploader.tsx @@ -1,8 +1,14 @@ import React, { FC, useState } from "react"; -import { Button, Card, Image } from "react-bulma-components"; +import { + Button, Card, Image, +} from "react-bulma-components"; import { useTranslation } from "react-i18next"; import { DefibrillatorData } from "src/model/defibrillatorData"; import SidebarAction from "src/model/sidebarAction"; +import store from "store"; +import { initialModalState, ModalType } from "src/model/modal"; +import { mdiArrowLeftBold, mdiFileDocumentRemove, mdiFileSend } from "@mdi/js"; +import Icon from "@mdi/react"; import { CloseSidebarButton } from "./buttons"; import { accessColourClass } from "./access"; import { useAppContext } from "../../appContext"; @@ -18,7 +24,7 @@ const PhotoUpload: FC = (props) => { const { data, closeSidebar, } = props; - const { authState: { auth }, setSidebarAction } = useAppContext(); + const { authState: { auth }, setSidebarAction, setModalState } = useAppContext(); const [selectedImage, setSelectedImage] = useState(null); if (data === null) return null; const accessText = data.tags.access ? ` - ${t(`access.${data.tags.access}`)}` : ""; @@ -42,8 +48,16 @@ const PhotoUpload: FC = (props) => { - - {selectedImage !== null && ( + +

{t("photo.license")}

+

{t("photo.license_short_description")}

+ {(selectedImage !== null && (
not found = (props) => { src={URL.createObjectURL(selectedImage)} />
- - +
+ + +
+ )) || ( + { + const { files } = event.target; + console.log(files); + setSelectedImage(files !== null && files.length > 0 ? files[0] : null); + }} + /> )} - { - const { files } = event.target; - console.log(files); - setSelectedImage(files !== null && files.length > 0 ? files[0] : null); - }} - />
diff --git a/src/model/modal.ts b/src/model/modal.ts index 41c90b3..df517c9 100644 --- a/src/model/modal.ts +++ b/src/model/modal.ts @@ -6,6 +6,8 @@ export enum ModalType { About, Error, Partners, + ThanksForPhoto, + ThanksForReport, } export interface ModalState {