AccountOverview.vue 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. <!-- Copyright (C) 2012-2023 Zammad Foundation, https://zammad-foundation.org/ -->
  2. <script setup lang="ts">
  3. /* eslint-disable vue/no-v-html */
  4. import { computed, ref } from 'vue'
  5. import { useRouter } from 'vue-router'
  6. import { storeToRefs } from 'pinia'
  7. import { MutationHandler, QueryHandler } from '@shared/server/apollo/handler'
  8. import { useSessionStore } from '@shared/stores/session'
  9. import { usePWASupport, isStandalone } from '@shared/utils/pwa'
  10. import { useLocaleStore } from '@shared/stores/locale'
  11. import { browser, os } from '@shared/utils/browser'
  12. import FormGroup from '@shared/components/Form/FormGroup.vue'
  13. import CommonUserAvatar from '@shared/components/CommonUserAvatar/CommonUserAvatar.vue'
  14. import { useProductAboutQuery } from '@shared/graphql/queries/about.api'
  15. import CommonSectionMenu from '@mobile/components/CommonSectionMenu/CommonSectionMenu.vue'
  16. import CommonSectionMenuLink from '@mobile/components/CommonSectionMenu/CommonSectionMenuLink.vue'
  17. import CommonSectionPopup from '@mobile/components/CommonSectionPopup/CommonSectionPopup.vue'
  18. import { useRawHTMLIcon } from '@shared/components/CommonIcon'
  19. import { i18n } from '@shared/i18n'
  20. import { useAccountLocaleMutation } from '../graphql/mutations/locale.api'
  21. const router = useRouter()
  22. const logout = () => {
  23. router.push('/logout')
  24. }
  25. const session = useSessionStore()
  26. const { user } = storeToRefs(session)
  27. const localeStore = useLocaleStore()
  28. const savingLocale = ref(false)
  29. const locales = computed(() => {
  30. return (
  31. localeStore.locales?.map((locale) => {
  32. return { label: locale.name, value: locale.locale }
  33. }) || []
  34. )
  35. })
  36. const localeMutation = new MutationHandler(useAccountLocaleMutation({}), {
  37. errorNotificationMessage: __('The language could not be updated.'),
  38. })
  39. const currentLocale = computed({
  40. get: () => localeStore.localeData?.locale ?? null,
  41. set: (locale) => {
  42. // don't update if locale is the same
  43. if (
  44. !locale ||
  45. savingLocale.value ||
  46. localeStore.localeData?.locale === locale
  47. )
  48. return
  49. savingLocale.value = true
  50. Promise.all([
  51. localeStore.setLocale(locale),
  52. localeMutation.send({ locale }),
  53. ]).finally(() => {
  54. savingLocale.value = false
  55. })
  56. },
  57. })
  58. const hasVersionPermission = session.hasPermission('admin.version')
  59. const productAboutQuery = new QueryHandler(
  60. useProductAboutQuery({ enabled: hasVersionPermission }),
  61. { errorNotificationMessage: __('The product version could not be fetched.') },
  62. )
  63. const productAbout = productAboutQuery.result()
  64. const isMobileIOS = browser.name?.includes('Safari') && os.name?.includes('iOS')
  65. const { canInstallPWA, installPWA } = usePWASupport()
  66. const showInstallButton = computed(
  67. () => !isStandalone && (canInstallPWA.value || isMobileIOS),
  68. )
  69. const showInstallIOSPopup = ref(false)
  70. const installPWAMessage = computed(() => {
  71. const iconShare = useRawHTMLIcon({
  72. class: 'inline-flex text-blue',
  73. decorative: true,
  74. size: 'small',
  75. name: 'mobile-ios-share',
  76. })
  77. const iconAdd = useRawHTMLIcon({
  78. class: 'inline-flex',
  79. decorative: true,
  80. size: 'small',
  81. name: 'mobile-add-square',
  82. })
  83. return i18n.t(
  84. __(
  85. 'To install %s as an app, press the %s "Share" button and then the %s "Add to Home Screen" button.',
  86. ),
  87. __('Zammad'),
  88. iconShare,
  89. iconAdd,
  90. )
  91. })
  92. const installZammadPWA = () => {
  93. if (isStandalone) return
  94. // on chromium this will show a chrome popup with native "install" button
  95. if (canInstallPWA.value) {
  96. installPWA()
  97. return
  98. }
  99. // on iOS we cannot install it with native functionality, so we show
  100. // instructions on how to install it
  101. // let's pray Apple will add native functionality in the future
  102. if (isMobileIOS) {
  103. showInstallIOSPopup.value = true
  104. }
  105. }
  106. </script>
  107. <template>
  108. <div class="px-4">
  109. <div v-if="user" class="flex flex-col items-center justify-center py-6">
  110. <div>
  111. <CommonUserAvatar :entity="user" size="xl" personal />
  112. </div>
  113. <div class="mt-2 text-xl font-bold">
  114. {{ user.firstname }} {{ user.lastname }}
  115. </div>
  116. <!-- TODO email -->
  117. </div>
  118. <!-- TODO maybe instead of a different page we can use a Dialog? -->
  119. <CommonSectionMenu v-if="session.hasPermission('user_preferences.avatar')">
  120. <CommonSectionMenuLink
  121. :icon="{ name: 'mobile-person', size: 'base' }"
  122. icon-bg="bg-pink"
  123. link="/account/avatar"
  124. >
  125. {{ $t('Avatar') }}
  126. </CommonSectionMenuLink>
  127. </CommonSectionMenu>
  128. <!--
  129. TODO: no-options-label-translation is not working currently, therefore we need to explicitly set it to true
  130. -->
  131. <FormGroup v-if="session.hasPermission('user_preferences.language')">
  132. <FormKit
  133. v-model="currentLocale"
  134. type="treeselect"
  135. :label="__('Language')"
  136. :options="locales"
  137. :disabled="savingLocale"
  138. :no-options-label-translation="true"
  139. sorting="label"
  140. />
  141. <template #help>
  142. {{ $t('Did you know? You can help translating %s at:', 'Zammad') }}
  143. <CommonLink class="text-blue" link="https://translations.zammad.org">
  144. translations.zammad.org
  145. </CommonLink>
  146. </template>
  147. </FormGroup>
  148. <CommonSectionMenu v-if="hasVersionPermission || showInstallButton">
  149. <CommonSectionMenuLink
  150. v-if="hasVersionPermission"
  151. :icon="{ name: 'mobile-info', size: 'base' }"
  152. :information="productAbout?.productAbout"
  153. icon-bg="bg-gray"
  154. >
  155. {{ $t('About') }}
  156. </CommonSectionMenuLink>
  157. <CommonSectionMenuLink
  158. v-if="showInstallButton"
  159. :icon="{ name: 'mobile-install', size: 'small' }"
  160. icon-bg="bg-blue"
  161. @click="installZammadPWA"
  162. >
  163. {{ $t('Install App') }}
  164. </CommonSectionMenuLink>
  165. </CommonSectionMenu>
  166. <div class="mb-4">
  167. <FormKit
  168. wrapper-class="mt-4 text-base flex grow justify-center items-center"
  169. input-class="py-2 px-4 w-full h-14 !text-red-bright formkit-variant-primary:bg-red-dark rounded-xl select-none"
  170. type="submit"
  171. name="signout"
  172. @click="logout"
  173. >
  174. {{ $t('Sign out') }}
  175. </FormKit>
  176. </div>
  177. <CommonSectionPopup v-model:state="showInstallIOSPopup" :items="[]">
  178. <template #header>
  179. <section class="inline-flex min-h-[54px] items-center p-3">
  180. <span v-html="installPWAMessage" />
  181. </section>
  182. </template>
  183. </CommonSectionPopup>
  184. </div>
  185. </template>