feat: new banner service and added ability to bind additional services from other platforms (#3474)
Co-authored-by: jamesgeorge007 <jamesgeorge998001@gmail.com>
This commit is contained in:
committed by
GitHub
parent
23e3739718
commit
507fe69efe
@@ -9,6 +9,7 @@ declare module "vue" {
|
|||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
AppActionHandler: typeof import('./components/app/ActionHandler.vue')['default']
|
AppActionHandler: typeof import('./components/app/ActionHandler.vue')['default']
|
||||||
AppAnnouncement: typeof import('./components/app/Announcement.vue')['default']
|
AppAnnouncement: typeof import('./components/app/Announcement.vue')['default']
|
||||||
|
AppBanner: typeof import('./components/app/Banner.vue')['default']
|
||||||
AppContextMenu: typeof import('./components/app/ContextMenu.vue')['default']
|
AppContextMenu: typeof import('./components/app/ContextMenu.vue')['default']
|
||||||
AppDeveloperOptions: typeof import('./components/app/DeveloperOptions.vue')['default']
|
AppDeveloperOptions: typeof import('./components/app/DeveloperOptions.vue')['default']
|
||||||
AppFooter: typeof import('./components/app/Footer.vue')['default']
|
AppFooter: typeof import('./components/app/Footer.vue')['default']
|
||||||
@@ -140,6 +141,7 @@ declare module "vue" {
|
|||||||
HttpTests: typeof import('./components/http/Tests.vue')['default']
|
HttpTests: typeof import('./components/http/Tests.vue')['default']
|
||||||
HttpURLEncodedParams: typeof import('./components/http/URLEncodedParams.vue')['default']
|
HttpURLEncodedParams: typeof import('./components/http/URLEncodedParams.vue')['default']
|
||||||
IconLucideActivity: typeof import('~icons/lucide/activity')['default']
|
IconLucideActivity: typeof import('~icons/lucide/activity')['default']
|
||||||
|
IconLucideAlertCircle: typeof import('~icons/lucide/alert-circle')['default']
|
||||||
IconLucideAlertTriangle: typeof import('~icons/lucide/alert-triangle')['default']
|
IconLucideAlertTriangle: typeof import('~icons/lucide/alert-triangle')['default']
|
||||||
IconLucideArrowLeft: typeof import('~icons/lucide/arrow-left')['default']
|
IconLucideArrowLeft: typeof import('~icons/lucide/arrow-left')['default']
|
||||||
IconLucideArrowUpRight: typeof import('~icons/lucide/arrow-up-right')['default']
|
IconLucideArrowUpRight: typeof import('~icons/lucide/arrow-up-right')['default']
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div
|
|
||||||
class="group relative flex items-center bg-error px-4 py-2 text-tiny transition"
|
|
||||||
role="alert"
|
|
||||||
>
|
|
||||||
<icon-lucide-info class="mr-2" />
|
|
||||||
<span class="text-secondaryDark">
|
|
||||||
<span class="md:hidden">
|
|
||||||
{{ t("helpers.offline_short") }}
|
|
||||||
</span>
|
|
||||||
<span class="<md:hidden">
|
|
||||||
{{ t("helpers.offline") }}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { useI18n } from "~/composables/i18n"
|
|
||||||
|
|
||||||
const t = useI18n()
|
|
||||||
</script>
|
|
||||||
54
packages/hoppscotch-common/src/components/app/Banner.vue
Normal file
54
packages/hoppscotch-common/src/components/app/Banner.vue
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
:role="bannerRole"
|
||||||
|
class="flex items-center px-4 py-2 text-tiny"
|
||||||
|
:class="bannerColor"
|
||||||
|
>
|
||||||
|
<component :is="bannerIcon" class="mr-2 text-white" />
|
||||||
|
|
||||||
|
<span class="text-white">
|
||||||
|
<span v-if="banner.alternateText" class="md:hidden">
|
||||||
|
{{ banner.alternateText }}
|
||||||
|
</span>
|
||||||
|
<span class="<md:hidden">
|
||||||
|
{{ banner.text }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from "vue"
|
||||||
|
|
||||||
|
import { BannerContent, BannerType } from "~/services/banner.service"
|
||||||
|
|
||||||
|
import IconAlertCircle from "~icons/lucide/alert-circle"
|
||||||
|
import IconAlertTriangle from "~icons/lucide/alert-triangle"
|
||||||
|
import IconInfo from "~icons/lucide/info"
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
banner: BannerContent
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const ariaRoles: Record<BannerType, string> = {
|
||||||
|
error: "alert",
|
||||||
|
warning: "status",
|
||||||
|
info: "status",
|
||||||
|
}
|
||||||
|
|
||||||
|
const bgColors: Record<BannerType, string> = {
|
||||||
|
error: "bg-red-700",
|
||||||
|
warning: "bg-yellow-700",
|
||||||
|
info: "bg-stone-800",
|
||||||
|
}
|
||||||
|
|
||||||
|
const icons = {
|
||||||
|
info: IconInfo,
|
||||||
|
warning: IconAlertCircle,
|
||||||
|
error: IconAlertTriangle,
|
||||||
|
}
|
||||||
|
|
||||||
|
const bannerColor = computed(() => bgColors[props.banner.type])
|
||||||
|
const bannerIcon = computed(() => icons[props.banner.type])
|
||||||
|
const bannerRole = computed(() => ariaRoles[props.banner.type])
|
||||||
|
</script>
|
||||||
@@ -215,7 +215,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<AppAnnouncement v-if="!network.isOnline" />
|
<AppBanner v-if="banner" :banner="banner" />
|
||||||
<TeamsModal :show="showTeamsModal" @hide-modal="showTeamsModal = false" />
|
<TeamsModal :show="showTeamsModal" @hide-modal="showTeamsModal = false" />
|
||||||
<TeamsInvite
|
<TeamsInvite
|
||||||
v-if="workspace.type === 'team' && workspace.teamID"
|
v-if="workspace.type === 'team' && workspace.teamID"
|
||||||
@@ -264,6 +264,7 @@ import IconUsers from "~icons/lucide/users"
|
|||||||
import { pipe } from "fp-ts/function"
|
import { pipe } from "fp-ts/function"
|
||||||
import * as TE from "fp-ts/TaskEither"
|
import * as TE from "fp-ts/TaskEither"
|
||||||
import { deleteTeam as backendDeleteTeam } from "~/helpers/backend/mutations/Team"
|
import { deleteTeam as backendDeleteTeam } from "~/helpers/backend/mutations/Team"
|
||||||
|
import { BannerService } from "~/services/banner.service"
|
||||||
|
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
@@ -281,8 +282,21 @@ const showTeamsModal = ref(false)
|
|||||||
const breakpoints = useBreakpoints(breakpointsTailwind)
|
const breakpoints = useBreakpoints(breakpointsTailwind)
|
||||||
const mdAndLarger = breakpoints.greater("md")
|
const mdAndLarger = breakpoints.greater("md")
|
||||||
|
|
||||||
|
const { content: banner } = useService(BannerService)
|
||||||
const network = reactive(useNetwork())
|
const network = reactive(useNetwork())
|
||||||
|
|
||||||
|
watch(network, () => {
|
||||||
|
if (network.isOnline) {
|
||||||
|
banner.value = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
banner.value = {
|
||||||
|
type: "info",
|
||||||
|
text: t("helpers.offline"),
|
||||||
|
alternateText: t("helpers.offline_short"),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const currentUser = useReadonlyStream(
|
const currentUser = useReadonlyStream(
|
||||||
platform.auth.getProbableUserStream(),
|
platform.auth.getProbableUserStream(),
|
||||||
platform.auth.getProbableUser()
|
platform.auth.getProbableUser()
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { HoppModule } from "."
|
|||||||
import { Container, Service } from "dioc"
|
import { Container, Service } from "dioc"
|
||||||
import { diocPlugin } from "dioc/vue"
|
import { diocPlugin } from "dioc/vue"
|
||||||
import { DebugService } from "~/services/debug.service"
|
import { DebugService } from "~/services/debug.service"
|
||||||
|
import { platform } from "~/platform"
|
||||||
|
|
||||||
const serviceContainer = new Container()
|
const serviceContainer = new Container()
|
||||||
|
|
||||||
@@ -34,5 +35,8 @@ export default <HoppModule>{
|
|||||||
app.use(diocPlugin, {
|
app.use(diocPlugin, {
|
||||||
container: serviceContainer,
|
container: serviceContainer,
|
||||||
})
|
})
|
||||||
|
for (const service of platform.addedServices ?? []) {
|
||||||
|
serviceContainer.bind(service)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,12 @@ import { AnalyticsPlatformDef } from "./analytics"
|
|||||||
import { InterceptorsPlatformDef } from "./interceptors"
|
import { InterceptorsPlatformDef } from "./interceptors"
|
||||||
import { HoppModule } from "~/modules"
|
import { HoppModule } from "~/modules"
|
||||||
import { InspectorsPlatformDef } from "./inspectors"
|
import { InspectorsPlatformDef } from "./inspectors"
|
||||||
|
import { Service } from "dioc"
|
||||||
|
|
||||||
export type PlatformDef = {
|
export type PlatformDef = {
|
||||||
ui?: UIPlatformDef
|
ui?: UIPlatformDef
|
||||||
addedHoppModules?: HoppModule[]
|
addedHoppModules?: HoppModule[]
|
||||||
|
addedServices?: Array<typeof Service<unknown> & { ID: string }>
|
||||||
auth: AuthPlatformDef
|
auth: AuthPlatformDef
|
||||||
analytics?: AnalyticsPlatformDef
|
analytics?: AnalyticsPlatformDef
|
||||||
sync: {
|
sync: {
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { describe, expect, it } from "vitest"
|
||||||
|
import { BannerContent, BannerService } from "../banner.service"
|
||||||
|
import { TestContainer } from "dioc/testing"
|
||||||
|
|
||||||
|
describe("BannerService", () => {
|
||||||
|
const container = new TestContainer()
|
||||||
|
const service = container.bind(BannerService)
|
||||||
|
|
||||||
|
it("initally there are no banners defined", () => {
|
||||||
|
expect(service.content.value).toEqual(null)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to set and retrieve banner content", () => {
|
||||||
|
const sampleBanner: BannerContent = {
|
||||||
|
type: "info",
|
||||||
|
text: "Info Banner",
|
||||||
|
}
|
||||||
|
|
||||||
|
const banner = service.content
|
||||||
|
banner.value = sampleBanner
|
||||||
|
const retrievedBanner = service.content.value
|
||||||
|
|
||||||
|
expect(retrievedBanner).toEqual(sampleBanner)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to update the banner content", () => {
|
||||||
|
const updatedBanner: BannerContent = {
|
||||||
|
type: "warning",
|
||||||
|
text: "Updated Banner Content",
|
||||||
|
alternateText: "Updated Banner",
|
||||||
|
}
|
||||||
|
|
||||||
|
service.content.value = updatedBanner
|
||||||
|
const retrievedBanner = service.content.value
|
||||||
|
|
||||||
|
expect(retrievedBanner).toEqual(updatedBanner)
|
||||||
|
})
|
||||||
|
})
|
||||||
28
packages/hoppscotch-common/src/services/banner.service.ts
Normal file
28
packages/hoppscotch-common/src/services/banner.service.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { Service } from "dioc"
|
||||||
|
import { ref } from "vue"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The different types of banners that can be used.
|
||||||
|
*/
|
||||||
|
export type BannerType = "info" | "warning" | "error"
|
||||||
|
|
||||||
|
export type BannerContent = {
|
||||||
|
type: BannerType
|
||||||
|
text: string
|
||||||
|
// Can be used to display an alternate text when display size is small
|
||||||
|
alternateText?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This service is used to display a banner on the app.
|
||||||
|
* It can used to display information, warnings or errors.
|
||||||
|
*/
|
||||||
|
export class BannerService extends Service {
|
||||||
|
public static readonly ID = "BANNER_SERVICE"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a reactive variable that can be used to set the contents of the banner
|
||||||
|
* and use it to render the banner on components.
|
||||||
|
*/
|
||||||
|
public content = ref<BannerContent | null>(null)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user