Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(fe2): ugly function card hydration issue #2381

Merged
merged 3 commits into from
Jun 18, 2024
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
837 changes: 817 additions & 20 deletions packages/dui3/lib/common/generated/gql/graphql.ts

Large diffs are not rendered by default.

22 changes: 17 additions & 5 deletions packages/frontend-2/components/automate/function/Card.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,30 @@
<div class="flex gap-3 items-center" :class="{ 'w-4/5': hasLabel }">
<AutomateFunctionLogo :logo="fn.logo" />
<div class="flex flex-col truncate">
<div class="normal font-semibold text-foreground truncate hover:underline">
<RouterLink
<div
:class="[
'normal font-semibold text-foreground truncate',
noButtons ? '' : 'hover:underline'
]"
>
<Component
:is="noButtons ? 'div' : NuxtLink"
:to="automationFunctionRoute(fn.id)"
:target="externalMoreInfo ? '_blank' : undefined"
>
{{ fn.name }}
</RouterLink>
</Component>
</div>
<div class="label-light flex items-center space-x-1">
<span>by</span>
<CommonTextLink external :to="fn.repo.url" size="sm">
<Component
:is="noButtons ? 'div' : CommonTextLink"
external
:to="fn.repo.url"
size="sm"
>
{{ fn.repo.owner }}
</CommonTextLink>
</Component>
</div>
</div>
</div>
Expand Down Expand Up @@ -81,6 +92,7 @@ import type { AutomationsFunctionsCard_AutomateFunctionFragment } from '~/lib/co
import { CheckIcon, PencilIcon } from '@heroicons/vue/24/outline'
import { automationFunctionRoute } from '~/lib/common/helpers/route'
import { useMarkdown } from '~/lib/common/composables/markdown'
import { CommonTextLink } from '@speckle/ui-components'

