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:
Joel Jacob Stephen
2023-11-06 11:41:19 +05:30
committed by GitHub
parent 23e3739718
commit 507fe69efe
8 changed files with 143 additions and 23 deletions

View File

@@ -9,6 +9,7 @@ declare module "vue" {
export interface GlobalComponents {
AppActionHandler: typeof import('./components/app/ActionHandler.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']
AppDeveloperOptions: typeof import('./components/app/DeveloperOptions.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']
HttpURLEncodedParams: typeof import('./components/http/URLEncodedParams.vue')['default']
IconLucideActivity: typeof import('~icons/lucide/activity')['default']
IconLucideAlertCircle: typeof import('~icons/lucide/alert-circle')['default']
IconLucideAlertTriangle: typeof import('~icons/lucide/alert-triangle')['default']
IconLucideArrowLeft: typeof import('~icons/lucide/arrow-left')['default']
IconLucideArrowUpRight: typeof import('~icons/lucide/arrow-up-right')['default']

View File

@@ -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>

View 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>

View File

@@ -215,7 +215,7 @@
</div>
</div>
</header>
<AppAnnouncement v-if="!network.isOnline" />
<AppBanner v-if="banner" :banner="banner" />
<TeamsModal :show="showTeamsModal" @hide-modal="showTeamsModal = false" />
<TeamsInvite
v-if="workspace.type === 'team' && workspace.teamID"
@@ -264,6 +264,7 @@ import IconUsers from "~icons/lucide/users"
import { pipe } from "fp-ts/function"
import * as TE from "fp-ts/TaskEither"
import { deleteTeam as backendDeleteTeam } from "~/helpers/backend/mutations/Team"
import { BannerService } from "~/services/banner.service"
const t = useI18n()
const toast = useToast()
@@ -281,8 +282,21 @@ const showTeamsModal = ref(false)
const breakpoints = useBreakpoints(breakpointsTailwind)
const mdAndLarger = breakpoints.greater("md")
const { content: banner } = useService(BannerService)
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(
platform.auth.getProbableUserStream(),
platform.auth.getProbableUser()

View File

@@ -2,6 +2,7 @@ import { HoppModule } from "."
import { Container, Service } from "dioc"
import { diocPlugin } from "dioc/vue"
import { DebugService } from "~/services/debug.service"
import { platform } from "~/platform"
const serviceContainer = new Container()
@@ -34,5 +35,8 @@ export default <HoppModule>{
app.use(diocPlugin, {
container: serviceContainer,
})
for (const service of platform.addedServices ?? []) {
serviceContainer.bind(service)
}
},
}

View File

@@ -9,10 +9,12 @@ import { AnalyticsPlatformDef } from "./analytics"
import { InterceptorsPlatformDef } from "./interceptors"
import { HoppModule } from "~/modules"
import { InspectorsPlatformDef } from "./inspectors"
import { Service } from "dioc"
export type PlatformDef = {
ui?: UIPlatformDef
addedHoppModules?: HoppModule[]
addedServices?: Array<typeof Service<unknown> & { ID: string }>
auth: AuthPlatformDef
analytics?: AnalyticsPlatformDef
sync: {

View File

@@ -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)
})
})

View 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)
}