feat: added change log prompt for PWA updates (#4098)
Co-authored-by: jamesgeorge007 <25279263+jamesgeorge007@users.noreply.github.com> Co-authored-by: nivedin <nivedinp@gmail.com> Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
This commit is contained in:
@@ -98,7 +98,9 @@
|
||||
"twitter": "Twitter",
|
||||
"type_a_command_search": "Type a command or search…",
|
||||
"we_use_cookies": "We use cookies",
|
||||
"updated_text": "Hoppscotch has been updated to the version {version} 🎉",
|
||||
"whats_new": "What's new?",
|
||||
"see_whats_new": "See what’s new",
|
||||
"wiki": "Wiki"
|
||||
},
|
||||
"auth": {
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
"@hoppscotch/codemirror-lang-graphql": "workspace:^",
|
||||
"@hoppscotch/data": "workspace:^",
|
||||
"@hoppscotch/js-sandbox": "workspace:^",
|
||||
"@hoppscotch/ui": "0.1.0",
|
||||
"@hoppscotch/ui": "0.2.0",
|
||||
"@hoppscotch/vue-toasted": "0.1.0",
|
||||
"@lezer/highlight": "1.2.0",
|
||||
"@unhead/vue": "1.8.8",
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
</div>
|
||||
<ErrorPage v-if="errorInfo !== null" :error="errorInfo" />
|
||||
<RouterView v-else />
|
||||
<Toaster rich-colors />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -19,6 +20,7 @@ import { isLoadingInitialRoute } from "@modules/router"
|
||||
import { useI18n } from "@composables/i18n"
|
||||
import { APP_IS_IN_DEV_MODE } from "@helpers/dev"
|
||||
import { platform } from "./platform"
|
||||
import { Toaster } from "@hoppscotch/ui"
|
||||
|
||||
const t = useI18n()
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ declare module 'vue' {
|
||||
AppLogo: typeof import('./components/app/Logo.vue')['default']
|
||||
AppOptions: typeof import('./components/app/Options.vue')['default']
|
||||
AppPaneLayout: typeof import('./components/app/PaneLayout.vue')['default']
|
||||
AppPWAPrompt: typeof import('./components/app/PWAPrompt.vue')['default']
|
||||
AppShare: typeof import('./components/app/Share.vue')['default']
|
||||
AppShortcuts: typeof import('./components/app/Shortcuts.vue')['default']
|
||||
AppShortcutsEntry: typeof import('./components/app/ShortcutsEntry.vue')['default']
|
||||
@@ -38,6 +39,7 @@ declare module 'vue' {
|
||||
AppSpotlightEntryRESTTeamRequestEntry: typeof import('./components/app/spotlight/entry/RESTTeamRequestEntry.vue')['default']
|
||||
AppSpotlightSearch: typeof import('./components/app/SpotlightSearch.vue')['default']
|
||||
AppSupport: typeof import('./components/app/Support.vue')['default']
|
||||
AppWhatsNewDialog: typeof import('./components/app/WhatsNewDialog.vue')['default']
|
||||
Collections: typeof import('./components/collections/index.vue')['default']
|
||||
CollectionsAdd: typeof import('./components/collections/Add.vue')['default']
|
||||
CollectionsAddFolder: typeof import('./components/collections/AddFolder.vue')['default']
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<div
|
||||
class="flex flex-col py-2 px-4 w-72 relative border border-[#BCB78B] bg-[#FEFFD2] rounded-md text-[#7E7103]"
|
||||
>
|
||||
<button
|
||||
class="absolute top-2 right-2 hover:text-black"
|
||||
@click="$emit('close-toast')"
|
||||
>
|
||||
<IconLucideX />
|
||||
</button>
|
||||
<div class="flex flex-col space-y-3">
|
||||
<p class="leading-5 font-semibold">
|
||||
{{ t("app.updated_text", { version: version }) }}
|
||||
</p>
|
||||
<button
|
||||
class="flex items-center space-x-1 hover:underline"
|
||||
@click="openWhatsNew"
|
||||
>
|
||||
<span>
|
||||
{{ t("app.see_whats_new") }}
|
||||
</span>
|
||||
<IconLucideArrowUpRight />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from "~/composables/i18n"
|
||||
import { platform } from "~/platform"
|
||||
|
||||
const t = useI18n()
|
||||
|
||||
const props = defineProps<{
|
||||
notesUrl: string
|
||||
version: string
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
(e: "close-toast"): void
|
||||
}>()
|
||||
|
||||
const openWhatsNew = () => {
|
||||
if (props.notesUrl) platform.io.openExternalLink(props.notesUrl)
|
||||
}
|
||||
</script>
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="flex flex-col">
|
||||
<label for="requestType" class="px-4 pb-4" v-if="!hideLabel">
|
||||
<label v-if="!hideLabel" for="requestType" class="px-4 pb-4">
|
||||
{{ t("request.choose_language") }}
|
||||
</label>
|
||||
<tippy
|
||||
|
||||
@@ -46,9 +46,9 @@
|
||||
{{ t("tab.code_snippet") }}
|
||||
</div>
|
||||
<HttpCodegen
|
||||
v-if="selectedNavigationTab === 'codegen'"
|
||||
:hide-label="true"
|
||||
class="px-4 mt-4"
|
||||
v-if="selectedNavigationTab === 'codegen'"
|
||||
/>
|
||||
</HoppSmartTab>
|
||||
</HoppSmartTabs>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { watch } from "vue"
|
||||
import { useToast } from "@composables/toast"
|
||||
import { useI18n } from "@composables/i18n"
|
||||
import { useToast } from "@composables/toast"
|
||||
import { pwaNeedsRefresh, refreshAppForPWAUpdate } from "@modules/pwa"
|
||||
import { watch } from "vue"
|
||||
|
||||
export const usePwaPrompt = function () {
|
||||
const toast = useToast()
|
||||
@@ -11,28 +11,33 @@ export const usePwaPrompt = function () {
|
||||
pwaNeedsRefresh,
|
||||
(value) => {
|
||||
if (value) {
|
||||
toast.show(`${t("app.new_version_found")}`, {
|
||||
duration: 0,
|
||||
action: [
|
||||
{
|
||||
text: `${t("action.dismiss")}`,
|
||||
onClick: (_, toastObject) => {
|
||||
toastObject.goAway(0)
|
||||
},
|
||||
},
|
||||
{
|
||||
text: `${t("app.reload")}`,
|
||||
onClick: (_, toastObject) => {
|
||||
toastObject.goAway(0)
|
||||
refreshAppForPWAUpdate()
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
showUpdateToast()
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
)
|
||||
|
||||
function showUpdateToast() {
|
||||
toast.show(`${t("app.new_version_found")}`, {
|
||||
position: "bottom-left",
|
||||
duration: 0,
|
||||
action: [
|
||||
{
|
||||
text: `${t("action.dismiss")}`,
|
||||
onClick: (_, toastObject) => {
|
||||
toastObject.goAway(0)
|
||||
},
|
||||
},
|
||||
{
|
||||
text: `${t("app.reload")}`,
|
||||
onClick: (_, toastObject) => {
|
||||
toastObject.goAway(0)
|
||||
refreshAppForPWAUpdate()
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
61
packages/hoppscotch-common/src/composables/whats-new.ts
Normal file
61
packages/hoppscotch-common/src/composables/whats-new.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { toast as sonner } from "@hoppscotch/ui"
|
||||
import { markRaw } from "vue"
|
||||
|
||||
import WhatsNewDialog from "~/components/app/WhatsNewDialog.vue"
|
||||
import { getService } from "~/modules/dioc"
|
||||
import { PersistenceService } from "~/services/persistence"
|
||||
import { version as hoppscotchCommonPkgVersion } from "./../../package.json"
|
||||
|
||||
export const useWhatsNewDialog = async function () {
|
||||
const persistenceService = getService(PersistenceService)
|
||||
|
||||
const versionFromLocalStorage = persistenceService.getLocalConfig("hopp_v")
|
||||
|
||||
// Set new entry `hopp_v` under `localStorage` if not present
|
||||
if (!versionFromLocalStorage) {
|
||||
persistenceService.setLocalConfig("hopp_v", hoppscotchCommonPkgVersion)
|
||||
return
|
||||
}
|
||||
|
||||
// Already on the latest version
|
||||
if (versionFromLocalStorage === hoppscotchCommonPkgVersion) {
|
||||
return
|
||||
}
|
||||
|
||||
const getMajorVersion = (v: string) => v.split(".").slice(0, 2).join(".")
|
||||
|
||||
const majorVersionFromLocalStorage = getMajorVersion(versionFromLocalStorage)
|
||||
const hoppscotchCommonPkgMajorVersion = getMajorVersion(
|
||||
hoppscotchCommonPkgVersion
|
||||
)
|
||||
|
||||
// Skipping the minor version update. e.g. 2024.1.0 -> 2024.1.1
|
||||
// Checking major version update. e.g. 2024.1 -> 2024.2
|
||||
|
||||
// Show the release notes during a major version update
|
||||
if (majorVersionFromLocalStorage !== hoppscotchCommonPkgMajorVersion) {
|
||||
const notesUrl = await getReleaseNotes(hoppscotchCommonPkgMajorVersion)
|
||||
if (notesUrl) {
|
||||
sonner.custom(markRaw(WhatsNewDialog), {
|
||||
componentProps: {
|
||||
notesUrl,
|
||||
version: hoppscotchCommonPkgVersion,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
persistenceService.setLocalConfig("hopp_v", hoppscotchCommonPkgVersion)
|
||||
}
|
||||
|
||||
async function getReleaseNotes(v: string): Promise<string | undefined> {
|
||||
try {
|
||||
const { release_notes } = await fetch(
|
||||
`https://releases.hoppscotch.com/releases/${v}.json`
|
||||
).then((res) => res.json())
|
||||
|
||||
return release_notes
|
||||
} catch (_) {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
8
packages/hoppscotch-common/src/modules/whats-new.ts
Normal file
8
packages/hoppscotch-common/src/modules/whats-new.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { useWhatsNewDialog } from "~/composables/whats-new"
|
||||
import { HoppModule } from "."
|
||||
|
||||
export default <HoppModule>{
|
||||
onRootSetup() {
|
||||
useWhatsNewDialog()
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user