{"version":3,"sources":["models/index.ts","services/index.ts","models/UploadState.ts","services/ApiService.ts","services/AuthService.ts","services/JSONPatchOperationType.ts","models/AccessType.ts","models/DeletionState.ts","models/ImageSize.ts","models/ImageType.ts","components/Layout/Footer.tsx","components/Layout/Navigation.tsx","helpers/ErrorHelpers.ts","components/Infrastructure/InlineValidationError.tsx","components/Shared/Title.tsx","components/Collection/Add.tsx","helpers/ToastHelpers.tsx","hooks/useWindowScrollPosition.ts","hooks/useLocalStorage.ts","services/sorting/Direction.ts","services/sorting/sorters/SorterBase.tsx","services/new-sorting/SortableBase.tsx","services/new-sorting/SortableImitations.ts","components/Infrastructure/Loader.tsx","models/RotationState.ts","components/Editor/EditStatus.ts","components/Editor/FieldEditorType.ts","helpers/ColorHelper.ts","components/Image/ImageLoader.tsx","components/Shared/Preview.tsx","components/Collection/Detail.tsx","components/Editor/EditStatusWidget.tsx","components/Editor/FieldEditor.tsx","hooks/useDebounce.ts","components/Collection/Edit.tsx","components/Imitation/MassEdit.tsx","constants/Constants.ts","components/Image/Detail.tsx","components/Image/Edit.tsx","components/Image/EditInline.tsx","services/UploadedImageFile.ts","components/Image/FileUploader.tsx","components/Imitation/Add.tsx","services/new-sorting/SortableImages.ts","components/Imitation/DetailUploads.tsx","components/Imitation/Detail.tsx","components/Imitation/Edit.tsx","components/Infrastructure/NotFound.tsx","components/Collection/Overview.tsx","components/Login/Login.tsx","hooks/useQuery.ts","components/Router/AppRouter.tsx","components/Presentation/Slide.tsx","components/Presentation/Presentation.tsx","components/App.tsx","serviceWorker.ts","index.tsx"],"names":["UploadState","ApiService","endpoint","redirectUri","headers","this","replace","Headers","Accept","method","uri","fetch","response","ensureValidResponse","data","success","failed","init","body","redirect","ok","json","model","ensureAuthenticated","error","MessageFromResponse","status","JSON","stringify","Error","indexOf","window","location","href","encodeURIComponent","title","traceId","errors","AuthService","apiService","get","post","value","JSONPatchOperationType","AccessType","DeletionState","ImageSize","ImageType","Footer","className","aria-label","role","Navigation","to","src","alt","ErrorHelpers","field","fieldErrors","caseInsensitiveLookup","length","object","key","keys","Object","find","k","toLowerCase","undefined","Array","isArray","InlineValidationError","props","getFieldErrors","map","index","style","marginBottom","Title","url","urlText","description","nextUrl","prevUrl","hasTitle","hasDescription","hasUrl","viewBox","fill","xmlns","fillRule","d","padding","display","margin","Add","useState","id","setId","setTitle","setDescription","setErrors","isSaving","setIsSaving","htmlFor","disabled","placeholder","type","onChange","e","target","onClick","event","preventDefault","item","ToastHelpers","message","options","toast","onOpen","handleOnOpen","onClose","handleOnClose","messageComponent","confirmButton","denyButton","denyFunction","position","autoClose","hideProgressBar","closeOnClick","pauseOnHover","draggable","progress","closeButton","elements","document","getElementsByClassName","i","classList","add","addEventListener","handleClick","removeEventListener","setTimeout","remove","dismiss","useWindowScrollPosition","localStorageKey","setCondition","initialValue","localStorage","getItem","parse","storedValue","setStoredValue","valueToStore","Function","setItem","useLocalStorage","scrollYStorage","setScrollYStorage","useEffect","scrollTo","scrollY","Direction","React","Component","SortableBase","state","activeFilterValue","lastSelectedValue","sortFunc","bind","constructor","name","a","b","filteredSorters","sorters","filter","sorter","property","direction","filteredSorter","Asc","dataSource","children","setState","label","sort","x","getStorageKey","SortableImitations","defaultProps","Desc","Loader","array","setArray","interval","setInterval","arr","unshift","pop","clearInterval","join","RotationState","EditStatus","FieldEditorType","ColorHelper","alpha","text","backgroundColorHex","getRandomColor","color","toRgbaColor","getYiqContrastColor","backgroundColor","ImageLoader","size","image","reloadHash","imageLoaded","setImageLoaded","colorInfo","getCSSColorPair","pixelate","width","height","fontFamily","fontSize","dy","fontWeight","y","textAnchor","visibility","transition","opacity","sources","getSrc","onLoad","Preview","deleteEndpoint","score","imitationId","Ready","deletionState","setDeletionState","rotationState","setRotationState","setReloadHash","onClickRotate","imageRotateService","Rotating","rotateResponse","showSimple","hash","onConfirmDelete","Deleting","delete","DeletingSuccessful","Deleted","subtitle","comments","updated","Date","rights","Edit","calculatedScore","Math","round","DeletingPreparing","getDeletionClassName","getRotationClassName","Square","Delete","showComplex","cursor","locale","fromNow","date","Detail","collectionId","useParams","imitations","setImitations","isLoading","setIsLoading","fetchData","marginTop","imageCount","shareUrl","protocol","host","navigator","clipboard","writeText","EditStatusWidget","Idle","Saving","Saved","FieldEditor","setValue","lastValue","setLastValue","editState","setEditState","setError","debouncedValue","delay","setDebouncedValue","handler","clearTimeout","useDebounce","requestObject","Replace","propertyName","patch","saveValue","fieldKey","cssClass","showLabel","Input","Textarea","collection","setCollection","MassEdit","floor","random","charCodeAt","output","push","Constants","imageId","setImage","imageService","fetchImage","tags","uploadedBy","calendar","GuidRegex","EditInline","browserPreviewUrl","Added","Uploaded","backgroundSize","backgroundImage","UploadedImageFile","file","originalFile","URL","createObjectURL","formData","FormData","append","toString","FileUploader","onImageChange","files","setFiles","validationError","setValidationError","imitationService","createImitation","uploadImage","Source","postFormData","getAsFormData","updateAttributes","useDropzone","multiple","accept","onDrop","acceptedFiles","ImitationRaw","getRootProps","getInputProps","isDragActive","classNames","totalImages","SortableImages","DetailUploads","images","imitationImages","creator","imitation","setImitation","setImages","fetchImages","then","reason","fetchImitation","nextId","prevId","cover","Full","NotFound","Overview","collections","setCollections","Login","authService","query","URLSearchParams","useLocation","search","setName","code","setCode","successful","setSuccessful","login","onKeyPressed","redirectUrl","decodeURIComponent","onKeyDown","AppRouter","exact","path","Slide","data-transition","Presentation","Reveal","autoSlide","loop","initialize","App","Boolean","hostname","match","ReactDOM","render","StrictMode","getElementById","serviceWorker","ready","registration","unregister","catch","console"],"mappings":"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","file":"static/js/main.35cc1ac1.chunk.js","sourcesContent":["export * from './AccessType';\r\nexport * from './AccountModel';\r\nexport * from './CollectionModel';\r\nexport * from './CollectionPreviewModel';\r\nexport * from './DeletionState';\r\nexport * from './Entity';\r\nexport * from './ISecuredEntity';\r\nexport * from './ISecuredEntity';\r\nexport * from './ImageModel';\r\nexport * from './ImagePreviewModel';\r\nexport * from './ImageRefreshModel';\r\nexport * from './ImageSize';\r\nexport * from './ImageType';\r\nexport * from './ImageUrlDictionary';\r\nexport * from './ImageViewModel';\r\nexport * from './ImitationModel';\r\nexport * from './ImitationPreviewModel';\r\nexport * from './ImitationViewModel';\r\nexport * from './UploadState';\r\nexport * from './IUploadedImageFile';\r\nexport * from './ValidationError';\r\n","export * from './ApiService';\r\nexport * from './AuthService';\r\nexport * from './JSONPatchOperation';\r\nexport * from './JSONPatchOperationType';","export enum UploadState {\r\n Added,\r\n Uploaded,\r\n Error,\r\n}","import { ValidationError } from \"../models\";\r\n\r\nexport class ApiService {\r\n\r\n private readonly endpoint: string;\r\n private readonly headers: Headers;\r\n private readonly redirectUri: string | null;\r\n\r\n constructor(endpoint: string, redirectUri: string | null = null) {\r\n this.redirectUri = redirectUri;\r\n this.endpoint = `/api/${endpoint}`.replace(\"//\", \"/\");\r\n this.headers = new Headers({\r\n Accept: \"application/json\",\r\n \"Content-Type\": \"application/json\",\r\n });\r\n }\r\n\r\n public async get(method: string | null = null): Promise {\r\n const uri = this.endpoint + (method === null ? '' : '/' + method);\r\n const response = await fetch(uri);\r\n return this.ensureValidResponse(response);\r\n }\r\n\r\n public async postFormData(data: FormData, success: (value: T) => void, failed: (value: ValidationError) => void): Promise {\r\n const init: RequestInit = {\r\n method: \"POST\",\r\n body: data,\r\n redirect: \"follow\"\r\n };\r\n\r\n const response = await fetch(this.endpoint, init);\r\n if (response.ok) {\r\n var model = (await response.json()) as T;\r\n success(model);\r\n } else {\r\n this.ensureAuthenticated(response);\r\n try {\r\n var error = (await response.json()) as ValidationError;\r\n failed(error);\r\n } catch {\r\n failed(ApiService.MessageFromResponse(response));\r\n }\r\n }\r\n }\r\n\r\n public async delete(): Promise {\r\n const init: RequestInit = {\r\n method: \"DELETE\",\r\n redirect: \"follow\",\r\n headers: this.headers\r\n };\r\n\r\n const response = await fetch(this.endpoint, init);\r\n if (!response.ok) {\r\n // TODO: don't we want to ensure authenticated before deleting?\r\n this.ensureAuthenticated(response);\r\n\r\n // Cool info about it https://stackoverflow.com/questions/42453683/how-to-reject-in-async-await-syntax\r\n throw response.status;\r\n }\r\n }\r\n\r\n public async patch(data: any, success: () => void, failed: (value: ValidationError) => void): Promise {\r\n const init: RequestInit = {\r\n method: \"PATCH\",\r\n body: JSON.stringify(data),\r\n redirect: \"follow\",\r\n headers: this.headers\r\n };\r\n\r\n const response = await fetch(this.endpoint, init);\r\n if (response.ok) {\r\n success();\r\n } else {\r\n this.ensureAuthenticated(response);\r\n try {\r\n var error = (await response.json()) as ValidationError;\r\n failed(error);\r\n } catch {\r\n failed(ApiService.MessageFromResponse(response));\r\n }\r\n }\r\n }\r\n\r\n public async post(data: any, success: (value: T) => void, failed: (value: ValidationError) => void): Promise {\r\n const init: RequestInit = {\r\n method: \"POST\",\r\n body: JSON.stringify(data),\r\n redirect: \"follow\",\r\n headers: this.headers\r\n };\r\n\r\n const response = await fetch(this.endpoint, init);\r\n if (response.ok) {\r\n var model = (await response.json()) as T;\r\n success(model);\r\n } else {\r\n this.ensureAuthenticated(response);\r\n try {\r\n var error = (await response.json()) as ValidationError;\r\n failed(error);\r\n } catch {\r\n failed(ApiService.MessageFromResponse(response));\r\n }\r\n }\r\n }\r\n\r\n private async ensureValidResponse(response: Response): Promise {\r\n if (!response.ok) {\r\n this.ensureAuthenticated(response);\r\n throw Error(`Request to ${this.endpoint} failed.`);\r\n }\r\n\r\n return (await response.json()) as T;\r\n }\r\n\r\n private ensureAuthenticated(response: Response) {\r\n if ([401, 403].indexOf(response.status) !== -1) {\r\n if (this.redirectUri !== null) {\r\n window.location.href = `/login?redirectUrl=${encodeURIComponent(this.redirectUri)}`;\r\n } else {\r\n window.location.href = `/login`;\r\n }\r\n }\r\n }\r\n\r\n private static MessageFromResponse(response: Response): ValidationError {\r\n return { title: 'Connection error.', status: response.status, traceId: '', errors: null };\r\n }\r\n}\r\n","import { ApiService } from \".\";\r\nimport { AccountModel, ValidationError } from \"../models\";\r\n\r\nexport class AuthService {\r\n\r\n public readonly apiService: ApiService;\r\n\r\n constructor() {\r\n this.apiService = new ApiService(\"account\");\r\n }\r\n\r\n public async getUser(): Promise {\r\n try {\r\n return await this.apiService.get();\r\n } catch {\r\n return null;\r\n }\r\n }\r\n\r\n public async login(model: AccountModel, success: (value: AccountModel) => void, failed: (value: ValidationError) => void): Promise {\r\n await this.apiService.post(model,\r\n (value: AccountModel) => {\r\n success(value);\r\n },\r\n (value: ValidationError) => {\r\n failed(value);\r\n });\r\n }\r\n\r\n public async logout(): Promise {\r\n await this.apiService.get('logout');\r\n }\r\n}","// oh, RFC 6902 compliant types for operations. See http://jsonpatch.com/\r\nexport enum JSONPatchOperationType {\r\n Add = \"add\",\r\n Remove = \"remove\",\r\n Replace = \"replace\",\r\n Copy = \"copy\",\r\n Move = \"move\",\r\n Test = \"test\"\r\n}","export enum AccessType {\r\n\r\n /// \r\n /// Cannot view this item at all.\r\n /// \r\n None = 0,\r\n\r\n /// \r\n /// Is able to see this item.\r\n /// \r\n View = 1,\r\n\r\n /// \r\n /// Can edit this item.\r\n /// \r\n Edit = 2,\r\n\r\n /// \r\n /// Is allowed to remove this item.\r\n /// \r\n Delete = 4,\r\n}","export enum DeletionState {\r\n Ready,\r\n DeletingPreparing,\r\n Deleting,\r\n DeletingSuccessful,\r\n Deleted,\r\n}","export enum ImageSize {\r\n\r\n // Side length = 200\r\n Thumbnail = 'Thumbnail',\r\n\r\n // Side length = 350\r\n Square = 'Square',\r\n\r\n // Side length = 750\r\n Full = 'Full',\r\n}","export enum ImageType {\r\n Source = 0,\r\n ImitationRaw = 1\r\n}\r\n","import * as React from 'react';\r\n\r\nexport default function Footer() {\r\n return (\r\n
\r\n
\r\n
\r\n
\r\n from 🇨🇭 with ❤️\r\n © 2019 - 2020\r\n
\r\n
\r\n
über uns
\r\n

\r\n nachäffen mit niveau.
mit imitate stellst du im handumdrehen fotos nach.\r\n

\r\n
\r\n
\r\n
status
\r\n

\r\n ok, version 2020.10.1601
f91cb22\r\n

\r\n
\r\n
\r\n

\r\n imitate
\r\n nachäffen mit niveau
version 2020.10.1601, f91cb22\r\n

\r\n
\r\n
\r\n
\r\n
\r\n );\r\n}\r\n\r\n\r\n","import * as React from \"react\";\r\nimport { Link } from \"react-router-dom\";\r\n\r\nexport default function Navigation() {\r\n return (\r\n
\r\n
\r\n \r\n \"imitate\r\n

