forked from MapComplete/MapComplete
		
	Feature: add "recently visited themes" on index page
This commit is contained in:
		
							parent
							
								
									1723f268c0
								
							
						
					
					
						commit
						4db2c86c0f
					
				
					 5 changed files with 47 additions and 21 deletions
				
			
		| 
						 | 
					@ -616,6 +616,7 @@
 | 
				
			||||||
        "intro": "Maps about various topics which you can contribute to",
 | 
					        "intro": "Maps about various topics which you can contribute to",
 | 
				
			||||||
        "learnMore": "Learn more",
 | 
					        "learnMore": "Learn more",
 | 
				
			||||||
        "logIn": "Log in to see other themes you previously visited",
 | 
					        "logIn": "Log in to see other themes you previously visited",
 | 
				
			||||||
 | 
					        "recentThemes": "Recently visited themes",
 | 
				
			||||||
        "title": "MapComplete"
 | 
					        "title": "MapComplete"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "inspector": {
 | 
					    "inspector": {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1140,10 +1140,6 @@ input[type="range"].range-lg::-moz-range-thumb {
 | 
				
			||||||
  position: absolute;
 | 
					  position: absolute;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.\!relative {
 | 
					 | 
				
			||||||
  position: relative !important;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.relative {
 | 
					.relative {
 | 
				
			||||||
  position: relative;
 | 
					  position: relative;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -2565,6 +2561,10 @@ input[type="range"].range-lg::-moz-range-thumb {
 | 
				
			||||||
  grid-template-columns: repeat(7, minmax(0, 1fr));
 | 
					  grid-template-columns: repeat(7, minmax(0, 1fr));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.grid-rows-2 {
 | 
				
			||||||
 | 
					  grid-template-rows: repeat(2, minmax(0, 1fr));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.flex-row {
 | 
					.flex-row {
 | 
				
			||||||
  flex-direction: row;
 | 
					  flex-direction: row;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -8729,6 +8729,10 @@ svg.apply-fill path {
 | 
				
			||||||
    grid-template-columns: repeat(3, minmax(0, 1fr));
 | 
					    grid-template-columns: repeat(3, minmax(0, 1fr));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .md\:grid-rows-1 {
 | 
				
			||||||
 | 
					    grid-template-rows: repeat(1, minmax(0, 1fr));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  .md\:flex-row {
 | 
					  .md\:flex-row {
 | 
				
			||||||
    flex-direction: row;
 | 
					    flex-direction: row;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,6 +27,7 @@
 | 
				
			||||||
  import { AndroidPolyfill } from "../Logic/Web/AndroidPolyfill"
 | 
					  import { AndroidPolyfill } from "../Logic/Web/AndroidPolyfill"
 | 
				
			||||||
  import Forgejo from "../assets/svg/Forgejo.svelte"
 | 
					  import Forgejo from "../assets/svg/Forgejo.svelte"
 | 
				
			||||||
  import Locale from "./i18n/Locale"
 | 
					  import Locale from "./i18n/Locale"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  AndroidPolyfill.init().then(() => console.log("Android polyfill setup completed"))
 | 
					  AndroidPolyfill.init().then(() => console.log("Android polyfill setup completed"))
 | 
				
			||||||
  const featureSwitches = new OsmConnectionFeatureSwitches()
 | 
					  const featureSwitches = new OsmConnectionFeatureSwitches()
 | 
				
			||||||
  const osmConnection = new OsmConnection({
 | 
					  const osmConnection = new OsmConnection({
 | 
				
			||||||
| 
						 | 
					@ -35,13 +36,15 @@
 | 
				
			||||||
      "oauth_token",
 | 
					      "oauth_token",
 | 
				
			||||||
      undefined,
 | 
					      undefined,
 | 
				
			||||||
      "Used to complete the login"
 | 
					      "Used to complete the login"
 | 
				
			||||||
    ),
 | 
					    )
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
  const state = new UserRelatedState(osmConnection)
 | 
					  const state = new UserRelatedState(osmConnection)
 | 
				
			||||||
  const t = Translations.t.index
 | 
					  const t = Translations.t.index
 | 
				
			||||||
  const tu = Translations.t.general
 | 
					  const tu = Translations.t.general
 | 
				
			||||||
  const tr = Translations.t.general.morescreen
 | 
					  const tr = Translations.t.general.morescreen
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const recentThemes = state.recentlyVisitedThemes.value.mapD(themes => themes.map(thId => ThemeSearch.officialThemesById.get(thId)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let userLanguages = osmConnection.userDetails.map((ud) => ud?.languages ?? [])
 | 
					  let userLanguages = osmConnection.userDetails.map((ud) => ud?.languages ?? [])
 | 
				
			||||||
  let search: UIEventSource<string | undefined> = new UIEventSource<string>("")
 | 
					  let search: UIEventSource<string | undefined> = new UIEventSource<string>("")
 | 
				
			||||||
  let searchStable = search.stabilized(100)
 | 
					  let searchStable = search.stabilized(100)
 | 
				
			||||||
| 
						 | 
					@ -64,6 +67,7 @@
 | 
				
			||||||
  const customThemes: Store<MinimalThemeInformation[]> = Stores.ListStabilized<string>(
 | 
					  const customThemes: Store<MinimalThemeInformation[]> = Stores.ListStabilized<string>(
 | 
				
			||||||
    state.installedUserThemes
 | 
					    state.installedUserThemes
 | 
				
			||||||
  ).mapD((stableIds) => Utils.NoNullInplace(stableIds.map((id) => state.getUnofficialTheme(id))))
 | 
					  ).mapD((stableIds) => Utils.NoNullInplace(stableIds.map((id) => state.getUnofficialTheme(id))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  function filtered(themes: Store<MinimalThemeInformation[]>): Store<MinimalThemeInformation[]> {
 | 
					  function filtered(themes: Store<MinimalThemeInformation[]>): Store<MinimalThemeInformation[]> {
 | 
				
			||||||
    const searchIndex = Locale.language.map(language => {
 | 
					    const searchIndex = Locale.language.map(language => {
 | 
				
			||||||
      return new ThemeSearchIndex(language, themes.data)
 | 
					      return new ThemeSearchIndex(language, themes.data)
 | 
				
			||||||
| 
						 | 
					@ -162,6 +166,17 @@
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <LoginToggle {state}>
 | 
				
			||||||
 | 
					      {#if $recentThemes.length > 2}
 | 
				
			||||||
 | 
					        <div class="my-4">
 | 
				
			||||||
 | 
					          <h2>
 | 
				
			||||||
 | 
					            <Tr t={Translations.t.index.recentThemes} />
 | 
				
			||||||
 | 
					          </h2>
 | 
				
			||||||
 | 
					          <ThemesList {state} themes={$recentThemes} onlyIcons />
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      {/if}
 | 
				
			||||||
 | 
					    </LoginToggle>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <Searchbar
 | 
					    <Searchbar
 | 
				
			||||||
      value={search}
 | 
					      value={search}
 | 
				
			||||||
      placeholder={tr.searchForATheme}
 | 
					      placeholder={tr.searchForATheme}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,4 @@
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
  import { ImmutableStore, Store } from "../../Logic/UIEventSource"
 | 
					 | 
				
			||||||
  import { OsmConnection } from "../../Logic/Osm/OsmConnection"
 | 
					  import { OsmConnection } from "../../Logic/Osm/OsmConnection"
 | 
				
			||||||
  import type { MinimalThemeInformation } from "../../Models/ThemeConfig/ThemeConfig"
 | 
					  import type { MinimalThemeInformation } from "../../Models/ThemeConfig/ThemeConfig"
 | 
				
			||||||
  import Tr from "../Base/Tr.svelte"
 | 
					  import Tr from "../Base/Tr.svelte"
 | 
				
			||||||
| 
						 | 
					@ -10,7 +9,7 @@
 | 
				
			||||||
  export let theme: MinimalThemeInformation & { isOfficial?: boolean }
 | 
					  export let theme: MinimalThemeInformation & { isOfficial?: boolean }
 | 
				
			||||||
  let isCustom: boolean = theme.id.startsWith("https://") || theme.id.startsWith("http://")
 | 
					  let isCustom: boolean = theme.id.startsWith("https://") || theme.id.startsWith("http://")
 | 
				
			||||||
  export let state: { layoutToUse?: { id: string }; osmConnection: OsmConnection }
 | 
					  export let state: { layoutToUse?: { id: string }; osmConnection: OsmConnection }
 | 
				
			||||||
 | 
					  export let iconOnly: boolean = false
 | 
				
			||||||
  $: title = Translations.T(
 | 
					  $: title = Translations.T(
 | 
				
			||||||
    theme.title,
 | 
					    theme.title,
 | 
				
			||||||
    !isCustom && !theme.mustHaveLanguage ? "themes:" + theme.id + ".title" : undefined
 | 
					    !isCustom && !theme.mustHaveLanguage ? "themes:" + theme.id + ".title" : undefined
 | 
				
			||||||
| 
						 | 
					@ -71,12 +70,17 @@
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{#if iconOnly}
 | 
				
			||||||
 | 
					  <a class="low-interaction my-1 rounded p-1" href={$href}>
 | 
				
			||||||
 | 
					    <Marker icons={theme.icon} size="w-8 h-8 sm:w-11 sm:h-11" />
 | 
				
			||||||
 | 
					  </a>
 | 
				
			||||||
 | 
					{:else}
 | 
				
			||||||
  <a class="low-interaction my-1 flex w-full items-center text-ellipsis rounded p-1" href={$href}>
 | 
					  <a class="low-interaction my-1 flex w-full items-center text-ellipsis rounded p-1" href={$href}>
 | 
				
			||||||
    <Marker icons={theme.icon} size="block h-8 w-8 sm:h-11 sm:w-11 m-1 sm:mx-2 md:mx-4 shrink-0" />
 | 
					    <Marker icons={theme.icon} size="block h-8 w-8 sm:h-11 sm:w-11 m-1 sm:mx-2 md:mx-4 shrink-0" />
 | 
				
			||||||
 | 
					 | 
				
			||||||
    <span class="flex flex-col overflow-hidden text-ellipsis text-xl font-bold">
 | 
					    <span class="flex flex-col overflow-hidden text-ellipsis text-xl font-bold">
 | 
				
			||||||
      <Tr cls="" t={title} />
 | 
					      <Tr cls="" t={title} />
 | 
				
			||||||
      <Tr cls="subtle text-base" t={description} />
 | 
					      <Tr cls="subtle text-base" t={description} />
 | 
				
			||||||
      <slot />
 | 
					      <slot />
 | 
				
			||||||
    </span>
 | 
					    </span>
 | 
				
			||||||
  </a>
 | 
					  </a>
 | 
				
			||||||
 | 
					{/if}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,19 +7,21 @@
 | 
				
			||||||
  import { MinimalThemeInformation } from "../../Models/ThemeConfig/ThemeConfig"
 | 
					  import { MinimalThemeInformation } from "../../Models/ThemeConfig/ThemeConfig"
 | 
				
			||||||
  import Translations from "../i18n/Translations"
 | 
					  import Translations from "../i18n/Translations"
 | 
				
			||||||
  import Tr from "../Base/Tr.svelte"
 | 
					  import Tr from "../Base/Tr.svelte"
 | 
				
			||||||
 | 
					  import { twMerge } from "tailwind-merge"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  export let search: UIEventSource<string>
 | 
					  export let search: UIEventSource<string> = new UIEventSource<string>(undefined)
 | 
				
			||||||
  export let themes: MinimalThemeInformation[]
 | 
					  export let themes: MinimalThemeInformation[]
 | 
				
			||||||
  export let state: { osmConnection: OsmConnection }
 | 
					  export let state: { osmConnection: OsmConnection }
 | 
				
			||||||
 | 
					  export let onlyIcons: boolean = false
 | 
				
			||||||
  export let hasSelection: boolean = true
 | 
					  export let hasSelection: boolean = true
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<section class="w-full">
 | 
					<section class="w-full">
 | 
				
			||||||
  <slot name="title" />
 | 
					  <slot name="title" />
 | 
				
			||||||
  <div class="theme-list my-2 gap-4 md:grid md:grid-flow-row md:grid-cols-2 lg:grid-cols-3">
 | 
					  <div
 | 
				
			||||||
 | 
					    class={onlyIcons ? "flex gap-x-2 flex-wrap items-center justify-center" : ("theme-list my-2 gap-4 md:grid md:grid-flow-row md:grid-cols-2 lg:grid-cols-3")}>
 | 
				
			||||||
    {#each themes as theme (theme.id)}
 | 
					    {#each themes as theme (theme.id)}
 | 
				
			||||||
      <ThemeButton {theme} {state}>
 | 
					      <ThemeButton {theme} {state} iconOnly={onlyIcons}>
 | 
				
			||||||
        {#if $search && hasSelection && themes?.[0] === theme}
 | 
					        {#if $search && hasSelection && themes?.[0] === theme}
 | 
				
			||||||
          <span class="thanks hidden-on-mobile" aria-hidden="true">
 | 
					          <span class="thanks hidden-on-mobile" aria-hidden="true">
 | 
				
			||||||
            <Tr t={Translations.t.general.morescreen.enterToOpen} />
 | 
					            <Tr t={Translations.t.general.morescreen.enterToOpen} />
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue