Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 29 additions & 25 deletions apps/app-frontend/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ import PromotionWrapper from '@/components/ui/PromotionWrapper.vue'
import QuickInstanceSwitcher from '@/components/ui/QuickInstanceSwitcher.vue'
import SplashScreen from '@/components/ui/SplashScreen.vue'
import WindowControls from '@/components/ui/WindowControls.vue'
import { useIntercomPositioning } from '@/composables/intercom-positioning'
import { useCheckDisableMouseover } from '@/composables/macCssFix.js'
import { config } from '@/config'
import { hide_ads_window, init_ads_window, show_ads_window } from '@/helpers/ads.js'
Expand Down Expand Up @@ -125,6 +126,17 @@ import { AppNotificationManager } from './providers/app-notifications'
import { AppPopupNotificationManager } from './providers/app-popup-notifications'

const themeStore = useTheming()
const router = useRouter()
const route = useRoute()
const intercomBubblePositioning = useIntercomPositioning({ route, themeStore })
const {
sidebarToggled,
forceSidebar,
sidebarVisible,
intercomBubblePosition,
updateIntercomBubbleStyles,
clearIntercomBubbleStyles,
} = intercomBubblePositioning

const notificationManager = new AppNotificationManager()
provideNotificationManager(notificationManager)
Expand Down Expand Up @@ -158,6 +170,7 @@ provideModrinthClient(tauriApiClient)
providePageContext({
hierarchicalSidebarAvailable: ref(true),
showAds: ref(false),
...intercomBubblePositioning.pageContext,
featureFlags: {
serverRamAsBytesAlwaysOn: computed(() =>
themeStore.getFeatureFlag('server_ram_as_bytes_always_on'),
Expand Down Expand Up @@ -242,6 +255,7 @@ onUnmounted(async () => {
document.querySelector('body').removeEventListener('click', handleClick)
document.querySelector('body').removeEventListener('auxclick', handleAuxClick)
shutdownHostingIntercom()
clearIntercomBubbleStyles()

await unlistenUpdateDownload?.()
})
Expand Down Expand Up @@ -423,9 +437,6 @@ const handleClose = async () => {
await getCurrentWindow().close()
}

const router = useRouter()
const route = useRoute()

const loading = setupLoadingStateProvider()
loading.setEnabled(false)
let initialLoadToken = loading.begin()
Expand Down Expand Up @@ -643,22 +654,10 @@ const hasPlus = computed(
(credentials.value.user.badges & MIDAS_BITFLAG) === MIDAS_BITFLAG,
)

const sidebarToggled = ref(true)

themeStore.$subscribe(() => {
sidebarToggled.value = !themeStore.toggleSidebar
})

const forceSidebar = computed(
() => route.path.startsWith('/browse') || route.path.startsWith('/project'),
)
const sidebarVisible = computed(() => sidebarToggled.value || forceSidebar.value)
const showAd = computed(
() => sidebarVisible.value && !hasPlus.value && credentials.value !== undefined,
)
const hostingRouteActive = computed(() => route.path.startsWith('/hosting'))
const INTERCOM_DEFAULT_PADDING = 20
const INTERCOM_APP_SIDEBAR_WIDTH = 300

let intercomBooting = false
let intercomBooted = false
Expand Down Expand Up @@ -706,10 +705,8 @@ async function bootIntercom() {
intercom_user_jwt: token,
session_duration: 1000 * 60 * 60 * 24,
alignment: 'right',
horizontal_padding: sidebarVisible.value
? INTERCOM_APP_SIDEBAR_WIDTH + INTERCOM_DEFAULT_PADDING
: INTERCOM_DEFAULT_PADDING,
vertical_padding: INTERCOM_DEFAULT_PADDING,
horizontal_padding: intercomBubblePosition.value.horizontalPadding,
vertical_padding: intercomBubblePosition.value.verticalPadding,
})
intercomBooted = true
} catch (error) {
Expand All @@ -727,14 +724,13 @@ function shutdownHostingIntercom() {
}

watch(
sidebarVisible,
(visible) => {
intercomBubblePosition,
(position) => {
updateIntercomBubbleStyles(position)
if (intercomBooted) {
window.Intercom?.('update', {
horizontal_padding: visible
? INTERCOM_APP_SIDEBAR_WIDTH + INTERCOM_DEFAULT_PADDING
: INTERCOM_DEFAULT_PADDING,
vertical_padding: INTERCOM_DEFAULT_PADDING,
horizontal_padding: position.horizontalPadding,
vertical_padding: position.verticalPadding,
})
}
},
Expand Down Expand Up @@ -1772,6 +1768,14 @@ provideAppUpdateDownloadProgress(appUpdateDownload)
--os-handle-bg-active: var(--color-scrollbar) !important;
}

.intercom-lightweight-app-launcher,
.intercom-launcher-frame,
iframe[name='intercom-launcher-frame'] {
right: var(--app-support-launcher-right, 20px) !important;
bottom: var(--app-support-launcher-bottom, 20px) !important;
z-index: 9 !important;
}

.mac {
.app-grid-statusbar {
padding-left: 5rem;
Expand Down
100 changes: 100 additions & 0 deletions apps/app-frontend/src/composables/intercom-positioning.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { computed, onUnmounted, ref } from 'vue'
import type { RouteLocationNormalizedLoaded } from 'vue-router'

interface ThemeStore {
toggleSidebar: boolean
$subscribe: (callback: () => void) => () => void
}

interface IntercomBubblePosition {
horizontalPadding: number
verticalPadding: number
}

const APP_LEFT_NAV_WIDTH = '4rem'
const APP_SIDEBAR_WIDTH = 300
const INTERCOM_BUBBLE_DEFAULT_PADDING = 20
const INTERCOM_BUBBLE_WIDTH = 72
const INTERCOM_BUBBLE_RIGHT_VAR = '--app-support-launcher-right'
const INTERCOM_BUBBLE_BOTTOM_VAR = '--app-support-launcher-bottom'

export function useIntercomPositioning({
route,
themeStore,
}: {
route: RouteLocationNormalizedLoaded
themeStore: ThemeStore
}) {
const sidebarToggled = ref(true)
const unsubscribeSidebarToggle = themeStore.$subscribe(() => {
sidebarToggled.value = !themeStore.toggleSidebar
})

onUnmounted(unsubscribeSidebarToggle)

const forceSidebar = computed(
() => route.path.startsWith('/browse') || route.path.startsWith('/project'),
)
const sidebarVisible = computed(() => sidebarToggled.value || forceSidebar.value)
const intercomBubbleHorizontalPadding = computed(() =>
sidebarVisible.value
? APP_SIDEBAR_WIDTH + INTERCOM_BUBBLE_DEFAULT_PADDING
: INTERCOM_BUBBLE_DEFAULT_PADDING,
)
const intercomBubbleVerticalClearance = ref<number | null>(null)
const intercomBubblePosition = computed(() => ({
horizontalPadding: intercomBubbleHorizontalPadding.value,
verticalPadding: intercomBubbleVerticalClearance.value ?? INTERCOM_BUBBLE_DEFAULT_PADDING,
}))
const intercomBubbleClearanceRequests = new Map<symbol, number>()

function requestIntercomBubbleVerticalClearance(id: symbol, clearance: number | null) {
if (clearance === null) {
intercomBubbleClearanceRequests.delete(id)
} else {
intercomBubbleClearanceRequests.set(id, clearance)
}

intercomBubbleVerticalClearance.value =
intercomBubbleClearanceRequests.size > 0
? Math.max(...intercomBubbleClearanceRequests.values())
: null
}

function updateIntercomBubbleStyles({
horizontalPadding,
verticalPadding,
}: IntercomBubblePosition) {
if (typeof document === 'undefined') return

document.documentElement.style.setProperty(INTERCOM_BUBBLE_RIGHT_VAR, `${horizontalPadding}px`)
document.documentElement.style.setProperty(INTERCOM_BUBBLE_BOTTOM_VAR, `${verticalPadding}px`)
}

function clearIntercomBubbleStyles() {
if (typeof document === 'undefined') return

document.documentElement.style.removeProperty(INTERCOM_BUBBLE_RIGHT_VAR)
document.documentElement.style.removeProperty(INTERCOM_BUBBLE_BOTTOM_VAR)
}

return {
sidebarToggled,
forceSidebar,
sidebarVisible,
intercomBubblePosition,
updateIntercomBubbleStyles,
clearIntercomBubbleStyles,
pageContext: {
floatingActionBarOffsets: {
left: ref(APP_LEFT_NAV_WIDTH),
right: computed(() => (sidebarVisible.value ? `${APP_SIDEBAR_WIDTH}px` : '0px')),
},
intercomBubble: {
width: ref(INTERCOM_BUBBLE_WIDTH),
horizontalPadding: intercomBubbleHorizontalPadding,
requestVerticalClearance: requestIntercomBubbleVerticalClearance,
},
},
}
}
Loading
Loading