nachäffen mit niveau

\r\n \r\n
\r\n
\r\n );\r\n}\r\n","import { ValidationError } from \"../models/ValidationError\";\r\n\r\nexport abstract class ErrorHelpers {\r\n\r\n public static getFieldErrors(error: ValidationError, field: string): string[] {\r\n if (error.errors) {\r\n const fieldErrors = ErrorHelpers.caseInsensitiveLookup(error.errors, field);\r\n if (!fieldErrors || fieldErrors.length <= 0) {\r\n return []\r\n }\r\n\r\n return fieldErrors;\r\n }\r\n \r\n return ErrorHelpers.caseInsensitiveLookup(error, field);\r\n }\r\n\r\n private static caseInsensitiveLookup(object: any, key: string) {\r\n // Because .NET returns the field names with PascalCase, so we need a case insensitive comparison here.\r\n const keys = Object.keys(object).find(k => k.toLowerCase() === key.toLowerCase());\r\n if (keys === undefined || keys.length <= 0) {\r\n return null;\r\n }\r\n\r\n // In case there is only one match (should always be the case), return the key, otherwise take first element\r\n return Array.isArray(keys) ? object[keys[0]] : object[keys];\r\n }\r\n}\r\n\r\n\r\n","import * as React from 'react';\r\nimport { ErrorHelpers } from '../../helpers/ErrorHelpers';\r\nimport { ValidationError } from '../../models';\r\n\r\ninterface IInlineErrorProps {\r\n field: string;\r\n error: ValidationError | null;\r\n}\r\n\r\nexport default function InlineValidationError(props: IInlineErrorProps) {\r\n\r\n const { error, field } = props;\r\n if (!error) {\r\n return <>\r\n }\r\n\r\n const fieldErrors = ErrorHelpers.getFieldErrors(error, field);\r\n\r\n if (fieldErrors.length <= 0) {\r\n return <>;\r\n }\r\n\r\n return (\r\n
\r\n {fieldErrors.map((error: string, index: number) =>

{error}

)}\r\n
\r\n );\r\n}\r\n","import * as React from 'react';\r\nimport { Link } from 'react-router-dom';\r\n\r\ninterface ITitleProps {\r\n url?: string;\r\n urlText?: string;\r\n title?: string;\r\n description?: string;\r\n nextUrl?: string;\r\n prevUrl?: string;\r\n}\r\n\r\nexport default function Title(props: ITitleProps) {\r\n\r\n const { url, urlText, title, description, nextUrl, prevUrl } = props;\r\n\r\n const getNextLink = () => {\r\n if (!nextUrl || nextUrl.length <= 0) {\r\n return <>;\r\n }\r\n\r\n return (\r\n \r\n \r\n \r\n \r\n );\r\n }\r\n\r\n const getPrevLink = () => {\r\n if (!prevUrl || prevUrl.length <= 0) {\r\n return <>;\r\n }\r\n\r\n return (\r\n \r\n \r\n \r\n \r\n );\r\n }\r\n\r\n const hasTitle = title && title.length > 0;\r\n const hasDescription = description && description.length > 0;\r\n const hasUrl = url && urlText && url.length > 0;\r\n\r\n return (\r\n
\r\n
\r\n {getPrevLink()}\r\n

{hasTitle ? title : 'titel fehlt'}

\r\n {hasUrl &&\r\n {urlText}\r\n }\r\n {hasDescription &&\r\n

{description}

\r\n }\r\n {getNextLink()}\r\n
\r\n
\r\n );\r\n}\r\n","import * as React from 'react';\r\nimport { ValidationError, CollectionModel } from '../../models';\r\nimport InlineValidationError from '../Infrastructure/InlineValidationError';\r\nimport { Redirect } from 'react-router-dom';\r\nimport { useState } from 'react';\r\nimport Title from '../Shared/Title';\r\nimport { ApiService } from '../../services';\r\n\r\nexport default function Add() {\r\n const apiService = new ApiService(`collection`);\r\n\r\n const [id, setId] = useState('');\r\n const [title, setTitle] = useState('');\r\n const [description, setDescription] = useState('');\r\n const [errors, setErrors] = useState(null);\r\n const [isSaving, setIsSaving] = useState(false);\r\n\r\n const onCreateClick = (event: React.MouseEvent) => {\r\n // Do not submit this form\r\n event.preventDefault();\r\n setIsSaving(true);\r\n\r\n // Try saving item\r\n apiService.post(\r\n {\r\n title: title,\r\n description: description,\r\n },\r\n (item) => {\r\n setIsSaving(false);\r\n setTitle('');\r\n setDescription('');\r\n setId(item.id);\r\n },\r\n (error) => {\r\n setIsSaving(false);\r\n setErrors(errors);\r\n }\r\n );\r\n };\r\n\r\n // Only way to redirect correctly according to https://dev.to/projectescape/programmatic-navigation-in-react-3p1l\r\n if (id.length > 0) {\r\n return ;\r\n }\r\n\r\n return (\r\n <>\r\n \r\n <div className=\"container\">\r\n <div className=\"row justify-content-center\">\r\n <div className=\"col-md-6 mb-4\">\r\n <label htmlFor=\"title\">titel deines albums:</label>\r\n <input\r\n id=\"title\"\r\n className=\"form-control\"\r\n disabled={isSaving}\r\n placeholder={`z. B. Lauras Kinderfotos`}\r\n type=\"text\"\r\n onChange={(e) => setTitle(e.target.value)}\r\n />\r\n <InlineValidationError field=\"title\" error={errors} />\r\n </div>\r\n </div>\r\n <div className=\"row justify-content-center\">\r\n <div className=\"col-md-6 mb-4\">\r\n <label htmlFor=\"description\">beschreibe um was es geht:</label>\r\n <textarea\r\n id=\"description\"\r\n className=\"form-control\"\r\n disabled={isSaving}\r\n placeholder={`z. B. Laura wird 25, ich habe lustige Fotos aus ihrer Kindheit gesammelt. Ich freue mich auf eure Imitationen.`}\r\n onChange={(e) => setDescription(e.target.value)}\r\n />\r\n <InlineValidationError field=\"description\" error={errors} />\r\n </div>\r\n </div>\r\n <div className=\"row justify-content-center\">\r\n <div className=\"col-md-6 mb-4\">\r\n <button disabled={isSaving} className=\"btn btn-primary\" onClick={onCreateClick}>\r\n kollektion erstellen\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n </>\r\n );\r\n}\r\n","import * as React from \"react\";\r\nimport { toast, ToastOptions } from \"react-toastify\";\r\n\r\nexport abstract class ToastHelpers {\r\n \r\n /**\r\n * Show a simple toast with default options.\r\n * @param message The message to show.\r\n * @param options Options to override.\r\n */\r\n public static showSimple(message: string, options?: ToastOptions) {\r\n toast(message, {\r\n ...options,\r\n onOpen: () => ToastHelpers.handleOnOpen(),\r\n onClose: () => ToastHelpers.handleOnClose(),\r\n });\r\n }\r\n\r\n /**\r\n * Shows a toast with custom confirm and deny buttons. Also will fire \"deny function\" and close if anywhere on the masking div is clicked.\r\n * @param messageComponent Markup for the message part.\r\n * @param confirmButton Markup for the confirm button.\r\n * @param denyButton Markup for deny button.\r\n * @param denyFunction Function to execute on deny click. Note that the \"deny function\" fires on any type of dismiss (swipe, clicking away etc. so it fires in the onClose())\r\n */\r\n public static showComplex(\r\n messageComponent: React.ReactNode,\r\n confirmButton: React.ReactNode,\r\n denyButton: React.ReactNode,\r\n denyFunction: () => void\r\n ): void {\r\n toast(\r\n <>\r\n {messageComponent}\r\n {confirmButton}\r\n {denyButton}\r\n </>,\r\n {\r\n position: \"top-center\",\r\n autoClose: false,\r\n hideProgressBar: true,\r\n closeOnClick: true,\r\n pauseOnHover: false,\r\n draggable: false,\r\n progress: undefined,\r\n closeButton: false,\r\n onOpen: () => ToastHelpers.handleOnOpen(),\r\n onClose: () => ToastHelpers.handleOnClose(denyFunction),\r\n }\r\n );\r\n }\r\n\r\n private static handleOnOpen() {\r\n const elements = document.getElementsByClassName(\"Toastify\");\r\n\r\n // add active class and event listener\r\n for (let i = 0; i < elements.length; i++) {\r\n\r\n const item = elements[i];\r\n\r\n item.classList.add(\"active\");\r\n item.addEventListener(\"click\", ToastHelpers.handleClick, false);\r\n }\r\n }\r\n\r\n private static handleOnClose(denyFunction?: () => void) {\r\n const elements = document.getElementsByClassName(\"Toastify\");\r\n\r\n // fire deny function, remove classes, cleanup event listeners\r\n\r\n for (let i = 0; i < elements.length; i++) {\r\n\r\n const item = elements[i];\r\n\r\n if (denyFunction) {\r\n denyFunction();\r\n }\r\n\r\n item.classList.add(\"closing\");\r\n item.removeEventListener(\"click\", ToastHelpers.handleClick);\r\n\r\n // Close all toasts after each other\r\n setTimeout(() => {\r\n item.classList.remove(\"closing\", \"active\");\r\n }, i * 50 + 250);\r\n }\r\n }\r\n\r\n private static handleClick() {\r\n toast.dismiss();\r\n }\r\n}\r\n","import { useEffect } from \"react\";\r\nimport useLocalStorage from \"./useLocalStorage\";\r\n\r\n// sets scrollY position of window based on a setting condition, i.e. when api calls are done\r\n// also sets the scroll position when unmounting, i.e. a user navigates to a different page\r\nexport default function useWindowScrollPosition(localStorageKey: string, setCondition: boolean): void {\r\n const [scrollYStorage, setScrollYStorage] = useLocalStorage(localStorageKey, 0);\r\n useEffect(() => {\r\n // if the setcondition is true (AKA everything in the DOM is loaded: fire off the scrollTo()!)\r\n if (setCondition) {\r\n window.scrollTo(0, scrollYStorage)\r\n }\r\n }, [setCondition, scrollYStorage])\r\n\r\n // purely on un mount (and thus we ignore the ESLint warning): store the scroll position the user was at to localStorage\r\n // see the yellow note at https://reactjs.org/docs/hooks-effect.html near the very bottom\r\n useEffect(()=> {\r\n return () => {\r\n setScrollYStorage(window.scrollY)\r\n };\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [])\r\n}\r\n","import { useState } from \"react\";\r\n\r\n// from https://usehooks.com/useLocalStorage/\r\nexport default function useLocalStorage(key: string, initialValue: string | number) {\r\n // State to store our value\r\n // Pass initial state function to useState so logic is only executed once\r\n const [storedValue, setStoredValue] = useState(() => {\r\n try {\r\n // Get from local storage by key\r\n const item = window.localStorage.getItem(key);\r\n // Parse stored json or if none return initialValue\r\n return item ? JSON.parse(item) : initialValue;\r\n } catch (error) {\r\n // If error also return initialValue\r\n return initialValue;\r\n }\r\n });\r\n\r\n // Return a wrapped version of useState's setter function that\r\n // persists the new value to localStorage.\r\n const setValue = (value: Function | string | number) => {\r\n try {\r\n // Allow value to be a function so we have same API as useState\r\n const valueToStore =\r\n value instanceof Function ? value(storedValue) : value;\r\n // Save state\r\n setStoredValue(valueToStore);\r\n // Save to local storage\r\n window.localStorage.setItem(key, JSON.stringify(valueToStore));\r\n } catch (error) {\r\n // A more advanced implementation would handle the error case\r\n }\r\n };\r\n return [storedValue, setValue];\r\n}\r\n","export enum Direction {\r\n None = 0,\r\n Asc = 1,\r\n Desc = 2,\r\n}","import React, { ReactNode } from \"react\";\r\nimport { Direction } from \"..\";\r\n\r\ninterface ISorterProps<T> {\r\n label: string;\r\n property: keyof T | \"\";\r\n direction?: Direction;\r\n}\r\n\r\nexport abstract class SorterBase<T> extends React.Component<ISorterProps<T>> {\r\n\r\n public get property(): keyof T | \"\" {\r\n return this.props.property;\r\n }\r\n\r\n public get direction(): Direction {\r\n return this.props.direction ?? Direction.Asc;\r\n }\r\n\r\n public get label(): string {\r\n return this.props.label;\r\n }\r\n\r\n public get key(): string {\r\n return `${this.property}_${this.direction}`;\r\n }\r\n\r\n public apply(a: T, b: T): number {\r\n const result = this.applyInternally(a, b);\r\n switch (this.direction) {\r\n case Direction.Desc:\r\n return result * -1;\r\n default:\r\n return result;\r\n }\r\n }\r\n\r\n protected abstract applyInternally(a: T, b: T): number;\r\n\r\n public render(): ReactNode {\r\n return (\r\n <option value={`${this.key}`}>{this.label}</option>\r\n );\r\n }\r\n}\r\n","import * as React from 'react';\r\nimport { ReactNode } from 'react';\r\nimport ISorter from '../../models/ISorter';\r\nimport { Direction } from '../sorting';\r\n\r\ninterface ISortableBaseState {\r\n activeFilterValue: string;\r\n}\r\n\r\nexport interface ISortableBaseProps<T> extends SortableBaseDefaultProps<T> {\r\n dataSource: T[];\r\n}\r\n\r\nexport type SortableBaseDefaultProps<T> = {\r\n sorters: Array<ISorter<T>>;\r\n}\r\n\r\ntype PropsWithChildrenFunction<P, T> = P & {\r\n children?(item: T): ReactNode;\r\n}\r\n\r\nexport abstract class SortableBase<T> extends React.Component<PropsWithChildrenFunction<ISortableBaseProps<T>, T>, ISortableBaseState> {\r\n\r\n constructor(props: ISortableBaseProps<T>) {\r\n super(props);\r\n this.state = {\r\n activeFilterValue: this.lastSelectedValue\r\n }\r\n this.sortFunc = this.sortFunc.bind(this);\r\n }\r\n\r\n private getStorageKey(): string {\r\n return `imitate_${this.constructor.name}`;\r\n }\r\n\r\n get lastSelectedValue(): string {\r\n return window.localStorage.getItem(this.getStorageKey()) ?? \"\";\r\n }\r\n\r\n set lastSelectedValue(value: string) {\r\n window.localStorage.setItem(this.getStorageKey(), value);\r\n }\r\n\r\n private sortFunc(a: T, b: T): number {\r\n const filteredSorters = this.props.sorters.filter(sorter => `${sorter.property}_${sorter.direction}` === this.state.activeFilterValue);\r\n if (filteredSorters.length === 0) {\r\n return 0;\r\n }\r\n const filteredSorter = filteredSorters[0];\r\n const property = filteredSorter.property;\r\n\r\n // typescript was smart enough to understand this line... incredible\r\n if (a[property] > b[property]) {\r\n return filteredSorter.direction === Direction.Asc ? 1 : -1;\r\n } else if (a[property] < b[property]) {\r\n return filteredSorter.direction === Direction.Asc ? -1 : 1;\r\n }\r\n return 0;\r\n }\r\n\r\n public render() {\r\n const { dataSource, children } = this.props;\r\n return (\r\n <>\r\n <div className=\"row justify-content-center\">\r\n <div className=\"col-md-6 col-sm-12 p-2\">\r\n <select className=\"form-control form-control-sm\" value={this.state.activeFilterValue} onChange={(e) => {\r\n const value = e.target.value;\r\n this.setState({\r\n activeFilterValue: value\r\n });\r\n this.lastSelectedValue = value;\r\n }\r\n }>\r\n <option disabled={true} value=\"\">bilder sortieren...</option>\r\n {this.props.sorters.map(sorter => {\r\n const key = `${sorter.property}_${sorter.direction}`;\r\n return (\r\n <option key={key} value={key}>{sorter.label}</option>\r\n )\r\n })}\r\n </select>\r\n </div>\r\n </div>\r\n {children && <div className=\"row\">\r\n {dataSource\r\n .sort(this.sortFunc)\r\n .map((x, i) => children(x))\r\n }\r\n </div>}\r\n </>\r\n );\r\n }\r\n}\r\n\r\n\r\n","import { ImitationPreviewModel } from \"../../models\";\r\nimport { Direction } from \"../sorting\";\r\nimport { SortableBase, SortableBaseDefaultProps } from \"./SortableBase\";\r\n\r\nexport class SortableImitations extends SortableBase<ImitationPreviewModel> {\r\n static defaultProps: SortableBaseDefaultProps<ImitationPreviewModel> = {\r\n sorters: [\r\n { label: \"🧓 älteste\", property: \"updated\", direction: Direction.Asc },\r\n { label: \"👶 neuste\", property: \"updated\", direction: Direction.Desc },\r\n { label: \"🏆 oft nachgestellt\", property: \"imageCount\", direction: Direction.Desc },\r\n { label: \"🐌 wenig nachgestellt\", property: \"imageCount\", direction: Direction.Asc }\r\n ],\r\n }\r\n}\r\n","import * as React from 'react';\r\nimport { useEffect, useState } from 'react';\r\n\r\n// TODO: This loader is a bit abstract, see https://codepen.io/jkeussen/pen/Fliwg for a good example\r\nexport default function Loader() {\r\n const [array, setArray] = useState<Array<string>>([\r\n '🙈',\r\n '🐵',\r\n '🐵',\r\n ]);\r\n\r\n const arrayRotate = (arr: any[]): any[] => {\r\n arr.unshift(arr.pop());\r\n return arr;\r\n }\r\n\r\n // important to use the clearInterval in useEffect so we don't have memory leaks - react will clear the interval for us when this unmounts\r\n // spreading the array is also required, otherwise react doesn't think that the monkey array has 'changed'\r\n useEffect(() => {\r\n const interval = setInterval(() => {\r\n setArray([...arrayRotate(array)]);\r\n }, 350);\r\n \r\n return () => clearInterval(interval);\r\n }, [array]);\r\n\r\n return (\r\n <div className=\"p-4 text-center\">\r\n <h6>deine daten werden geladen ...</h6>\r\n <p className=\"text-center p-2 h1\">{array.join(' ')}</p>\r\n </div>\r\n );\r\n}\r\n","export enum RotationState {\r\n Ready,\r\n Rotating,\r\n RotatingSuccessful,\r\n}\r\n","export enum EditStatus {\r\n Idle,\r\n Saving,\r\n Error,\r\n Saved\r\n}","export enum FieldEditorType {\r\n Input,\r\n Textarea\r\n}","import { CSSProperties } from \"react\";\r\n\r\nexport abstract class ColorHelper {\r\n\r\n // Returns black or white - whatever gives the better contrast\r\n private static getYiqContrastColor = (color: number[]): number[] => {\r\n // Get YIQ ratio, stolen from https://24ways.org/2010/calculating-color-contrast/\r\n var yiq = (color[0] * 299 + color[1] * 587 + color[2] * 114) / 1000;\r\n return yiq >= 128 ? [0, 0, 0] : [255, 255, 255];\r\n };\r\n\r\n // Returns a random color\r\n private static getRandomColor = (text?: string): number[] => {\r\n // 16777216 = 2^25 to generate a random color\r\n let hash = Math.floor(Math.random() * 16777216);\r\n\r\n // Function to calculate a hash on the text ... so the color matches the text description\r\n if (text) {\r\n hash = 0;\r\n for (let i = 0; i < text.length; i++) {\r\n hash = (hash << 5) - hash + text.charCodeAt(i);\r\n hash |= 0;\r\n }\r\n }\r\n\r\n // Generate the hex code from the newly created color, then add the alpha channel\r\n return [0, 1, 2].map((x) => (hash >> (x * 8)) & 255);\r\n };\r\n\r\n // Converts number array to rgba value\r\n private static toRgbaColor = (color: number[], alpha: number = 1): string => {\r\n const output = new Array<number>(...color);\r\n output.push(alpha);\r\n\r\n // Create the rgba string\r\n return `rgba(${output.join(\",\")})`;\r\n };\r\n\r\n public static getCSSColorPair(alpha: number, text?: string): CSSProperties {\r\n const backgroundColorHex = ColorHelper.getRandomColor(text);\r\n return {\r\n color: ColorHelper.toRgbaColor(ColorHelper.getYiqContrastColor(backgroundColorHex)),\r\n backgroundColor: ColorHelper.toRgbaColor(backgroundColorHex, alpha),\r\n };\r\n }\r\n}\r\n","import * as React from 'react';\r\nimport { ColorHelper } from '../../helpers/ColorHelper';\r\nimport { ImageModel, ImageSize } from '../../models';\r\n\r\nexport interface IImageLoaderProps {\r\n text?: string;\r\n size: ImageSize;\r\n image?: ImageModel;\r\n pixelate?: boolean;\r\n reloadHash?: string;\r\n}\r\n\r\nexport function ImageLoader(props: IImageLoaderProps) {\r\n const { size, image, text, reloadHash } = props;\r\n const [imageLoaded, setImageLoaded] = React.useState<boolean>(false);\r\n const colorInfo = ColorHelper.getCSSColorPair(0.5, text);\r\n const pixelate = props.pixelate ?? false;\r\n\r\n // Ha, nasty animation on rotate\r\n React.useEffect(() => {\r\n setImageLoaded(false);\r\n }, [props.reloadHash]);\r\n\r\n const getSrc = () => {\r\n let src = image?.sources[size] + (pixelate ? '&pixelate=12' : '');\r\n if (reloadHash) {\r\n src = src.replace(/@[a-f0-9]+\\./g, `@${reloadHash}.`);\r\n }\r\n return src;\r\n }\r\n\r\n return (\r\n <>\r\n {(!image || !imageLoaded) && (\r\n // Another good way would be https://stackoverflow.com/questions/48780889/colour-overlay-fade\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"100%\" height=\"100%\" viewBox=\"0 0 350 350\" className=\"card-img-top\">\r\n <rect fill={colorInfo.backgroundColor} width=\"350\" height=\"350\" />\r\n <text fill={colorInfo.color} fontFamily=\"sans-serif\" fontSize=\"20\" dy=\"10.5\" fontWeight=\"bold\" x=\"50%\" y=\"50%\" textAnchor=\"middle\">\r\n {text ?? \"lade dein bild ...\"}\r\n </text>\r\n </svg>\r\n )}\r\n {image && (\r\n <img\r\n style={{\r\n visibility: imageLoaded ? 'visible' : 'hidden',\r\n position: imageLoaded ? 'inherit' : 'absolute',\r\n transition: 'all 500ms',\r\n opacity: imageLoaded ? 1 : 0,\r\n width: '100%',\r\n }}\r\n className=\"img-responsive card-img-top\"\r\n src={getSrc()}\r\n alt={image.title}\r\n title={image.title}\r\n onLoad={() => setImageLoaded(true)}\r\n />\r\n )}\r\n </>\r\n );\r\n}\r\n","import React, { useState } from \"react\";\r\nimport Moment from \"react-moment\";\r\nimport 'moment/locale/de-ch';\r\nimport { Link } from \"react-router-dom\";\r\nimport { ImageSize, ImageModel, AccessType, DeletionState, ImageRefreshModel } from \"../../models\";\r\nimport { ImageLoader } from \"../Image/ImageLoader\";\r\nimport { ToastHelpers } from \"../../helpers/ToastHelpers\";\r\nimport { ApiService } from \"../../services\";\r\nimport { RotationState } from \"../../models/RotationState\";\r\n\r\nexport interface IPreviewProps {\r\n className: string;\r\n updated?: Date;\r\n title?: string;\r\n subtitle?: string;\r\n image?: ImageModel;\r\n url: string;\r\n deleteEndpoint?: string;\r\n rights: AccessType;\r\n pixelate?: boolean;\r\n score?: number;\r\n imitationId?: string;\r\n}\r\n\r\nexport default function Preview(props: React.PropsWithChildren<IPreviewProps>) {\r\n const { className, image, url, deleteEndpoint, score, imitationId } = props;\r\n const [deletionState, setDeletionState] = useState<DeletionState>(DeletionState.Ready);\r\n const [rotationState, setRotationState] = React.useState<RotationState>(RotationState.Ready);\r\n const [reloadHash, setReloadHash] = React.useState<string>(\"\");\r\n\r\n const onClickRotate = async (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {\r\n // Not following the actual link anymore as we are rotating\r\n event.preventDefault();\r\n if (imitationId && image) {\r\n const imageRotateService = new ApiService(`${imitationId}/image/${image.id}/rotate`);\r\n setRotationState(RotationState.Rotating);\r\n await imageRotateService.post<ImageRefreshModel>({},\r\n (rotateResponse) => {\r\n ToastHelpers.showSimple('👍 bild gedreht');\r\n setRotationState(RotationState.Ready);\r\n setReloadHash(rotateResponse.hash);\r\n },\r\n () => {\r\n ToastHelpers.showSimple('🤔 fehler beim drehen. bitte nochmals versuchen.');\r\n setRotationState(RotationState.Ready);\r\n setReloadHash(\"\");\r\n }\r\n )\r\n }\r\n }\r\n\r\n const onConfirmDelete = async () => {\r\n // TODO: Very bad implementation as it requires and additional parameter\r\n const apiService = new ApiService(deleteEndpoint ?? url);\r\n try {\r\n setDeletionState(DeletionState.Deleting);\r\n await apiService.delete();\r\n setDeletionState(DeletionState.DeletingSuccessful);\r\n setTimeout(() => {\r\n setDeletionState(DeletionState.Deleted);\r\n }, 2000)\r\n ToastHelpers.showSimple('🚽 element gelöscht', {\r\n autoClose: 2000,\r\n });\r\n } catch {\r\n ToastHelpers.showSimple('💥 fehler beim löschen. bitte erneut versuchen.');\r\n setDeletionState(DeletionState.Ready);\r\n }\r\n };\r\n\r\n const onClickDelete = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {\r\n e.preventDefault();\r\n setDeletionState(DeletionState.DeletingPreparing);\r\n ToastHelpers.showComplex(\r\n <p className=\"text-dark text-center p-2\">\r\n <span role=\"img\" aria-label=\"see no evil\">🙈</span> \r\n element wirklich löschen?\r\n </p>,\r\n <button className=\"btn btn-light m-2\" onClick={onConfirmDelete}>\r\n <span role=\"img\" aria-label=\"smiling imp\">\r\n 😈\r\n </span>{\" \"}\r\n ja, wirklich\r\n </button>,\r\n <button className=\"btn btn-secondary m-2\">\r\n <span role=\"img\" aria-label=\"scream\">\r\n 😱\r\n </span>{\" \"}\r\n lieber nicht\r\n </button>,\r\n () => setDeletionState(DeletionState.Ready)\r\n );\r\n };\r\n\r\n const getDeletionClassName = (state: DeletionState) => {\r\n switch (state) {\r\n case DeletionState.DeletingPreparing:\r\n return ' deleting-preparing';\r\n case DeletionState.Deleting:\r\n return ' deleting';\r\n case DeletionState.DeletingSuccessful:\r\n return ' deleting-successful';\r\n default:\r\n return '';\r\n }\r\n }\r\n\r\n const getRotationClassName = (state: RotationState) => {\r\n switch (state) {\r\n case RotationState.Rotating:\r\n return ' rotating'\r\n default:\r\n return '';\r\n }\r\n }\r\n\r\n // Override strictly specified types\r\n const title = props.title ?? image?.title;\r\n const subtitle = props.subtitle ?? image?.comments;\r\n const updated = props.updated ?? image?.updated ?? new Date();\r\n const pixelate = (props.rights & AccessType.Edit) <= 0 && (props.pixelate ?? true);\r\n\r\n // TODO: Filter actions based on user rights\r\n if (deletionState === DeletionState.Deleted) {\r\n return <></>;\r\n }\r\n\r\n const style = { \"--rating\": (score ?? 0) * 5 } as React.CSSProperties;\r\n const calculatedScore = Math.round((score ?? 0) * 100);\r\n\r\n return (\r\n <div className={className}>\r\n <div className={`card shadow-sm${getDeletionClassName(deletionState)}${getRotationClassName(rotationState)}`}>\r\n <Link to={url}>\r\n <ImageLoader reloadHash={reloadHash} pixelate={pixelate} image={image} size={ImageSize.Square} text={image ? undefined : title} />\r\n {(props.rights & AccessType.Edit) > 0 &&\r\n <button className=\"btn btn-rotate\" title=\"bild 90° im uhrzeigersinn drehen\" onClick={onClickRotate}>\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" style={{ height: '18px', width: '18px' }}>\r\n <path fill=\"currentColor\" d=\"M.62 4.9A8.07 8.07 0 014.88.64a7.78 7.78 0 016.07-.06c.95.39 1.8.93 2.55 1.63L14.85.87c.2-.22.45-.27.72-.15.28.12.42.32.42.62V6c0 .18-.07.34-.2.47a.64.64 0 01-.47.2h-4.67a.61.61 0 01-.61-.42c-.12-.27-.07-.5.14-.72L11.6 4.1a5.18 5.18 0 00-3.63-1.43A5.37 5.37 0 002.65 8a5.37 5.37 0 005.34 5.33 5.25 5.25 0 004.2-2.07c.06-.07.14-.1.25-.12.1 0 .2.03.26.1l1.43 1.43a.3.3 0 01.09.2.4.4 0 01-.07.25 7.95 7.95 0 01-9.26 2.25A8.07 8.07 0 01.62 4.9\" />\r\n </svg>\r\n </button>\r\n }\r\n {props.children}\r\n </Link>\r\n <div className=\"card-body\">\r\n {(title && title.length > 0) &&\r\n <h4 style={{display:'inline'}}>{title}</h4>\r\n }\r\n\r\n {score !== undefined &&\r\n <div className=\"rating\" style={style} title={`${calculatedScore}} % übereinstimmung - laut unserer künstlichen intelligenz. hier klicken um mehr zu erfahren.`}>{calculatedScore} % ähnlich</div>\r\n }\r\n {(subtitle && subtitle.length > 0) &&\r\n <p className=\"card-text\">{subtitle}</p>\r\n }\r\n <div className=\"d-flex justify-content-between align-items-center\">\r\n <div className=\"btn-group\">\r\n {(props.rights & AccessType.Edit) > 0 &&\r\n <Link className=\"btn btn-link\" to={`${url}/edit`}>bearbeiten</Link>\r\n }\r\n {(props.rights & AccessType.Delete) > 0 &&\r\n <button disabled={deletionState !== DeletionState.Ready} className=\"btn btn-link\" onClick={onClickDelete}>löschen</button>\r\n }\r\n </div>\r\n <small style={{cursor: 'default'}} className=\"text-muted\">letzte änderung <Moment locale=\"de-CH\" fromNow date={updated} /></small>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import * as React from \"react\";\r\nimport { useEffect, useState } from \"react\";\r\nimport { Link, useParams } from \"react-router-dom\";\r\nimport { ToastHelpers } from \"../../helpers/ToastHelpers\";\r\nimport useWindowScrollPosition from \"../../hooks/useWindowScrollPosition\";\r\nimport { ImitationPreviewModel } from \"../../models\";\r\nimport { ApiService } from \"../../services/ApiService\";\r\nimport { SortableImitations } from \"../../services/new-sorting/SortableImitations\";\r\nimport Loader from \"../Infrastructure/Loader\";\r\nimport IRouterParams from \"../Router/IRouterParams\";\r\nimport Preview from \"../Shared/Preview\";\r\nimport Title from \"../Shared/Title\";\r\n\r\nexport default function Detail() {\r\n\r\n const { collectionId } = useParams<IRouterParams>();\r\n const [imitations, setImitations] = useState<ImitationPreviewModel[]>([]);\r\n const [isLoading, setIsLoading] = useState<boolean>(true);\r\n\r\n useWindowScrollPosition('Collection_Detail_ScrollY', !isLoading);\r\n\r\n useEffect(() => {\r\n const fetchData = async () => {\r\n try {\r\n if (imitations.length <= 0) {\r\n const apiService = new ApiService(`${collectionId}/imitation`, `/collection/${collectionId}`);\r\n const imitations = await apiService.get<ImitationPreviewModel[]>();\r\n setImitations(imitations);\r\n }\r\n } catch (error) {\r\n ToastHelpers.showSimple('🙅 album kann nicht geöffnet werden.');\r\n } finally {\r\n setIsLoading(false);\r\n }\r\n }\r\n if (isLoading) {\r\n fetchData();\r\n }\r\n },[isLoading, collectionId, imitations.length]);\r\n\r\n if (isLoading) {\r\n return <Loader />;\r\n }\r\n\r\n const onClickShareUrl = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {\r\n e.preventDefault();\r\n const shareUrl = `${window.location.protocol}//${window.location.host}/collection/${collectionId}`;\r\n navigator.clipboard.writeText(shareUrl);\r\n ToastHelpers.showSimple('😻 kopiert. jetzt musst du es nur noch teilen.');\r\n };\r\n\r\n return (\r\n <>\r\n <Title title=\"wähle ein bild und stelle es nach\" urlText=\"alle öffentlichen alben anschauen\" url={`/`} />\r\n <div className=\"container\">\r\n <div className=\"row justify-content-center p-2\">\r\n <div className=\"alert alert-warning text-center\" style={{ marginTop: 0 }}>\r\n eine erklärung in videoform findet ihr <a href=\"https://youtu.be/imgfCUrx-ok\" title=\"YouTube öffnen\" target=\"youtube\">hier</a>.\r\n </div>\r\n </div>\r\n {imitations.length > 0 && (\r\n <>\r\n <SortableImitations dataSource={imitations}>\r\n {(x: ImitationPreviewModel) => (\r\n <Preview {...x}\r\n imitationId={x.id}\r\n key={`cp_${x.id}`}\r\n className=\"col-sm-12 col-md-4 p-2\"\r\n pixelate={false}\r\n url={`/collection/${collectionId}/imitation/${x.id}`}\r\n deleteEndpoint={`/${collectionId}/imitation/${x.id}`}>\r\n {x.imageCount > 0 &&\r\n <>\r\n <h4 className=\"img-info\">\r\n {x.imageCount} <span role=\"img\" aria-label=\"imitations\">🐵</span>\r\n </h4>\r\n <div className=\"img-stats\">\r\n {x.imageCount} <span role=\"img\" aria-label=\"imitations\">🐵</span>\r\n </div>\r\n </>\r\n }\r\n </Preview>\r\n )\r\n }\r\n </SortableImitations>\r\n <div className=\"row justify-content-center\">\r\n <div className=\"alert alert-secondary text-center\">\r\n nicht genug? jetzt noch <Link to={`${collectionId}/add`}>mehr bilder hinzufügen</Link>,\r\n alle bilder <Link to={`${collectionId}/mass-edit`}>bearbeiten</Link> oder\r\n dieses <button className=\"btn btn-link\" onClick={onClickShareUrl}>album teilen</button>.\r\n </div>\r\n </div>\r\n </>\r\n )}\r\n\r\n {imitations.length <= 0 &&\r\n <div className=\"row\">\r\n <div className=\"col-sm-12 text-center\">\r\n <img src=\"/hey.svg\" alt=\"bist du bereit?\" width=\"40%\" />\r\n <p className=\"p-3 text-center\">\r\n hmm? keine fotos in dieser sammlung gefunden! wir sind\r\n bereit für <Link to={`${collectionId}/add`}>deine bilder</Link>.\r\n </p>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n </>\r\n );\r\n}\r\n","import * as React from 'react';\r\nimport { EditStatus } from './EditStatus';\r\n\r\nexport interface IEditStatusWidgetProps {\r\n status: EditStatus;\r\n}\r\n\r\nexport function EditStatusWidget(props: IEditStatusWidgetProps) {\r\n switch (props.status) {\r\n case EditStatus.Idle:\r\n return <></>\r\n case EditStatus.Saving:\r\n return <div className=\"text-muted pt-2\"><span role=\"img\" aria-label=\"See no evil!\">🙈</span> speichere ...</div>;\r\n case EditStatus.Saved:\r\n return <div className=\"text-muted pt-2\"><span role=\"img\" aria-label=\"Monkey see!\">🙉</span> gespeichert!</div>;\r\n case EditStatus.Error:\r\n return <div className=\"text-muted pt-2\"><span role=\"img\" aria-label=\"Explode\">💥</span> fehler beim speichern</div>;\r\n default:\r\n throw Error(`Status not handled ${props.status}.`);\r\n }\r\n}\r\n","import * as React from 'react';\r\nimport { useState, useEffect } from 'react';\r\nimport useDebounce from '../../hooks/useDebounce';\r\nimport { EditStatus } from './EditStatus';\r\nimport { EditStatusWidget } from './EditStatusWidget';\r\nimport { FieldEditorType } from './FieldEditorType';\r\nimport { ValidationError } from '../../models';\r\nimport InlineValidationError from '../Infrastructure/InlineValidationError';\r\nimport { ApiService, JSONPatchOperation, JSONPatchOperationType } from '../../services';\r\n\r\ninterface IFieldEditorProps {\r\n value: string;\r\n id: string;\r\n label: string;\r\n showLabel?: boolean;\r\n propertyName: string;\r\n endpoint: string;\r\n type: FieldEditorType;\r\n}\r\n\r\nexport default function FieldEditor(props: IFieldEditorProps) {\r\n const [value, setValue] = useState<string>(props.value);\r\n const [lastValue, setLastValue] = useState<string>(props.value);\r\n const [editState, setEditState] = useState<EditStatus>(EditStatus.Idle);\r\n const [error, setError] = useState<ValidationError | null>(null);\r\n const debouncedValue = useDebounce(value, 500);\r\n const apiService = new ApiService(props.endpoint);\r\n\r\n useEffect(() => {\r\n async function saveValue() {\r\n // if they haven't picked an image yet, we can't update its title or comments (need an id to PATCH)\r\n if (props.id === '') {\r\n return;\r\n }\r\n // Make sure we have a value (user has entered something in input)\r\n if (debouncedValue !== lastValue) {\r\n setLastValue(debouncedValue);\r\n\r\n // Dynamically create the JSON patch object based on the field changed\r\n let requestObject: Array<JSONPatchOperation> = [{\r\n \"op\": JSONPatchOperationType.Replace,\r\n \"path\": props.propertyName,\r\n \"value\": debouncedValue\r\n }];\r\n\r\n await apiService.patch(\r\n requestObject,\r\n () => {\r\n setEditState(EditStatus.Saved);\r\n setTimeout(() => setEditState(EditStatus.Idle), 1500)\r\n },\r\n (error) => {\r\n setError(error);\r\n setEditState(EditStatus.Error);\r\n }\r\n );\r\n }\r\n }\r\n saveValue();\r\n });\r\n\r\n // Unique key for identifying this field\r\n const fieldKey = `key_${props.id}_${props.propertyName}`;\r\n const cssClass = `form-control ${editState === EditStatus.Error ? ' is-invalid' : ''}`;\r\n\r\n return (\r\n <div className=\"form-group\">\r\n {(props.showLabel || props.showLabel === undefined) &&\r\n <label className=\"text-muted\" htmlFor={fieldKey}>{props.label}</label>\r\n }\r\n {props.type === FieldEditorType.Input && (\r\n <>\r\n <input\r\n type=\"text\"\r\n id={fieldKey}\r\n placeholder={props.label}\r\n name={fieldKey}\r\n className={cssClass}\r\n value={value ?? \"\"}\r\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => { setEditState(EditStatus.Saving); setValue(e.target.value) }}\r\n />\r\n <InlineValidationError field={props.propertyName} error={error} />\r\n </>\r\n )}\r\n {props.type === FieldEditorType.Textarea && (\r\n <>\r\n <textarea\r\n id={fieldKey}\r\n placeholder={props.label}\r\n name={fieldKey}\r\n className={cssClass}\r\n value={value ?? \"\"}\r\n onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => { setEditState(EditStatus.Saving); setValue(e.target.value) }}\r\n />\r\n <InlineValidationError field={props.propertyName} error={error} />\r\n </>\r\n )}\r\n <EditStatusWidget status={editState} />\r\n </div>\r\n );\r\n}\r\n","import { useState, useEffect } from 'react';\r\n\r\n// Taken from https://dev.to/gabe_ragland/debouncing-with-react-hooks-jci\r\nexport default function useDebounce(value: any, delay: number) {\r\n\r\n const [debouncedValue, setDebouncedValue] = useState(value);\r\n\r\n useEffect(\r\n () => {\r\n const handler = setTimeout(() => {\r\n setDebouncedValue(value);\r\n }, delay);\r\n\r\n return () => {\r\n clearTimeout(handler);\r\n };\r\n }\r\n );\r\n\r\n return debouncedValue;\r\n}\r\n","import * as React from 'react';\r\nimport { useEffect, useState } from 'react';\r\nimport { useParams } from 'react-router-dom';\r\nimport { ToastHelpers } from '../../helpers/ToastHelpers';\r\nimport useWindowScrollPosition from '../../hooks/useWindowScrollPosition';\r\nimport { CollectionModel } from '../../models';\r\nimport { ApiService } from '../../services';\r\nimport FieldEditor from '../Editor/FieldEditor';\r\nimport { FieldEditorType } from '../Editor/FieldEditorType';\r\nimport Loader from '../Infrastructure/Loader';\r\nimport IRouterParams from '../Router/IRouterParams';\r\nimport Title from '../Shared/Title';\r\n\r\nexport default function Edit() {\r\n const { collectionId } = useParams<IRouterParams>();\r\n const [collection, setCollection] = useState<CollectionModel | undefined>(undefined);\r\n const [isLoading, setIsLoading] = useState<boolean>(true);\r\n useWindowScrollPosition('Collection_Edit_ScrollY', !isLoading);\r\n\r\n const fetchData = async () => {\r\n try {\r\n const apiService = new ApiService(`/collection/${collectionId}`);\r\n const collection = await apiService.get<CollectionModel>();\r\n setCollection(collection);\r\n } catch (error) {\r\n ToastHelpers.showSimple('⌛ album kann nicht geladen werden. bitte später nochmals versuchen.');\r\n } finally {\r\n setIsLoading(false);\r\n }\r\n };\r\n\r\n useEffect(() => {\r\n if (isLoading) {\r\n fetchData();\r\n }\r\n });\r\n\r\n if (isLoading) {\r\n return <Loader />;\r\n }\r\n\r\n if (collection === undefined) {\r\n return <Title title=\"kein album gefunden\" urlText=\"jetzt ein album erstellen\" url={`/create`} />;\r\n }\r\n\r\n return (\r\n <>\r\n <Title title=\"album bearbeiten\" urlText=\"zurück zu den öffentlichen albun\" url={`/collection/${collectionId}`} />\r\n <div className=\"container\">\r\n <div className=\"row pb-4\">\r\n <div className=\"col-md-12\">\r\n <FieldEditor\r\n id={collection.id}\r\n value={collection.title}\r\n type={FieldEditorType.Input}\r\n label=\"titel:\"\r\n propertyName=\"Title\"\r\n endpoint={`/collection/${collectionId}`}\r\n />\r\n <FieldEditor\r\n id={collection.id}\r\n value={collection.description}\r\n type={FieldEditorType.Textarea}\r\n label=\"beschreibung:\"\r\n propertyName=\"Description\"\r\n endpoint={`/collection/${collectionId}`}\r\n />\r\n </div>\r\n </div>\r\n </div>\r\n </>\r\n );\r\n}\r\n","import * as React from \"react\";\r\nimport { useEffect, useState } from \"react\";\r\nimport { ImageSize, ImitationPreviewModel, } from \"../../models\";\r\nimport FieldEditor from \"../Editor/FieldEditor\";\r\nimport { FieldEditorType } from \"../Editor/FieldEditorType\";\r\nimport Title from \"../Shared/Title\";\r\nimport Loader from \"../Infrastructure/Loader\";\r\nimport { useParams } from \"react-router-dom\";\r\nimport IRouterParams from \"../Router/IRouterParams\";\r\nimport { ApiService } from \"../../services\";\r\nimport { ToastHelpers } from \"../../helpers/ToastHelpers\";\r\nimport useWindowScrollPosition from \"../../hooks/useWindowScrollPosition\";\r\n\r\nexport default function MassEdit() {\r\n const { collectionId } = useParams<IRouterParams>();\r\n const [imitations, setImitations] = useState<ImitationPreviewModel[]>([]);\r\n const [isLoading, setIsLoading] = useState<boolean>(true);\r\n useWindowScrollPosition('Imitation_MassEdit_ScrollY', !isLoading);\r\n\r\n const fetchData = async () => {\r\n try {\r\n if (imitations.length <= 0) {\r\n const apiService = new ApiService(`${collectionId}/imitation`);\r\n const imitations = await apiService.get<ImitationPreviewModel[]>();\r\n setImitations(imitations);\r\n }\r\n } catch (error) {\r\n ToastHelpers.showSimple('🙅 album kann nicht geöffnet werden.');\r\n } finally {\r\n setIsLoading(false);\r\n }\r\n };\r\n\r\n useEffect(() => {\r\n if (isLoading) {\r\n fetchData();\r\n }\r\n });\r\n\r\n if (isLoading) {\r\n return <Loader />;\r\n }\r\n\r\n return (\r\n <>\r\n <Title title=\"album bearbeiten\" urlText=\"zur ansicht wechseln\" url={`/collection/${collectionId}`} />\r\n <div className=\"container\">\r\n <div className=\"row\">\r\n {imitations.map((x) => {\r\n return (\r\n <React.Fragment key={`ie_${x.id}`}>\r\n <div className=\"col-sm-2 mb-2\">\r\n <img className=\"w-100\" src={x.image.sources[ImageSize.Square]} alt=\"Preview\" />\r\n </div>\r\n <div className=\"col-sm-4\">\r\n <FieldEditor\r\n id={x.image.id}\r\n value={x.image.title}\r\n type={FieldEditorType.Input}\r\n label=\"titel:\"\r\n propertyName=\"Title\"\r\n endpoint={`/${x.id}/image/${x.image.id}`}\r\n />\r\n <FieldEditor\r\n id={x.image.id}\r\n value={x.image.comments}\r\n type={FieldEditorType.Textarea}\r\n label=\"kommentare:\"\r\n propertyName=\"Comments\"\r\n endpoint={`/${x.id}/image/${x.image.id}`}\r\n />\r\n </div>\r\n </React.Fragment>\r\n );\r\n })}\r\n </div>\r\n </div>\r\n </>\r\n );\r\n}\r\n","export default class Constants {\r\n public static readonly GuidRegex = \"[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}\";\r\n}\r\n","import * as React from \"react\";\r\nimport { useEffect, useState } from \"react\";\r\nimport { useParams } from \"react-router-dom\";\r\nimport Moment from \"react-moment\";\r\nimport 'moment/locale/de-ch';\r\nimport { ImageViewModel, ImageSize, AccessType } from \"../../models\";\r\nimport Loader from \"../Infrastructure/Loader\";\r\nimport IRouterParams from \"../Router/IRouterParams\";\r\nimport Title from \"../Shared/Title\";\r\nimport { ApiService } from \"../../services\";\r\nimport { ImageLoader } from \"./ImageLoader\";\r\nimport { ColorHelper } from \"../../helpers/ColorHelper\";\r\n\r\nexport default function Detail() {\r\n const { imitationId, imageId, collectionId } = useParams<IRouterParams>();\r\n const [image, setImage] = useState<ImageViewModel | undefined>(undefined);\r\n const imageService = new ApiService(`${imitationId}/image/${imageId}`);\r\n\r\n const fetchImage = async () => {\r\n const image = await imageService.get<ImageViewModel>();\r\n setImage(image);\r\n };\r\n\r\n useEffect(() => {\r\n if (image === undefined) {\r\n fetchImage();\r\n }\r\n });\r\n\r\n if (image === undefined) {\r\n return <Loader />;\r\n }\r\n\r\n return (\r\n <>\r\n <Title title=\"bilddetails anzeigen\" urlText=\"zurück zum originalbild\" url={`/collection/${collectionId}/imitation/${imitationId}`} />\r\n <div className=\"container\">\r\n <div className=\"row pb-4\">\r\n <div className=\"col-md-6 mb-4 p-0\">\r\n <ImageLoader image={image} size={ImageSize.Square} pixelate={(image.rights & AccessType.Edit) <= 0} />\r\n </div>\r\n <div className=\"col-md-6\">\r\n <h3>{image.title}</h3>\r\n <hr />\r\n {image.score > 0 &&\r\n <>\r\n <p>unser system hat eine <b>ähnlichkeit von {Math.round(image.score * 100)} %</b> zum originalbild berechnet.</p>\r\n <p>tags: {image.tags.map((value, index) => {\r\n const colorInfo = ColorHelper.getCSSColorPair(1, value);\r\n return (\r\n <span style={colorInfo} className=\"badge p-2 m-1 tag\" key={`tag_${index}`}>\r\n {value}\r\n </span>\r\n )\r\n })}</p>\r\n <hr />\r\n </>\r\n }\r\n {image.comments &&\r\n <>\r\n <p>{image.comments}</p>\r\n <hr />\r\n </>\r\n }\r\n <p>von: {image.uploadedBy?.name}</p>\r\n {image.updated &&\r\n <p>geändert: <Moment locale=\"de-CH\" calendar date={image.updated} /></p>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n </>\r\n );\r\n}\r\n","import * as React from \"react\";\r\nimport { useEffect, useState } from \"react\";\r\nimport { useParams } from \"react-router-dom\";\r\nimport { ImageModel, ImageSize } from \"../../models\";\r\nimport { ApiService } from \"../../services\";\r\nimport FieldEditor from \"../Editor/FieldEditor\";\r\nimport { FieldEditorType } from \"../Editor/FieldEditorType\";\r\nimport Loader from \"../Infrastructure/Loader\";\r\nimport IRouterParams from \"../Router/IRouterParams\";\r\nimport Title from \"../Shared/Title\";\r\n\r\nexport default function Edit() {\r\n const { imitationId, imageId, collectionId } = useParams<IRouterParams>();\r\n const [image, setImage] = useState<ImageModel | undefined>(undefined);\r\n\r\n useEffect(() => {\r\n if (image === undefined) {\r\n fetchImage();\r\n }\r\n });\r\n\r\n const fetchImage = async () => {\r\n const apiService = new ApiService(`${imitationId}/image/${imageId}`);\r\n const image = await apiService.get<ImageModel>();\r\n setImage(image);\r\n };\r\n\r\n if (image === undefined) {\r\n return <Loader />;\r\n }\r\n return (\r\n <>\r\n <Title title=\"imitation bearbeiten\" urlText=\"zurück zum originalbild\" url={`/collection/${collectionId}/imitation/${imitationId}`} />\r\n <div className=\"container\">\r\n <div className=\"row pb-4\">\r\n <div className=\"col-md-6\">\r\n <img className=\"w-100 pb-2\" src={image.sources[ImageSize.Square]} alt={image.title} />\r\n </div>\r\n <div className=\"col-md-6\">\r\n <FieldEditor\r\n id={image.id}\r\n value={image.title}\r\n type={FieldEditorType.Input}\r\n label=\"titel:\"\r\n propertyName=\"Title\"\r\n endpoint={`/${imitationId}/image/${image.id}`}\r\n />\r\n <FieldEditor\r\n id={image.id}\r\n value={image.comments}\r\n type={FieldEditorType.Textarea}\r\n label=\"kommentare:\"\r\n propertyName=\"Comments\"\r\n endpoint={`/${imitationId}/image/${image.id}`}\r\n />\r\n </div>\r\n </div>\r\n </div>\r\n </>\r\n );\r\n}\r\n","import * as React from \"react\";\r\nimport { ErrorHelpers } from \"../../helpers/ErrorHelpers\";\r\nimport { ToastHelpers } from \"../../helpers/ToastHelpers\";\r\nimport { UploadState } from \"../../models\";\r\nimport UploadedImageFile from \"../../models/IUploadedImageFile\";\r\nimport FieldEditor from \"../Editor/FieldEditor\";\r\nimport { FieldEditorType } from \"../Editor/FieldEditorType\";\r\n\r\nexport default function EditInline(props: UploadedImageFile) {\r\n const { imageId, imitationId, state, error, browserPreviewUrl } = props;\r\n\r\n switch (state) {\r\n case UploadState.Added:\r\n return (\r\n <p className=\"text-muted p-3\">dein bild wird hochgeladen ...</p>\r\n );\r\n case UploadState.Error:\r\n if (error) {\r\n const fieldErrors = ErrorHelpers.getFieldErrors(error, \"Image\");\r\n ToastHelpers.showSimple(`❌ ${fieldErrors.join(\", \")}`);\r\n }\r\n return <></>;\r\n case UploadState.Uploaded:\r\n if (!imageId) {\r\n return <></>;\r\n }\r\n return (\r\n <>\r\n <div className=\"row pl-3 pr-3 pt-3 pb-1\">\r\n <div className=\"col-auto w-25 p-0\">\r\n <div\r\n style={{\r\n backgroundSize: \"cover\",\r\n backgroundImage:\r\n \"url(\" + browserPreviewUrl + \")\",\r\n width: \"100%\",\r\n height: \"100%\",\r\n }}\r\n ></div>\r\n </div>\r\n <div className=\"w-75 pl-2\">\r\n <FieldEditor\r\n id={imageId}\r\n value=\"\"\r\n type={FieldEditorType.Input}\r\n showLabel={false}\r\n label=\"titel\"\r\n propertyName=\"Title\"\r\n endpoint={`/${imitationId}/image/${imageId}`}\r\n />\r\n <FieldEditor\r\n id={imageId}\r\n value=\"\"\r\n type={FieldEditorType.Textarea}\r\n showLabel={false}\r\n label=\"beschreibung\"\r\n propertyName=\"Comments\"\r\n endpoint={`/${imitationId}/image/${imageId}`}\r\n />\r\n </div>\r\n </div>\r\n </>\r\n );\r\n default:\r\n return <></>;\r\n }\r\n}\r\n","import { ImageType } from \"../models\";\r\nimport IUploadedImageFile from \"../models/IUploadedImageFile\";\r\nimport { UploadState } from \"../models/UploadState\";\r\nimport { ValidationError } from \"../models/ValidationError\";\r\n\r\nexport default class UploadedImageFile implements IUploadedImageFile {\r\n public browserPreviewUrl: string;\r\n public imageId?: string;\r\n public imitationId: string;\r\n public state?: UploadState;\r\n public error?: ValidationError;\r\n public onImageChange?(): void;\r\n public originalFile: File;\r\n public type: ImageType;\r\n\r\n constructor(imitationId: string, file: File, type: ImageType) {\r\n this.imitationId = imitationId;\r\n this.originalFile = file;\r\n this.type = type;\r\n this.browserPreviewUrl = URL.createObjectURL(file);\r\n this.state = UploadState.Added;\r\n }\r\n\r\n public updateAttributes(imageId: string): UploadedImageFile {\r\n this.imageId = imageId;\r\n this.state = UploadState.Uploaded;\r\n return this;\r\n }\r\n\r\n public getAsFormData() {\r\n const formData = new FormData();\r\n formData.append('Image', this.originalFile as Blob);\r\n formData.append('Type', this.type.toString());\r\n return formData;\r\n }\r\n}\r\n","import React, { useState } from 'react';\r\nimport { useDropzone } from 'react-dropzone';\r\nimport { ImageModel, ValidationError, ImitationModel, ImageType, ImitationPreviewModel } from '../../models';\r\nimport InlineValidationError from '../Infrastructure/InlineValidationError';\r\nimport EditInline from './EditInline';\r\nimport Loader from '../Infrastructure/Loader';\r\nimport { ApiService } from '../../services';\r\nimport { ToastHelpers } from '../../helpers/ToastHelpers';\r\nimport { ErrorHelpers } from '../../helpers/ErrorHelpers';\r\nimport UploadedImageFile from '../../services/UploadedImageFile';\r\n\r\ninterface IFileUploaderProps {\r\n collectionId: string;\r\n imitationId?: string;\r\n imitations?: Array<ImitationPreviewModel>;\r\n onImageChange?(): void;\r\n}\r\n\r\nexport default function FileUploader(props: IFileUploaderProps) {\r\n const { collectionId, imitations, onImageChange } = props;\r\n const [files, setFiles] = useState<Array<UploadedImageFile>>([]);\r\n const [isLoading, setIsLoading] = useState<boolean>(false);\r\n const [\r\n validationError,\r\n setValidationError,\r\n ] = useState<ValidationError | null>(null);\r\n\r\n const imitationService = new ApiService(`${collectionId}/imitation`);\r\n\r\n const createImitation = (file: File) => {\r\n imitationService.post<ImitationModel>({ title: file.name, },\r\n (value: ImitationModel) => {\r\n setValidationError(null);\r\n uploadImage(new UploadedImageFile(value.id, file, ImageType.Source));\r\n },\r\n (error) => {\r\n setValidationError(error);\r\n setIsLoading(false);\r\n }\r\n );\r\n }\r\n\r\n const uploadImage = async (file: UploadedImageFile) => {\r\n\r\n const imageService = new ApiService(`${file.imitationId}/image`);\r\n setFiles([...files, file]);\r\n\r\n await imageService.postFormData<ImageModel>(\r\n file.getAsFormData(),\r\n (value: ImageModel) => {\r\n setFiles([...files, file.updateAttributes(value.id)]);\r\n if (onImageChange) {\r\n onImageChange();\r\n }\r\n },\r\n (error: ValidationError) => {\r\n // filter out failed uploads using browserPreviewUrl as key\r\n setFiles([\r\n ...files.filter(x => x.browserPreviewUrl !== file.browserPreviewUrl)\r\n ]);\r\n const fieldErrors = ErrorHelpers.getFieldErrors(error, \"Image\");\r\n ToastHelpers.showSimple(`❌ ${fieldErrors.join(\", \")}`);\r\n }\r\n );\r\n\r\n setIsLoading(false);\r\n };\r\n\r\n const { getRootProps, getInputProps, isDragActive } = useDropzone({\r\n multiple: true,\r\n accept: 'image/*',\r\n onDrop: (acceptedFiles) => {\r\n acceptedFiles.map(async (file) => {\r\n setIsLoading(true);\r\n ToastHelpers.showSimple(`🚀 bild ${file.name} hochladen ...`)\r\n if (props.imitationId === undefined) {\r\n createImitation(file);\r\n } else {\r\n uploadImage(new UploadedImageFile(props.imitationId, file, ImageType.ImitationRaw));\r\n }\r\n });\r\n },\r\n });\r\n\r\n const classNames = 'dropzone card text-center upload-card' + (files.length > 0 ? ' h-25' : '');\r\n const totalImages = files.length + (imitations !== undefined ? imitations.length : 0);\r\n\r\n return (\r\n <div className=\"h-100\">\r\n <div className={isDragActive ? classNames + ' dragActive' : classNames} {...getRootProps()}>\r\n <input {...getInputProps()} />\r\n <div>\r\n <h3>füge dein bild hinzu</h3>\r\n <p>\r\n {isDragActive ? 'fast geschafft 🐒' : ''}\r\n {totalImages > 0 ? 'noch eins? klick nochmals!' : 'hier klicken oder reinziehen'}\r\n </p>\r\n </div>\r\n </div>\r\n <div className=\"h-75\">\r\n {isLoading && <Loader />}\r\n {files.length > 0 && files.map((file, i) => {\r\n return (\r\n <div key={file.browserPreviewUrl}>\r\n <EditInline {...file} onImageChange={onImageChange} />\r\n {(i < files.length - 1) && <hr className=\"p-0 m-0\" />}\r\n </div>\r\n );\r\n })}\r\n <InlineValidationError field=\"Image\" error={validationError} />\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import * as React from 'react';\r\nimport FileUploader from '../Image/FileUploader';\r\nimport { useParams } from 'react-router-dom';\r\nimport IRouterParams from '../Router/IRouterParams';\r\nimport Title from '../Shared/Title';\r\n\r\nexport default function Add() {\r\n const { collectionId } = useParams<IRouterParams>();\r\n\r\n return (\r\n <>\r\n <Title title=\"bilder hinzufügen\" urlText=\"album anzeigen\" url={`/collection/${collectionId}`} />\r\n <div className=\"container\">\r\n <div className=\"row pb-4\">\r\n <div className=\"col-md-12\">\r\n <FileUploader collectionId={collectionId} />\r\n </div>\r\n </div>\r\n </div>\r\n </>\r\n );\r\n}\r\n","import { ImagePreviewModel } from \"../../models\";\r\nimport { Direction } from \"../sorting\";\r\nimport { SortableBase, SortableBaseDefaultProps } from \"./SortableBase\";\r\n\r\nexport class SortableImages extends SortableBase<ImagePreviewModel> {\r\n static defaultProps: SortableBaseDefaultProps<ImagePreviewModel> = {\r\n sorters: [\r\n { label: \"🙉 beste übereinstimmung\", property: \"score\", direction: Direction.Desc },\r\n { label: \"🙈 wenig ähnlich\", property: \"score\", direction: Direction.Asc },\r\n { label: \"🧓 älteste\", property: \"updated\", direction: Direction.Asc },\r\n { label: \"👶 neuste\", property: \"updated\", direction: Direction.Desc }\r\n ],\r\n }\r\n}\r\n","import * as React from \"react\";\r\nimport { ImageType, ImagePreviewModel } from \"../../models\";\r\nimport { SortableImages } from \"../../services/new-sorting/SortableImages\";\r\nimport Loader from \"../Infrastructure/Loader\";\r\nimport Preview from \"../Shared/Preview\";\r\n\r\ninterface IUserImagesProps {\r\n isLoading: boolean;\r\n images: ImagePreviewModel[];\r\n collectionId: string;\r\n imitationId: string;\r\n}\r\n\r\nexport default function DetailUploads(props: IUserImagesProps) {\r\n const { isLoading, images, collectionId, imitationId } = props;\r\n const imitationImages = images.filter((x) => x.type === ImageType.ImitationRaw);\r\n\r\n return (\r\n <>\r\n <div className=\"col-lg-12 pt-4\">\r\n <h5 className=\"text-center\">imitationen von anderen</h5>\r\n <hr />\r\n </div>\r\n {imitationImages.length <= 0 && isLoading && <Loader/>}\r\n {imitationImages.length <= 0 && !isLoading && (\r\n <div className=\"row pb-4\">\r\n <div className=\"col-lg-12 pt-4\">\r\n <p className=\"text-center\">\r\n zu diesem bild gibt es noch keine imitationen. lade jetzt dein foto hoch.\r\n </p>\r\n </div>\r\n </div>\r\n )}\r\n {imitationImages.length > 0 && !isLoading && (\r\n <SortableImages dataSource={images}>\r\n {(x: ImagePreviewModel) => {\r\n const creator = `von ${x.uploadedBy?.name}`;\r\n return (\r\n <Preview\r\n key={`ip_${x.id}`}\r\n className=\"col-md-4 col-xs-12 p-2 specialTest\"\r\n image={x}\r\n imitationId={imitationId}\r\n title={creator}\r\n score={x.score}\r\n updated={x.updated}\r\n rights={x.rights}\r\n pixelate={true}\r\n url={`/collection/${collectionId}/imitation/${imitationId}/image/${x.id}`}\r\n deleteEndpoint={`/${imitationId}/image/${x.id}`}\r\n >\r\n <h4 className=\"img-info\">\r\n <span role=\"img\" aria-label={creator}>\r\n 🐵\r\n </span>\r\n </h4>\r\n </Preview>\r\n );\r\n }}\r\n </SortableImages>\r\n\r\n\r\n )}\r\n </>\r\n );\r\n}\r\n","import * as React from 'react';\r\nimport { useParams } from 'react-router-dom';\r\nimport { ImagePreviewModel, ImageSize, ImitationViewModel } from '../../models';\r\nimport Loader from '../Infrastructure/Loader';\r\nimport { useEffect } from 'react';\r\nimport { ImageLoader } from '../Image/ImageLoader';\r\nimport FileUploader from '../Image/FileUploader';\r\nimport IRouterParams from '../Router/IRouterParams';\r\nimport DetailUploads from './DetailUploads';\r\nimport Title from '../Shared/Title';\r\nimport { ApiService } from '../../services';\r\nimport { ToastHelpers } from '../../helpers/ToastHelpers';\r\n\r\nexport default function Detail() {\r\n const { collectionId, imitationId } = useParams<IRouterParams>();\r\n const [imitation, setImitation] = React.useState<ImitationViewModel | null>(null);\r\n const [images, setImages] = React.useState<Array<ImagePreviewModel>>([]);\r\n const [isLoading, setIsLoading] = React.useState<boolean>(true);\r\n\r\n const fetchImages = React.useCallback(() => {\r\n const apiService = new ApiService(`${imitationId}/image`);\r\n apiService.get<Array<ImagePreviewModel>>()\r\n .then(\r\n (value) => {\r\n setImages(value);\r\n setIsLoading(false);\r\n },\r\n (reason: any) => {\r\n ToastHelpers.showSimple('🙀 nachstellungen von anderen personen können nicht geladen werden. versuchs nochmals.');\r\n setIsLoading(false);\r\n });\r\n }, [imitationId])\r\n\r\n const fetchImitation = React.useCallback(() => {\r\n const apiService = new ApiService(`${collectionId}/imitation/${imitationId}`);\r\n apiService.get<ImitationViewModel>()\r\n .then(\r\n (value) => {\r\n setImitation(value);\r\n fetchImages();\r\n },\r\n (reason: any) => {\r\n ToastHelpers.showSimple('🙀 imitation kann nicht geladen werden. versuchs nochmals.');\r\n setIsLoading(false);\r\n });\r\n }, [collectionId, imitationId, fetchImages])\r\n\r\n useEffect(() => {\r\n if (imitationId && isLoading) {\r\n fetchImitation();\r\n }\r\n }, [fetchImitation, imitationId, isLoading]);\r\n\r\n if (!imitation || !imitationId || collectionId === imitation.nextId || collectionId === imitation.prevId) {\r\n return <Loader />\r\n }\r\n\r\n return (\r\n <>\r\n <Title title={imitation.cover.title}\r\n urlText=\"zurück zum album\"\r\n url={`/collection/${collectionId}`}\r\n nextUrl={`/collection/${collectionId}/imitation/${imitation.nextId}`}\r\n prevUrl={`/collection/${collectionId}/imitation/${imitation.prevId}`} />\r\n\r\n <div className=\"container\">\r\n <div className=\"row\">\r\n <div className=\"col-md-6 p-2 large\">\r\n <ImageLoader image={imitation.cover} size={ImageSize.Full} />\r\n </div>\r\n <div className=\"col-md-6 p-2\">\r\n <FileUploader imitationId={imitationId} collectionId={collectionId} onImageChange={fetchImages} />\r\n </div>\r\n </div>\r\n <DetailUploads isLoading={isLoading} images={images} collectionId={collectionId} imitationId={imitationId} />\r\n </div>\r\n </>\r\n\r\n );\r\n}\r\n","import * as React from 'react';\r\nimport { ImageSize, ImitationViewModel } from '../../models';\r\nimport FieldEditor from '../Editor/FieldEditor';\r\nimport { FieldEditorType } from '../Editor/FieldEditorType';\r\nimport { useParams } from 'react-router-dom';\r\nimport IRouterParams from '../Router/IRouterParams';\r\nimport { useEffect } from 'react';\r\nimport Loader from '../Infrastructure/Loader';\r\nimport Title from '../Shared/Title';\r\nimport { ApiService } from '../../services';\r\nimport { ToastHelpers } from '../../helpers/ToastHelpers';\r\nimport useWindowScrollPosition from '../../hooks/useWindowScrollPosition';\r\n\r\nexport default function Edit() {\r\n const { imitationId, collectionId } = useParams<IRouterParams>();\r\n const [imitation, setImitation] = React.useState<ImitationViewModel | null>(null);\r\n const [isLoading, setIsLoading] = React.useState<boolean>(true);\r\n useWindowScrollPosition('Imitation_Edit_ScrollY', !isLoading);\r\n\r\n useEffect(() => {\r\n if (imitationId && isLoading) {\r\n const apiService = new ApiService(`${collectionId}/imitation/${imitationId}`);\r\n apiService.get<ImitationViewModel>()\r\n .then(\r\n (value) => {\r\n setImitation(value);\r\n setIsLoading(false);\r\n },\r\n (reason: any) => {\r\n ToastHelpers.showSimple('🖼️ bild kann nicht geladen werden. bitte später nochmals versuchen.');\r\n setIsLoading(false);\r\n });\r\n }\r\n }, [imitationId, collectionId, isLoading]);\r\n\r\n if (!imitation || !imitationId) {\r\n return <Loader />\r\n }\r\n\r\n return (\r\n <>\r\n <Title title=\"bildeigenschaften bearbeiten\" urlText=\"zurück zum album\" url={`/collection/${collectionId}`} />\r\n <div className=\"container\">\r\n <div className=\"row pb-4\">\r\n <div className=\"col-md-6\">\r\n <img className=\"w-100\" src={imitation.cover.sources[ImageSize.Square]} alt=\"Preview\" />\r\n </div>\r\n <div className=\"col-md-6\">\r\n <FieldEditor\r\n id={imitation.cover.id}\r\n value={imitation.cover.title}\r\n type={FieldEditorType.Input}\r\n label=\"titel:\"\r\n propertyName=\"Title\"\r\n endpoint={`/${imitationId}/image/${imitation.cover.id}`}\r\n />\r\n <FieldEditor\r\n id={imitation.cover.id}\r\n value={imitation.cover.comments}\r\n type={FieldEditorType.Textarea}\r\n label=\"kommentare:\"\r\n propertyName=\"Comments\"\r\n endpoint={`/${imitationId}/image/${imitation.cover.id}`}\r\n />\r\n </div>\r\n </div>\r\n </div>\r\n </>\r\n );\r\n}\r\n","import * as React from 'react';\r\nimport { Link } from 'react-router-dom';\r\n\r\nexport default function NotFound() {\r\n return (\r\n <div className=\"container\">\r\n <div className=\"row justify-content-center\">\r\n <img className=\"pt-4\" src=\"/hey.svg\" alt=\"was machst du?\" width=\"40%\" />\r\n <p className=\"text-center p-3 m-0\">was du suchst, finden wir nicht. <br/><Link to={``}>hier</Link> geht`s zur startseite.</p>\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import * as React from \"react\";\r\nimport { useEffect, useState } from \"react\";\r\nimport { CollectionPreviewModel } from \"../../models\";\r\nimport { Link } from \"react-router-dom\";\r\nimport Loader from \"../Infrastructure/Loader\";\r\nimport Preview from \"../Shared/Preview\";\r\nimport Title from \"../Shared/Title\";\r\nimport { ApiService } from \"../../services\";\r\nimport { ToastHelpers } from \"../../helpers/ToastHelpers\";\r\nimport useWindowScrollPosition from \"../../hooks/useWindowScrollPosition\";\r\n\r\nexport default function Overview() {\r\n const [collections, setCollections] = useState<CollectionPreviewModel[]>([]);\r\n const [isLoading, setIsLoading] = useState<boolean>(true);\r\n useWindowScrollPosition('Collection_Overview_ScrollY', !isLoading);\r\n\r\n const fetchData = async () => {\r\n try {\r\n const apiService = new ApiService(`/collection`);\r\n const collections = await apiService.get<CollectionPreviewModel[]>();\r\n setCollections(collections);\r\n } catch (error) {\r\n ToastHelpers.showSimple('⌛ alben können nicht geladen werden. versuchen sie es in einer minute nochmals.');\r\n } finally {\r\n setIsLoading(false);\r\n }\r\n };\r\n\r\n useEffect(() => {\r\n if (isLoading) {\r\n fetchData();\r\n }\r\n });\r\n\r\n if (isLoading) {\r\n return <Loader />;\r\n }\r\n\r\n if (collections.length <= 0) {\r\n return <Title title=\"kein album gefunden\" urlText=\"jetzt ein album erstellen\" url={`/create`} />;\r\n }\r\n\r\n return (\r\n <>\r\n <Title title=\"wähle ein album\" urlText=\"oder erstelle selber eines\" url={`/create`}/>\r\n <div className=\"container\">\r\n <div className=\"row\">\r\n {collections.map((x) =>\r\n <Preview key={`op_${x.id}`} className=\"col-sm-12 col-md-4 p-2\" subtitle={x.description} {...x} url={`/collection/${x.id}`}>\r\n <img src=\"/overlay.svg\" alt=\"imitate\" className=\"card-img-hover\" />\r\n </Preview>\r\n )}\r\n </div>\r\n\r\n <div className=\"row justify-content-center\">\r\n <div className=\"alert alert-secondary text-center\">\r\n jetzt ein neues <Link to={`/create`}>album erstellen</Link>.\r\n </div>\r\n </div>\r\n </div>\r\n </>\r\n );\r\n}\r\n","import * as React from 'react';\r\nimport { useState } from 'react';\r\nimport { Redirect } from 'react-router-dom';\r\nimport { ValidationError, AccountModel } from '../../models';\r\nimport InlineValidationError from '../Infrastructure/InlineValidationError';\r\nimport useQuery from '../../hooks/useQuery';\r\nimport Title from '../Shared/Title';\r\nimport { AuthService } from '../../services';\r\n\r\nexport interface ILoginParams {\r\n redirectUrl: string | undefined;\r\n}\r\n\r\nexport function Login() {\r\n const authService = new AuthService();\r\n const query = useQuery();\r\n const [name, setName] = useState<string>('');\r\n const [code, setCode] = useState<string>('');\r\n const [isSaving, setIsSaving] = useState<boolean>(false);\r\n const [successful, setSuccessful] = useState<boolean>(false);\r\n const [errors, setErrors] = useState<ValidationError | null>(null);\r\n\r\n const login = () => {\r\n setIsSaving(true);\r\n authService.login({ name: name, code: code } as AccountModel,\r\n (value: AccountModel) => {\r\n setSuccessful(true);\r\n setIsSaving(false);\r\n },\r\n (value: ValidationError) => {\r\n setSuccessful(false);\r\n setIsSaving(false);\r\n setErrors(value);\r\n });\r\n }\r\n\r\n const onLoginClick = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {\r\n // Do not submit this form\r\n event.preventDefault();\r\n login();\r\n };\r\n\r\n const onKeyPressed = (e: React.KeyboardEvent<HTMLInputElement>) => {\r\n if (e.key === 'Enter') {\r\n login();\r\n }\r\n };\r\n\r\n if (successful) {\r\n\r\n const redirectUrl = query.get('redirectUrl');\r\n if (redirectUrl) {\r\n // ALWAYS check if it's an absolute URL or not\r\n const url = decodeURIComponent(redirectUrl);\r\n if (url.indexOf('://') <= 0 || url.indexOf('//') < 0) {\r\n // Absolute URL\r\n return <Redirect to={url} />;\r\n }\r\n }\r\n\r\n // redirect to home\r\n return <Redirect to=\"/\" />;\r\n }\r\n\r\n return (\r\n <>\r\n <Title title=\"melde dich an\" urlText=\"lieber nicht, zur startseite\" url={`/`} />\r\n <div className=\"container\">\r\n <div className=\"row justify-content-center\">\r\n <div className=\"col-md-6 mb-4\">\r\n <label htmlFor=\"name\">dein name:</label>\r\n <input\r\n id=\"name\"\r\n className=\"form-control\"\r\n placeholder=\"z. B. Jonas\"\r\n disabled={isSaving}\r\n type=\"text\"\r\n onKeyDown={onKeyPressed}\r\n onChange={(e) => setName(e.target.value)}\r\n />\r\n <InlineValidationError field=\"name\" error={errors} />\r\n </div>\r\n </div>\r\n <div className=\"row justify-content-center\">\r\n <div className=\"col-md-6 mb-4\">\r\n <label htmlFor=\"code\">zugriffscode (optional):</label>\r\n <input\r\n id=\"code\"\r\n className=\"form-control\"\r\n placeholder=\"z. B. X2810\"\r\n disabled={isSaving}\r\n type=\"text\"\r\n onKeyDown={onKeyPressed}\r\n onChange={(e) => setCode(e.target.value)}\r\n />\r\n <InlineValidationError field=\"code\" error={errors} />\r\n </div>\r\n </div>\r\n <div className=\"row justify-content-center\">\r\n <div className=\"col-md-6 mb-4\">\r\n <button disabled={isSaving} className=\"btn btn-primary btn-block\" onClick={onLoginClick}>anmelden</button>\r\n </div>\r\n </div>\r\n </div>\r\n </>\r\n );\r\n}\r\n","import { useLocation } from 'react-router-dom';\r\n\r\nexport default function useQuery() {\r\n return new URLSearchParams(useLocation().search);\r\n}\r\n","import * as React from 'react';\r\nimport CollectionAdd from '../Collection/Add';\r\nimport CollectionDetail from '../Collection/Detail';\r\nimport CollectionEdit from '../Collection/Edit';\r\nimport ImitationMassEdit from '../Imitation/MassEdit';\r\nimport Constants from '../../constants/Constants';\r\nimport ImageDetail from '../Image/Detail';\r\nimport ImageEdit from '../Image/Edit';\r\nimport ImitationAdd from '../Imitation/Add';\r\nimport ImitationDetail from '../Imitation/Detail';\r\nimport ImitationEdit from '../Imitation/Edit';\r\nimport NotFound from '../Infrastructure/NotFound';\r\nimport Overview from '../Collection/Overview';\r\nimport { Login } from '../Login/Login';\r\nimport { Switch, Route } from 'react-router-dom';\r\n\r\nexport function AppRouter() {\r\n return (\r\n <Switch>\r\n <Route exact path={`/collection/:collectionId(${Constants.GuidRegex})`}>\r\n <CollectionDetail />\r\n </Route>\r\n <Route exact path={`/collection/:collectionId(${Constants.GuidRegex})/edit`}>\r\n <CollectionEdit />\r\n </Route>\r\n <Route exact path={`/collection/:collectionId(${Constants.GuidRegex})/add`}>\r\n <ImitationAdd />\r\n </Route>\r\n <Route exact path={`/collection/:collectionId(${Constants.GuidRegex})/imitation/:imitationId(${Constants.GuidRegex})`}>\r\n <ImitationDetail />\r\n </Route>\r\n <Route exact path={`/collection/:collectionId(${Constants.GuidRegex})/imitation/:imitationId(${Constants.GuidRegex})/edit`}>\r\n <ImitationEdit />\r\n </Route>\r\n <Route exact path={`/collection/:collectionId(${Constants.GuidRegex})/mass-edit`}>\r\n <ImitationMassEdit />\r\n </Route>\r\n <Route exact path={`/collection/:collectionId(${Constants.GuidRegex})/imitation/:imitationId(${Constants.GuidRegex})/image/:imageId(${Constants.GuidRegex})`}>\r\n <ImageDetail />\r\n </Route>\r\n <Route exact path={`/collection/:collectionId(${Constants.GuidRegex})/imitation/:imitationId(${Constants.GuidRegex})/image/:imageId(${Constants.GuidRegex})/edit`}>\r\n <ImageEdit />\r\n </Route>\r\n <Route exact path=\"/create\">\r\n <CollectionAdd />\r\n </Route>\r\n <Route path=\"/login\">\r\n <Login />\r\n </Route>\r\n <Route exact path=\"/\">\r\n <Overview />\r\n </Route>\r\n <Route path=\"/\">\r\n <NotFound />\r\n </Route>\r\n </Switch>\r\n );\r\n}\r\n","import * as React from \"react\";\r\n\r\nexport interface ISlideProps {\r\n title: string;\r\n text: string;\r\n}\r\n\r\nexport function Slide(props: ISlideProps) {\r\n const { title, text } = props;\r\n return (\r\n <section data-transition=\"slide\">\r\n <h2>{title}</h2>\r\n <p>\r\n {text}\r\n </p>\r\n </section>\r\n );\r\n}\r\n","import * as React from \"react\";\r\nimport { useEffect } from \"react\";\r\nimport Reveal from \"reveal.js\";\r\n\r\n// TODO: programmatically always copy these from npm_modules?\r\nimport \"../../styles/reveal.js/reveal.css\";\r\nimport \"../../styles/reveal.js/theme/white.css\";\r\nimport { Slide } from \"./Slide\";\r\n\r\nexport default function Presentation() {\r\n useEffect(() => {\r\n let deck = new Reveal({\r\n autoSlide: 3000,\r\n loop: true,\r\n });\r\n deck.initialize();\r\n }, []);\r\n\r\n return (\r\n <div className=\"reveal\">\r\n <div className=\"slides\">\r\n <Slide title=\"Wow, a first slide\" text=\"here's the text prop\" />\r\n <Slide\r\n title=\"Second Slide!!!\"\r\n text=\"much coolness, many amazing\"\r\n />\r\n <Slide\r\n title=\"Wow, even with components\"\r\n text=\"this is truly amazing\"\r\n />\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import React from \"react\";\r\nimport { BrowserRouter as Router, Route, Switch } from \"react-router-dom\";\r\nimport Footer from \"./Layout/Footer\";\r\nimport Navigation from \"./Layout/Navigation\";\r\nimport { AppRouter } from \"./Router/AppRouter\";\r\nimport { ToastContainer } from \"react-toastify\";\r\nimport \"react-toastify/dist/ReactToastify.css\";\r\nimport Constants from \"../constants/Constants\";\r\nimport Presentation from \"./Presentation/Presentation\";\r\n\r\nexport default function App() {\r\n return (\r\n <Router>\r\n <Switch>\r\n <Route\r\n exact\r\n path={`/collection/:collectionId(${Constants.GuidRegex})/presentation`}\r\n >\r\n <Presentation />\r\n </Route>\r\n <Route>\r\n <header>\r\n <Navigation />\r\n </header>\r\n <main role=\"main\">\r\n <AppRouter />\r\n </main>\r\n <Footer />\r\n <ToastContainer/>\r\n </Route>\r\n </Switch>\r\n </Router>\r\n );\r\n}\r\n","// This optional code is used to register a service worker.\r\n// register() is not called by default.\r\n\r\n// This lets the app load faster on subsequent visits in production, and gives\r\n// it offline capabilities. However, it also means that developers (and users)\r\n// will only see deployed updates on subsequent visits to a page, after all the\r\n// existing tabs open on the page have been closed, since previously cached\r\n// resources are updated in the background.\r\n\r\n// To learn more about the benefits of this model and instructions on how to\r\n// opt-in, read https://bit.ly/CRA-PWA\r\n\r\nconst isLocalhost = Boolean(\r\n window.location.hostname === 'localhost' ||\r\n // [::1] is the IPv6 localhost address.\r\n window.location.hostname === '[::1]' ||\r\n // 127.0.0.0/8 are considered localhost for IPv4.\r\n window.location.hostname.match(\r\n /^127(?:\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/\r\n )\r\n);\r\n\r\ntype Config = {\r\n onSuccess?: (registration: ServiceWorkerRegistration) => void;\r\n onUpdate?: (registration: ServiceWorkerRegistration) => void;\r\n};\r\n\r\nexport function register(config?: Config) {\r\n if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {\r\n // The URL constructor is available in all browsers that support SW.\r\n const publicUrl = new URL(\r\n process.env.PUBLIC_URL,\r\n window.location.href\r\n );\r\n if (publicUrl.origin !== window.location.origin) {\r\n // Our service worker won't work if PUBLIC_URL is on a different origin\r\n // from what our page is served on. This might happen if a CDN is used to\r\n // serve assets; see https://github.com/facebook/create-react-app/issues/2374\r\n return;\r\n }\r\n\r\n window.addEventListener('load',\r\n () => {\r\n const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;\r\n\r\n if (isLocalhost) {\r\n // This is running on localhost. Let's check if a service worker still exists or not.\r\n checkValidServiceWorker(swUrl, config);\r\n\r\n // Add some additional logging to localhost, pointing developers to the\r\n // service worker/PWA documentation.\r\n navigator.serviceWorker.ready.then(() => {\r\n console.log(\r\n 'This web app is being served cache-first by a service ' +\r\n 'worker. To learn more, visit https://bit.ly/CRA-PWA'\r\n );\r\n });\r\n } else {\r\n // Is not localhost. Just register service worker\r\n registerValidSW(swUrl, config);\r\n }\r\n });\r\n }\r\n}\r\n\r\nfunction registerValidSW(swUrl: string, config?: Config) {\r\n navigator.serviceWorker\r\n .register(swUrl)\r\n .then(registration => {\r\n registration.onupdatefound = () => {\r\n const installingWorker = registration.installing;\r\n if (installingWorker == null) {\r\n return;\r\n }\r\n installingWorker.onstatechange = () => {\r\n if (installingWorker.state === 'installed') {\r\n if (navigator.serviceWorker.controller) {\r\n // At this point, the updated precached content has been fetched,\r\n // but the previous service worker will still serve the older\r\n // content until all client tabs are closed.\r\n console.log(\r\n 'New content is available and will be used when all ' +\r\n 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'\r\n );\r\n\r\n // Execute callback\r\n if (config && config.onUpdate) {\r\n config.onUpdate(registration);\r\n }\r\n } else {\r\n // At this point, everything has been precached.\r\n // It's the perfect time to display a\r\n // \"Content is cached for offline use.\" message.\r\n console.log('Content is cached for offline use.');\r\n\r\n // Execute callback\r\n if (config && config.onSuccess) {\r\n config.onSuccess(registration);\r\n }\r\n }\r\n }\r\n };\r\n };\r\n })\r\n .catch(error => {\r\n console.error('Error during service worker registration:', error);\r\n });\r\n}\r\n\r\nfunction checkValidServiceWorker(swUrl: string, config?: Config) {\r\n // Check if the service worker can be found. If it can't reload the page.\r\n fetch(swUrl,\r\n {\r\n headers: { 'Service-Worker': 'script' }\r\n })\r\n .then(response => {\r\n // Ensure service worker exists, and that we really are getting a JS file.\r\n const contentType = response.headers.get('content-type');\r\n if (\r\n response.status === 404 ||\r\n (contentType != null && contentType.indexOf('javascript') === -1)\r\n ) {\r\n // No service worker found. Probably a different app. Reload the page.\r\n navigator.serviceWorker.ready.then(registration => {\r\n registration.unregister().then(() => {\r\n window.location.reload();\r\n });\r\n });\r\n } else {\r\n // Service worker found. Proceed as normal.\r\n registerValidSW(swUrl, config);\r\n }\r\n })\r\n .catch(() => {\r\n console.log(\r\n 'No internet connection found. App is running in offline mode.'\r\n );\r\n });\r\n}\r\n\r\nexport function unregister() {\r\n if ('serviceWorker' in navigator) {\r\n navigator.serviceWorker.ready\r\n .then(registration => {\r\n registration.unregister();\r\n })\r\n .catch(error => {\r\n console.error(error.message);\r\n });\r\n }\r\n}","import React from 'react';\r\nimport ReactDOM from 'react-dom';\r\nimport App from './components/App';\r\nimport * as serviceWorker from './serviceWorker';\r\nimport './styles/styles.scss';\r\n\r\nReactDOM.render(\r\n <React.StrictMode>\r\n <App/>\r\n </React.StrictMode>,\r\n document.getElementById('root')\r\n);\r\n\r\n// If you want your app to work offline and load faster, you can change\r\n// unregister() to register() below. Note this comes with some pitfalls.\r\n// Learn more about service workers: https://bit.ly/CRA-PWA\r\nserviceWorker.unregister();"],"sourceRoot":""}