graphql(`
fragment AutomationsFunctionsCard_AutomateFunction on AutomateFunction {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ const route = useRoute()
const projectId = computed(() => route.params.id as string)
const search = ref('')
const isAutomateEnabled = useIsAutomateModuleEnabled()
const pageFetchPolicy = usePageQueryStandardFetchPolicy()

// Base tab query (no pagination)
const { result, loading } = useQuery(
Expand All @@ -69,7 +70,7 @@ const { result, loading } = useQuery(
}),
() => ({
enabled: isAutomateEnabled.value,
fetchPolicy: 'cache-and-network'
fetchPolicy: pageFetchPolicy.value
})
)

Expand Down
3 changes: 2 additions & 1 deletion packages/frontend-2/composables/globals.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useActiveUser } from '~/lib/auth/composables/activeUser'
import { usePageQueryStandardFetchPolicy } from '~/lib/common/composables/graphql'
import { useGlobalToast } from '~/lib/common/composables/toast'

export const useIsAutomateModuleEnabled = () => {
Expand All @@ -16,4 +17,4 @@ export const useIsGendoModuleEnabled = () => {
return ref(FF_GENDOAI_MODULE_ENABLED)
}

export { useGlobalToast, useActiveUser }
export { useGlobalToast, useActiveUser, usePageQueryStandardFetchPolicy }
39 changes: 37 additions & 2 deletions packages/frontend-2/lib/common/composables/graphql.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { OperationVariables, QueryOptions } from '@apollo/client/core'
import type {
OperationVariables,
QueryOptions,
WatchQueryFetchPolicy
} from '@apollo/client/core'
import type {
DocumentParameter,
OptionsParameter
Expand All @@ -8,7 +12,8 @@ import { useQuery } from '@vue/apollo-composable'
import { convertThrowIntoFetchResult } from '~/lib/common/helpers/graphql'
import type { InfiniteLoaderState } from '@speckle/ui-components'
import { isUndefined } from 'lodash-es'
import type { MaybeNullOrUndefined } from '@speckle/shared'
import type { MaybeNullOrUndefined, Optional } from '@speckle/shared'
import { useScopedState } from '~/lib/common/composables/scopedState'

export const useApolloClientIfAvailable = () => {
const nuxt = useNuxtApp()
Expand Down Expand Up @@ -205,3 +210,33 @@ export const usePaginatedQuery = <
bustCache
}
}

/**
* We want our page queries to have the cache-and-network fetch policy, so that when you switch to a new page, the data
* gets refreshed, but in the background - while the old data is still shown.
*
* This, however, is unnecessary when hydrating the SSR page in CSR for the first time, and also
* causes weird hydration mismatches.
*
* So this sets the correct fetch policy based on whether this is a CSR->CSR navigation
*/
export const usePageQueryStandardFetchPolicy = () => {
if (import.meta.server) return computed(() => undefined)

const router = useRouter()
const hasNavigatedInCSR = useScopedState(
'usePageQueryStandardFetchPolicy-state',
() => ref(false)
)
const quitTracking = router.beforeEach((to, from) => {
if (!from || !to) return
hasNavigatedInCSR.value = true
quitTracking()
})

return computed((): Optional<WatchQueryFetchPolicy> => {
// use cache, but reload in background
// we only wanna do this when transitioning between CSR routes
return hasNavigatedInCSR.value ? 'cache-and-network' : undefined
})
}
9 changes: 4 additions & 5 deletions packages/frontend-2/pages/functions/[fid].vue
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,7 @@ const pageQuery = graphql(`
definePageMeta({
middleware: ['auth', 'require-valid-function']
})

// const { activeUser } = useActiveUser()
const pageFetchPolicy = usePageQueryStandardFetchPolicy()
const route = useRoute()
const functionId = computed(() => route.params.fid as string)
const loading = useQueryLoading()
Expand All @@ -70,9 +69,9 @@ const { result, onResult } = useQuery(
() => ({
functionId: functionId.value
}),
{
fetchPolicy: 'cache-and-network'
}
() => ({
fetchPolicy: pageFetchPolicy.value
})
)

const queryLoadedOnce = useQueryLoaded({ onResult })
Expand Down
22 changes: 13 additions & 9 deletions packages/frontend-2/pages/functions/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
v-model:search="search"
:active-user="result?.activeUser"
:server-info="result?.serverInfo"
class="mb-8"
class="mb-6"
/>
<CommonLoadingBar :loading="pageQueryLoading" />
<CommonLoadingBar :loading="pageQueryLoading" client-only class="mb-2" />
<AutomateFunctionsPageItems
:functions="finalResult"
:search="!!search"
Expand All @@ -27,7 +27,10 @@ import { CommonLoadingBar } from '@speckle/ui-components'
import { useQuery } from '@vue/apollo-composable'
import { automateFunctionsPagePaginationQuery } from '~/lib/automate/graphql/queries'
import type { CreateAutomationSelectableFunction } from '~/lib/automate/helpers/automations'
import { usePaginatedQuery } from '~/lib/common/composables/graphql'
import {
usePageQueryStandardFetchPolicy,
usePaginatedQuery
} from '~/lib/common/composables/graphql'
import { graphql } from '~/lib/common/generated/gql'

definePageMeta({
Expand All @@ -42,14 +45,15 @@ const pageQuery = graphql(`
`)

const search = ref('')
const pageFetchPolicy = usePageQueryStandardFetchPolicy()
const { result, loading: pageQueryLoading } = useQuery(
pageQuery,
() => ({
search: search.value?.length ? search.value : null
}),
{
fetchPolicy: 'cache-and-network'
}
() => ({
fetchPolicy: pageFetchPolicy.value
})
)

const {
Expand All @@ -69,9 +73,9 @@ const {
cursor
}),
resolveCursorFromVariables: (vars) => vars.cursor,
options: {
fetchPolicy: 'cache-and-network'
}
options: () => ({
fetchPolicy: pageFetchPolicy.value
})
})

const showNewAutomationDialog = ref(false)
Expand Down
3 changes: 2 additions & 1 deletion packages/frontend-2/pages/projects/[id]/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ const projectId = computed(() => route.params.id as string)
const shouldAutoAcceptInvite = computed(() => route.query.accept === 'true')
const token = computed(() => route.query.token as Optional<string>)

const pageFetchPolicy = usePageQueryStandardFetchPolicy()
useGeneralProjectPageUpdateTracking({ projectId }, { notifyOnProjectUpdate: true })
const { result: projectPageResult } = useQuery(
projectPageQuery,
Expand All @@ -82,7 +83,7 @@ const { result: projectPageResult } = useQuery(
...(token.value?.length ? { token: token.value } : {})
}),
() => ({
fetchPolicy: 'cache-and-network',
fetchPolicy: pageFetchPolicy.value,
// Custom error policy so that a failing invitedTeam resolver (due to access rights)
// doesn't kill the entire query
errorPolicy: 'all'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ graphql(`
}
`)

const pageFetchPolicy = usePageQueryStandardFetchPolicy()
const route = useRoute()
const projectId = computed(() => route.params.id as string)
const automationId = computed(() => route.params.aid as string)
Expand All @@ -53,9 +54,9 @@ const { result, loading } = useQuery(
projectId: projectId.value,
automationId: automationId.value
}),
{
fetchPolicy: 'cache-and-network'
}
() => ({
fetchPolicy: pageFetchPolicy.value
})
)
const automation = computed(() => result.value?.project.automation || null)
const project = computed(() => result.value?.project)
Expand Down
2 changes: 1 addition & 1 deletion packages/server/.env-example
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ REDIS_URL="redis://127.0.0.1:6379"
############################################################

# Whether server is meant to be used with Frontend 2.0
USE_FRONTEND_2=false
USE_FRONTEND_2=true
FRONTEND_ORIGIN="http://127.0.0.1:8081"

# URL of a project on any FE2 speckle server that will be pulled in and used as the onboarding stream
Expand Down
10 changes: 8 additions & 2 deletions packages/ui-components/src/components/common/loading/Bar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,20 @@
<div
:class="[
'relative w-full h-1 bg-blue-500/30 text-xs text-foreground-on-primary overflow-hidden rounded-xl',
loading ? 'opacity-100' : 'opacity-0'
showBar ? 'opacity-100' : 'opacity-0'
]"
>
<div class="swoosher relative top-0 bg-blue-500/50"></div>
</div>
</template>
<script setup lang="ts">
defineProps<{ loading: boolean }>()
import { useMounted } from '@vueuse/core'
import { computed } from 'vue'

const props = defineProps<{ loading: boolean; clientOnly?: boolean }>()

const mounted = useMounted()
const showBar = computed(() => (mounted.value || !props.clientOnly) && props.loading)
</script>
<style scoped>
.swoosher {
Expand Down
11 changes: 6 additions & 5 deletions utils/eslint-projectwide.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,15 @@ const resolvePackageContexts = async (absoluteFileNames) => {
throw new Error(`File ${absoluteFileName} does not belong to any package`)
}

if (!contexts.has(pkg.absolutePath)) {
contexts.set(pkg, new Set())
const contextsKey = pkg.absolutePath
if (!contexts.has(contextsKey)) {
contexts.set(contextsKey, new Set())
}
contexts.get(pkg).add(absoluteFileName)
contexts.get(contextsKey).add(absoluteFileName)
}

return [...contexts.entries()].map(([pkg, files]) => ({
absolutePath: pkg.absolutePath,
return [...contexts.entries()].map(([pkgPath, files]) => ({
absolutePath: pkgPath,
files: [...files]
}))
}
Expand Down