Studio: Add deeplinks, fix #1926
This commit is contained in:
parent
204027b4e9
commit
504cc1fe33
5 changed files with 82 additions and 40 deletions
|
@ -20,12 +20,16 @@
|
||||||
import NextButton from "../Base/NextButton.svelte"
|
import NextButton from "../Base/NextButton.svelte"
|
||||||
import BackButton from "../Base/BackButton.svelte"
|
import BackButton from "../Base/BackButton.svelte"
|
||||||
import DeleteButton from "./DeleteButton.svelte"
|
import DeleteButton from "./DeleteButton.svelte"
|
||||||
|
import StudioHashSetter from "./StudioHashSetter"
|
||||||
|
|
||||||
const layerSchema: ConfigMeta[] = <any>layerSchemaRaw
|
const layerSchema: ConfigMeta[] = <any>layerSchemaRaw
|
||||||
|
|
||||||
export let state: EditLayerState
|
export let state: EditLayerState
|
||||||
|
|
||||||
export let backToStudio: () => void
|
export let backToStudio: () => void
|
||||||
|
|
||||||
|
new StudioHashSetter("layer", state.selectedTab, state.getStoreFor(["id"]))
|
||||||
|
|
||||||
let messages = state.messages
|
let messages = state.messages
|
||||||
let hasErrors = messages.mapD(
|
let hasErrors = messages.mapD(
|
||||||
(m: ConversionMessage[]) => m.filter((m) => m.level === "error").length
|
(m: ConversionMessage[]) => m.filter((m) => m.level === "error").length
|
||||||
|
@ -127,7 +131,7 @@
|
||||||
{/each}
|
{/each}
|
||||||
{:else}
|
{:else}
|
||||||
<div class="m4 h-full overflow-y-auto">
|
<div class="m4 h-full overflow-y-auto">
|
||||||
<TabbedGroup>
|
<TabbedGroup tab={state.selectedTab}>
|
||||||
<div slot="title0" class="flex">
|
<div slot="title0" class="flex">
|
||||||
General properties
|
General properties
|
||||||
<ErrorIndicatorForRegion firstPaths={firstPathsFor("Basic")} {state} />
|
<ErrorIndicatorForRegion firstPaths={firstPathsFor("Basic")} {state} />
|
||||||
|
|
|
@ -42,6 +42,11 @@ export abstract class EditJsonState<T> {
|
||||||
public readonly configuration: UIEventSource<Partial<T>> = new UIEventSource<Partial<T>>({})
|
public readonly configuration: UIEventSource<Partial<T>> = new UIEventSource<Partial<T>>({})
|
||||||
public readonly messages: Store<ConversionMessage[]>
|
public readonly messages: Store<ConversionMessage[]>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The tab in the UI that is selected, used for deeplinks
|
||||||
|
*/
|
||||||
|
public readonly selectedTab: UIEventSource<number> = new UIEventSource<number>(0)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The EditLayerUI shows a 'schemaBasedInput' for this path to pop advanced questions out
|
* The EditLayerUI shows a 'schemaBasedInput' for this path to pop advanced questions out
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -9,12 +9,15 @@
|
||||||
import RawEditor from "./RawEditor.svelte"
|
import RawEditor from "./RawEditor.svelte"
|
||||||
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
|
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
|
||||||
import DeleteButton from "./DeleteButton.svelte"
|
import DeleteButton from "./DeleteButton.svelte"
|
||||||
|
import { UIEventSource } from "../../Logic/UIEventSource"
|
||||||
|
import StudioHashSetter from "./StudioHashSetter"
|
||||||
|
|
||||||
export let state: EditThemeState
|
export let state: EditThemeState
|
||||||
export let osmConnection: OsmConnection
|
export let osmConnection: OsmConnection
|
||||||
export let backToStudio: () => void
|
export let backToStudio: () => void
|
||||||
|
|
||||||
let schema: ConfigMeta[] = state.schema.filter((schema) => schema.path.length > 0)
|
let schema: ConfigMeta[] = state.schema.filter((schema) => schema.path.length > 0)
|
||||||
|
new StudioHashSetter("theme", state.selectedTab, state.getStoreFor(["id"]))
|
||||||
|
|
||||||
export let selfLayers: { owner: number; id: string }[]
|
export let selfLayers: { owner: number; id: string }[]
|
||||||
export let otherLayers: { owner: number; id: string }[]
|
export let otherLayers: { owner: number; id: string }[]
|
||||||
|
@ -94,7 +97,7 @@
|
||||||
|
|
||||||
<div class="m4 h-full overflow-y-auto">
|
<div class="m4 h-full overflow-y-auto">
|
||||||
<!-- {Object.keys(perRegion).join(";")} -->
|
<!-- {Object.keys(perRegion).join(";")} -->
|
||||||
<TabbedGroup>
|
<TabbedGroup tab={state.selectedTab}>
|
||||||
<div slot="title0">Basic properties</div>
|
<div slot="title0">Basic properties</div>
|
||||||
<div slot="content0" class="mb-8">
|
<div slot="content0" class="mb-8">
|
||||||
<Region configs={perRegion["basic"]} path={[]} {state} title="Basic properties" />
|
<Region configs={perRegion["basic"]} path={[]} {state} title="Basic properties" />
|
||||||
|
|
11
src/UI/Studio/StudioHashSetter.ts
Normal file
11
src/UI/Studio/StudioHashSetter.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||||
|
import Hash from "../../Logic/Web/Hash"
|
||||||
|
|
||||||
|
export default class StudioHashSetter {
|
||||||
|
constructor(mode: "layer" | "theme", tab: Store<number>, name: Store<string>) {
|
||||||
|
tab.mapD(tab => {
|
||||||
|
Hash.hash.setData(mode + "/" + name.data + "/" + tab)
|
||||||
|
}
|
||||||
|
, [name])
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import NextButton from "./Base/NextButton.svelte"
|
import NextButton from "./Base/NextButton.svelte"
|
||||||
import { Store, UIEventSource } from "../Logic/UIEventSource"
|
import { Store, UIEventSource } from "../Logic/UIEventSource"
|
||||||
import EditLayerState, { EditThemeState } from "./Studio/EditLayerState"
|
import EditLayerState, { EditJsonState, EditThemeState } from "./Studio/EditLayerState"
|
||||||
import EditLayer from "./Studio/EditLayer.svelte"
|
import EditLayer from "./Studio/EditLayer.svelte"
|
||||||
import Loading from "../assets/svg/Loading.svelte"
|
import Loading from "../assets/svg/Loading.svelte"
|
||||||
import StudioServer from "./Studio/StudioServer"
|
import StudioServer from "./Studio/StudioServer"
|
||||||
|
@ -30,6 +30,7 @@
|
||||||
import Tr from "./Base/Tr.svelte"
|
import Tr from "./Base/Tr.svelte"
|
||||||
import Add from "../assets/svg/Add.svelte"
|
import Add from "../assets/svg/Add.svelte"
|
||||||
import { SearchIcon } from "@rgossiaux/svelte-heroicons/solid"
|
import { SearchIcon } from "@rgossiaux/svelte-heroicons/solid"
|
||||||
|
import Hash from "../Logic/Web/Hash"
|
||||||
|
|
||||||
export let studioUrl =
|
export let studioUrl =
|
||||||
window.location.hostname === "127.0.0.2"
|
window.location.hostname === "127.0.0.2"
|
||||||
|
@ -43,11 +44,11 @@
|
||||||
)
|
)
|
||||||
let osmConnection = new OsmConnection({
|
let osmConnection = new OsmConnection({
|
||||||
oauth_token,
|
oauth_token,
|
||||||
checkOnlineRegularly: true,
|
checkOnlineRegularly: true
|
||||||
})
|
})
|
||||||
const expertMode = UIEventSource.asBoolean(
|
const expertMode = UIEventSource.asBoolean(
|
||||||
osmConnection.GetPreference("studio-expert-mode", "false", {
|
osmConnection.GetPreference("studio-expert-mode", "false", {
|
||||||
documentation: "Indicates if more options are shown in mapcomplete studio",
|
documentation: "Indicates if more options are shown in mapcomplete studio"
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
expertMode.addCallbackAndRunD((expert) => console.log("Expert mode is", expert))
|
expertMode.addCallbackAndRunD((expert) => console.log("Expert mode is", expert))
|
||||||
|
@ -61,12 +62,12 @@
|
||||||
l["success"]?.filter((l) => l.category === "layers")
|
l["success"]?.filter((l) => l.category === "layers")
|
||||||
)
|
)
|
||||||
$: selfLayers = layers.mapD(
|
$: selfLayers = layers.mapD(
|
||||||
(ls) =>
|
(ls) =>
|
||||||
ls.filter(
|
ls.filter(
|
||||||
(l) => l.owner === uid.data && l.id.toLowerCase().includes(layerFilterTerm.toLowerCase())
|
(l) => l.owner === uid.data && l.id.toLowerCase().includes(layerFilterTerm.toLowerCase())
|
||||||
),
|
),
|
||||||
[uid]
|
[uid]
|
||||||
)
|
)
|
||||||
$: otherLayers = layers.mapD(
|
$: otherLayers = layers.mapD(
|
||||||
(ls) =>
|
(ls) =>
|
||||||
ls.filter(
|
ls.filter(
|
||||||
|
@ -132,16 +133,17 @@
|
||||||
|
|
||||||
const version = meta.version
|
const version = meta.version
|
||||||
|
|
||||||
async function editLayer(event: Event) {
|
async function editLayer(event: { detail }): Promise<EditLayerState> {
|
||||||
const layerId: { owner: number; id: string } = event["detail"]
|
const layerId: { owner: number; id: string } = event["detail"]
|
||||||
state = "loading"
|
state = "loading"
|
||||||
editLayerState.startSavingUpdates(false)
|
editLayerState.startSavingUpdates(false)
|
||||||
editLayerState.configuration.setData(await studio.fetch(layerId.id, "layers", layerId.owner))
|
editLayerState.configuration.setData(await studio.fetch(layerId.id, "layers", layerId.owner))
|
||||||
editLayerState.startSavingUpdates()
|
editLayerState.startSavingUpdates()
|
||||||
state = "editing_layer"
|
state = "editing_layer"
|
||||||
|
return editLayerState
|
||||||
}
|
}
|
||||||
|
|
||||||
async function editTheme(event: Event) {
|
async function editTheme(event: { detail }): Promise<EditThemeState> {
|
||||||
const id: { id: string; owner: number } = event["detail"]
|
const id: { id: string; owner: number } = event["detail"]
|
||||||
state = "loading"
|
state = "loading"
|
||||||
editThemeState.startSavingUpdates(false)
|
editThemeState.startSavingUpdates(false)
|
||||||
|
@ -149,6 +151,7 @@
|
||||||
editThemeState.configuration.setData(layout)
|
editThemeState.configuration.setData(layout)
|
||||||
editThemeState.startSavingUpdates()
|
editThemeState.startSavingUpdates()
|
||||||
state = "editing_theme"
|
state = "editing_theme"
|
||||||
|
return editThemeState
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createNewLayer() {
|
async function createNewLayer() {
|
||||||
|
@ -162,23 +165,50 @@
|
||||||
marker: [
|
marker: [
|
||||||
{
|
{
|
||||||
icon: "circle",
|
icon: "circle",
|
||||||
color: "white",
|
color: "white"
|
||||||
},
|
}
|
||||||
],
|
]
|
||||||
},
|
}
|
||||||
],
|
],
|
||||||
tagRenderings: ["images"],
|
tagRenderings: ["images"],
|
||||||
lineRendering: [
|
lineRendering: [
|
||||||
{
|
{
|
||||||
width: 1,
|
width: 1,
|
||||||
color: "blue",
|
color: "blue"
|
||||||
},
|
}
|
||||||
],
|
]
|
||||||
}
|
}
|
||||||
editLayerState.configuration.setData(initialLayerConfig)
|
editLayerState.configuration.setData(initialLayerConfig)
|
||||||
editLayerState.startSavingUpdates()
|
editLayerState.startSavingUpdates()
|
||||||
state = "editing_layer"
|
state = "editing_layer"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function selectStateBasedOnHash() {
|
||||||
|
const hash = Hash.hash.data
|
||||||
|
if (!hash) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.log("Selecting state based on ", hash)
|
||||||
|
const [mode, id, tab] = hash.split("/")
|
||||||
|
// Not really an event, we just set the 'detail'
|
||||||
|
const event = {
|
||||||
|
detail: {
|
||||||
|
id,
|
||||||
|
owner: uid.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const statePromise: Promise<EditJsonState<any>> = mode === "layer" ? editLayer(event) : editTheme(event)
|
||||||
|
const state = await statePromise
|
||||||
|
state.selectedTab.setData(Number(tab))
|
||||||
|
}
|
||||||
|
|
||||||
|
selectStateBasedOnHash()
|
||||||
|
|
||||||
|
function backToStudio() {
|
||||||
|
console.log("Back to studio")
|
||||||
|
state = undefined
|
||||||
|
Hash.hash.setData(undefined)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<If condition={layersWithErr.map((d) => d?.["error"] !== undefined)}>
|
<If condition={layersWithErr.map((d) => d?.["error"] !== undefined)}>
|
||||||
|
@ -191,8 +221,8 @@
|
||||||
<li>Try again in a few minutes</li>
|
<li>Try again in a few minutes</li>
|
||||||
<li>
|
<li>
|
||||||
Contact <a href="https://app.element.io/#/room/#MapComplete:matrix.org">
|
Contact <a href="https://app.element.io/#/room/#MapComplete:matrix.org">
|
||||||
the MapComplete community via the chat.
|
the MapComplete community via the chat.
|
||||||
</a>
|
</a>
|
||||||
Someone might be able to help you
|
Someone might be able to help you
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
@ -257,9 +287,7 @@
|
||||||
<BackButton
|
<BackButton
|
||||||
clss="small p-1"
|
clss="small p-1"
|
||||||
imageClass="w-8 h-8"
|
imageClass="w-8 h-8"
|
||||||
on:click={() => {
|
on:click={() => backToStudio()}
|
||||||
state = undefined
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
MapComplete Studio
|
MapComplete Studio
|
||||||
</BackButton>
|
</BackButton>
|
||||||
|
@ -306,9 +334,7 @@
|
||||||
<BackButton
|
<BackButton
|
||||||
clss="small p-1"
|
clss="small p-1"
|
||||||
imageClass="w-8 h-8"
|
imageClass="w-8 h-8"
|
||||||
on:click={() => {
|
on:click={() => backToStudio()}
|
||||||
state = undefined
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
MapComplete Studio
|
MapComplete Studio
|
||||||
</BackButton>
|
</BackButton>
|
||||||
|
@ -348,30 +374,23 @@
|
||||||
{:else if state === "editing_layer"}
|
{:else if state === "editing_layer"}
|
||||||
<EditLayer
|
<EditLayer
|
||||||
state={editLayerState}
|
state={editLayerState}
|
||||||
backToStudio={() => {
|
{backToStudio}
|
||||||
state = undefined
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<BackButton
|
<BackButton
|
||||||
clss="small p-1"
|
clss="small p-1"
|
||||||
imageClass="w-8 h-8"
|
imageClass="w-8 h-8"
|
||||||
on:click={() => {
|
on:click={() => backToStudio()}
|
||||||
state = undefined
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
MapComplete Studio
|
MapComplete Studio
|
||||||
</BackButton>
|
</BackButton>
|
||||||
</EditLayer>
|
</EditLayer>
|
||||||
{:else if state === "editing_theme"}
|
{:else if state === "editing_theme"}
|
||||||
<EditTheme state={editThemeState} selfLayers={$selfLayers} otherLayers={$otherLayers} {osmConnection} backToStudio={() => {
|
<EditTheme state={editThemeState} selfLayers={$selfLayers} otherLayers={$otherLayers} {osmConnection}
|
||||||
state = undefined
|
{backToStudio}>
|
||||||
}}>
|
|
||||||
<BackButton
|
<BackButton
|
||||||
clss="small p-1"
|
clss="small p-1"
|
||||||
imageClass="w-8 h-8"
|
imageClass="w-8 h-8"
|
||||||
on:click={() => {
|
on:click={() => backToStudio()}
|
||||||
state = undefined
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
MapComplete Studio
|
MapComplete Studio
|
||||||
</BackButton>
|
</BackButton>
|
||||||
|
|
Loading…
Reference in a new issue