default.vue 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. <template>
  2. <div class="flex w-screen h-screen">
  3. <Splitpanes class="no-splitter" :dbl-click-splitter="false" horizontal>
  4. <Pane v-if="!ZEN_MODE" style="height: auto">
  5. <AppHeader />
  6. </Pane>
  7. <Pane
  8. :class="spacerClass"
  9. class="flex flex-1 hide-scrollbar !overflow-auto md:mb-0"
  10. >
  11. <Splitpanes
  12. class="no-splitter"
  13. :dbl-click-splitter="false"
  14. :horizontal="!mdAndLarger"
  15. >
  16. <Pane
  17. style="width: auto; height: auto"
  18. class="hide-scrollbar !overflow-auto hidden md:flex md:flex-col"
  19. >
  20. <AppSidenav />
  21. </Pane>
  22. <Pane class="flex flex-1 hide-scrollbar !overflow-auto">
  23. <Splitpanes
  24. class="no-splitter"
  25. :dbl-click-splitter="false"
  26. horizontal
  27. >
  28. <Pane class="flex flex-1 hide-scrollbar !overflow-auto">
  29. <main class="flex flex-1 w-full" role="main">
  30. <nuxt class="flex flex-1" />
  31. </main>
  32. </Pane>
  33. </Splitpanes>
  34. </Pane>
  35. </Splitpanes>
  36. </Pane>
  37. <Pane v-if="mdAndLarger" style="height: auto">
  38. <AppFooter />
  39. </Pane>
  40. <Pane
  41. v-else
  42. style="height: auto"
  43. class="hide-scrollbar !overflow-auto flex flex-col fixed inset-x-0 bottom-0 z-10"
  44. >
  45. <AppSidenav />
  46. </Pane>
  47. </Splitpanes>
  48. <AppPowerSearch :show="showSearch" @hide-modal="showSearch = false" />
  49. <AppSupport
  50. v-if="mdAndLarger"
  51. :show="showSupport"
  52. @hide-modal="showSupport = false"
  53. />
  54. <AppOptions v-else :show="showSupport" @hide-modal="showSupport = false" />
  55. </div>
  56. </template>
  57. <script lang="ts">
  58. import {
  59. defineComponent,
  60. computed,
  61. onBeforeMount,
  62. useContext,
  63. useRouter,
  64. watch,
  65. ref,
  66. } from "@nuxtjs/composition-api"
  67. import { Splitpanes, Pane } from "splitpanes"
  68. import "splitpanes/dist/splitpanes.css"
  69. import { breakpointsTailwind, useBreakpoints } from "@vueuse/core"
  70. import { setupLocalPersistence } from "~/newstore/localpersistence"
  71. import { performMigrations } from "~/helpers/migrations"
  72. import { initUserInfo } from "~/helpers/teams/BackendUserInfo"
  73. import { applySetting, useSetting } from "~/newstore/settings"
  74. import { logPageView } from "~/helpers/fb/analytics"
  75. import { hookKeybindingsListener } from "~/helpers/keybindings"
  76. import { defineActionHandler } from "~/helpers/actions"
  77. import { useSentry } from "~/helpers/sentry"
  78. import { useColorMode } from "~/helpers/utils/composables"
  79. function appLayout() {
  80. const rightSidebar = useSetting("SIDEBAR")
  81. const columnLayout = useSetting("COLUMN_LAYOUT")
  82. const breakpoints = useBreakpoints(breakpointsTailwind)
  83. const mdAndLarger = breakpoints.greater("md")
  84. // Initially apply
  85. onBeforeMount(() => {
  86. if (!mdAndLarger.value) {
  87. rightSidebar.value = false
  88. columnLayout.value = true
  89. }
  90. })
  91. // Listen for updates
  92. watch(mdAndLarger, () => {
  93. if (mdAndLarger.value) rightSidebar.value = true
  94. else {
  95. rightSidebar.value = false
  96. columnLayout.value = true
  97. }
  98. })
  99. }
  100. function setupSentry() {
  101. const sentry = useSentry()
  102. const telemetryEnabled = useSetting("TELEMETRY_ENABLED")
  103. // Disable sentry error reporting if no telemetry allowed
  104. watch(
  105. telemetryEnabled,
  106. () => {
  107. const client = sentry.getCurrentHub()?.getClient()
  108. if (!client) return
  109. client.getOptions().enabled = telemetryEnabled.value
  110. },
  111. { immediate: true }
  112. )
  113. }
  114. function updateThemes() {
  115. const $colorMode = useColorMode()
  116. // Apply theme updates
  117. const themeColor = useSetting("THEME_COLOR")
  118. const bgColor = useSetting("BG_COLOR")
  119. const fontSize = useSetting("FONT_SIZE")
  120. const EXPAND_NAVIGATION = useSetting("EXPAND_NAVIGATION")
  121. const spacerClass = computed(() => {
  122. if (fontSize.value === "small" && EXPAND_NAVIGATION.value)
  123. return "spacer-small"
  124. if (fontSize.value === "medium" && EXPAND_NAVIGATION.value)
  125. return "spacer-medium"
  126. if (fontSize.value === "large" && EXPAND_NAVIGATION.value)
  127. return "spacer-large"
  128. if (
  129. (fontSize.value === "small" ||
  130. fontSize.value === "medium" ||
  131. fontSize.value === "large") &&
  132. !EXPAND_NAVIGATION.value
  133. )
  134. return "spacer-expand"
  135. })
  136. // Initially apply
  137. onBeforeMount(() => {
  138. document.documentElement.setAttribute("data-accent", themeColor.value)
  139. $colorMode.preference = bgColor.value
  140. document.documentElement.setAttribute("data-font-size", fontSize.value)
  141. })
  142. // Listen for updates
  143. watch(themeColor, () =>
  144. document.documentElement.setAttribute("data-accent", themeColor.value)
  145. )
  146. watch(bgColor, () => ($colorMode.preference = bgColor.value))
  147. watch(fontSize, () =>
  148. document.documentElement.setAttribute("data-font-size", fontSize.value)
  149. )
  150. return {
  151. spacerClass,
  152. }
  153. }
  154. function defineJumpActions() {
  155. const router = useRouter()
  156. const { localePath } = useContext() as any
  157. defineActionHandler("navigation.jump.rest", () => {
  158. router.push({ path: localePath("/") })
  159. })
  160. defineActionHandler("navigation.jump.graphql", () => {
  161. router.push({ path: localePath("/graphql") })
  162. })
  163. defineActionHandler("navigation.jump.realtime", () => {
  164. router.push({ path: localePath("/realtime") })
  165. })
  166. defineActionHandler("navigation.jump.documentation", () => {
  167. router.push({ path: localePath("/documentation") })
  168. })
  169. defineActionHandler("navigation.jump.settings", () => {
  170. router.push({ path: localePath("/settings") })
  171. })
  172. defineActionHandler("navigation.jump.profile", () => {
  173. router.push({ path: localePath("/profile") })
  174. })
  175. defineActionHandler("settings.theme.system", () => {
  176. applySetting("BG_COLOR", "system")
  177. })
  178. defineActionHandler("settings.theme.light", () => {
  179. applySetting("BG_COLOR", "light")
  180. })
  181. defineActionHandler("settings.theme.dark", () => {
  182. applySetting("BG_COLOR", "dark")
  183. })
  184. defineActionHandler("settings.theme.black", () => {
  185. applySetting("BG_COLOR", "black")
  186. })
  187. }
  188. export default defineComponent({
  189. components: { Splitpanes, Pane },
  190. setup() {
  191. appLayout()
  192. hookKeybindingsListener()
  193. defineJumpActions()
  194. const { spacerClass } = updateThemes()
  195. setupSentry()
  196. const breakpoints = useBreakpoints(breakpointsTailwind)
  197. const mdAndLarger = breakpoints.greater("md")
  198. const showSearch = ref(false)
  199. const showSupport = ref(false)
  200. defineActionHandler("modals.search.toggle", () => {
  201. showSearch.value = !showSearch.value
  202. })
  203. defineActionHandler("modals.support.toggle", () => {
  204. showSupport.value = !showSupport.value
  205. })
  206. return {
  207. mdAndLarger,
  208. spacerClass,
  209. ZEN_MODE: useSetting("ZEN_MODE"),
  210. showSearch,
  211. showSupport,
  212. }
  213. },
  214. head() {
  215. return this.$nuxtI18nHead({ addSeoAttributes: true })
  216. },
  217. watch: {
  218. $route(to) {
  219. logPageView(to.fullPath)
  220. },
  221. },
  222. beforeMount() {
  223. setupLocalPersistence()
  224. },
  225. async mounted() {
  226. performMigrations()
  227. console.info(
  228. "%cWe ❤︎ open source!",
  229. "background-color:white;padding:8px 16px;border-radius:8px;font-size:32px;color:red;"
  230. )
  231. console.info(
  232. "%cContribute: https://github.com/hoppscotch/hoppscotch",
  233. "background-color:black;padding:4px 8px;border-radius:8px;font-size:16px;color:white;"
  234. )
  235. const workbox = await (window as any).$workbox
  236. if (workbox) {
  237. workbox.addEventListener("installed", (event: any) => {
  238. if (event.isUpdate) {
  239. this.$toast.show(`${this.$t("app.new_version_found")}`, {
  240. duration: 0,
  241. action: [
  242. {
  243. text: `${this.$t("action.dismiss")}`,
  244. onClick: (_, toastObject) => {
  245. toastObject.goAway(0)
  246. },
  247. },
  248. {
  249. text: `${this.$t("app.reload")}`,
  250. onClick: (_, toastObject) => {
  251. toastObject.goAway(0)
  252. window.location.reload()
  253. },
  254. },
  255. ],
  256. })
  257. }
  258. })
  259. }
  260. initUserInfo()
  261. logPageView(this.$router.currentRoute.fullPath)
  262. },
  263. })
  264. </script>
  265. <style scoped>
  266. .spacer-small {
  267. margin-bottom: 4.2rem;
  268. }
  269. .spacer-medium {
  270. margin-bottom: 4.8rem;
  271. }
  272. .spacer-large {
  273. margin-bottom: 5.5rem;
  274. }
  275. .spacer-expand {
  276. margin-bottom: 2.9rem;
  277. }
  278. @media screen and (min-width: 768px) {
  279. .spacer-small {
  280. margin-bottom: 0;
  281. }
  282. .spacer-medium {
  283. margin-bottom: 0;
  284. }
  285. .spacer-large {
  286. margin-bottom: 0;
  287. }
  288. .spacer-expand {
  289. margin-bottom: 0;
  290. }
  291. }
  292. </style>