[WBX-31] Make account modal less annoying in the viewer (#2009)
* Changes settings card to team when logged out * Speckle Logo to Marketing Site when logged out * Add Signin button to desktop header * Remove click counter - Launch modal instead * Fix webhook being in Settings dialog * Change team icon * Changes from Benjamins Comments * Remove Join the conversation * Move dialogs to parent * Move loginUrl logic to parent * Small fixes * Default active to true in LogoBlock * Simplify HeaderLogoBlock for this ticket. Change type from CR
This commit is contained in:
Родитель
c727669be4
Коммит
ec95ebdfb3
|
@ -1,28 +1,35 @@
|
|||
<template>
|
||||
<NuxtLink class="flex items-center shrink-0" to="/">
|
||||
<NuxtLink class="flex items-center shrink-0" :to="to" :target="target">
|
||||
<img
|
||||
class="block h-6 w-6"
|
||||
:class="{ 'mr-2': !minimal, grayscale: active }"
|
||||
class="h-8 w-8 block"
|
||||
:class="{
|
||||
grayscale: active
|
||||
}"
|
||||
src="~~/assets/images/speckle_logo_big.png"
|
||||
alt="Speckle"
|
||||
/>
|
||||
|
||||
<div
|
||||
v-if="!minimal"
|
||||
class="text-primary h6 mt-0 hidden font-bold leading-7 md:flex"
|
||||
class="text-primary text-base mt-0 font-bold leading-7"
|
||||
:class="showTextOnMobile ? '' : 'hidden md:flex'"
|
||||
>
|
||||
Speckle
|
||||
</div>
|
||||
</NuxtLink>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
defineProps({
|
||||
minimal: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
active: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
minimal?: boolean
|
||||
active?: boolean
|
||||
to?: string
|
||||
showTextOnMobile?: boolean
|
||||
target?: string
|
||||
}>(),
|
||||
{
|
||||
active: true,
|
||||
to: '/'
|
||||
}
|
||||
})
|
||||
)
|
||||
</script>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<nav class="fixed z-20 top-0 h-14 bg-foundation shadow hover:shadow-md transition">
|
||||
<div class="flex items-center justify-between h-full w-screen px-4">
|
||||
<div class="flex items-center truncate">
|
||||
<HeaderLogoBlock :active="false" />
|
||||
<HeaderLogoBlock :active="false" to="/" />
|
||||
<HeaderNavLink
|
||||
to="/"
|
||||
name="Dashboard"
|
||||
|
@ -17,8 +17,17 @@
|
|||
<PortalTarget name="primary-actions"></PortalTarget>
|
||||
<!-- Notifications dropdown -->
|
||||
<HeaderNavNotifications />
|
||||
<FormButton
|
||||
v-if="!activeUser"
|
||||
:to="loginUrl.fullPath"
|
||||
color="invert"
|
||||
class="hidden md:flex"
|
||||
size="sm"
|
||||
>
|
||||
Sign In
|
||||
</FormButton>
|
||||
<!-- Profile dropdown -->
|
||||
<HeaderNavUserMenu />
|
||||
<HeaderNavUserMenu :login-url="loginUrl" />
|
||||
</div>
|
||||
</div>
|
||||
<PopupsSignIn v-if="!activeUser" />
|
||||
|
@ -28,5 +37,21 @@
|
|||
</template>
|
||||
<script setup lang="ts">
|
||||
import { useActiveUser } from '~~/lib/auth/composables/activeUser'
|
||||
import { loginRoute } from '~~/lib/common/helpers/route'
|
||||
import type { Optional } from '@speckle/shared'
|
||||
|
||||
const { activeUser } = useActiveUser()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const token = computed(() => route.query.token as Optional<string>)
|
||||
|
||||
const loginUrl = computed(() =>
|
||||
router.resolve({
|
||||
path: loginRoute,
|
||||
query: {
|
||||
token: token.value || undefined
|
||||
}
|
||||
})
|
||||
)
|
||||
</script>
|
||||
|
|
|
@ -105,7 +105,7 @@
|
|||
Sign Out
|
||||
</NuxtLink>
|
||||
</MenuItem>
|
||||
<MenuItem v-if="!activeUser" v-slot="{ active }">
|
||||
<MenuItem v-if="!activeUser && loginUrl" v-slot="{ active }">
|
||||
<NuxtLink
|
||||
:class="[
|
||||
active ? 'bg-foundation-focus' : '',
|
||||
|
@ -143,23 +143,24 @@ import {
|
|||
ChatBubbleLeftRightIcon
|
||||
} from '@heroicons/vue/24/outline'
|
||||
import { Roles } from '@speckle/shared'
|
||||
import type { Optional } from '@speckle/shared'
|
||||
import { useActiveUser } from '~~/lib/auth/composables/activeUser'
|
||||
import { useAuthManager } from '~~/lib/auth/composables/auth'
|
||||
import { loginRoute } from '~~/lib/common/helpers/route'
|
||||
import { useTheme, AppTheme } from '~~/lib/core/composables/theme'
|
||||
import { useServerInfo } from '~/lib/core/composables/server'
|
||||
import type { RouteLocationRaw } from 'vue-router'
|
||||
|
||||
defineProps<{
|
||||
loginUrl?: RouteLocationRaw
|
||||
}>()
|
||||
|
||||
const { logout } = useAuthManager()
|
||||
const { activeUser, isGuest } = useActiveUser()
|
||||
const { isDarkTheme, setTheme } = useTheme()
|
||||
const { serverInfo } = useServerInfo()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const showInviteDialog = ref(false)
|
||||
const showProfileEditDialog = ref(false)
|
||||
const token = computed(() => route.query.token as Optional<string>)
|
||||
|
||||
const Icon = computed(() => (isDarkTheme.value ? SunIcon : MoonIcon))
|
||||
const version = computed(() => serverInfo.value?.version)
|
||||
|
@ -185,13 +186,4 @@ const goToConnectors = () => {
|
|||
const goToServerManagement = () => {
|
||||
router.push('/server-management')
|
||||
}
|
||||
|
||||
const loginUrl = computed(() =>
|
||||
router.resolve({
|
||||
path: loginRoute,
|
||||
query: {
|
||||
token: token.value || undefined
|
||||
}
|
||||
})
|
||||
)
|
||||
</script>
|
||||
|
|
|
@ -4,21 +4,12 @@
|
|||
dialog-mode
|
||||
max-width="sm"
|
||||
subtitle="Create a free account to keep using Speckle!"
|
||||
:hide-closer="dialogOpenCount >= 3"
|
||||
:prevent-close-on-click-outside="dialogOpenCount >= 3"
|
||||
/>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { useActiveUser } from '~~/lib/auth/composables/activeUser'
|
||||
import { debounce } from 'lodash-es'
|
||||
|
||||
const { activeUser } = useActiveUser()
|
||||
const logger = useLogger()
|
||||
const route = useRoute()
|
||||
|
||||
const showDialogBase = ref(false)
|
||||
const clickyCounts = ref(0)
|
||||
let clicksToOpenDialog = 6
|
||||
|
||||
const showDialog = computed({
|
||||
get: () => showDialogBase.value,
|
||||
|
@ -30,46 +21,4 @@ const showDialog = computed({
|
|||
showDialogBase.value = newVal
|
||||
}
|
||||
})
|
||||
|
||||
watch(clickyCounts, (newVal) => {
|
||||
if (activeUser.value) return
|
||||
if (newVal < clicksToOpenDialog) return
|
||||
clickyCounts.value = 0
|
||||
showDialog.value = true
|
||||
clicksToOpenDialog *= 2
|
||||
})
|
||||
|
||||
const countClicks = () => {
|
||||
logger.debug({
|
||||
clickyCounts: clickyCounts.value,
|
||||
clicksToOpenDialog,
|
||||
dialogOpenCount: dialogOpenCount.value
|
||||
})
|
||||
clickyCounts.value++
|
||||
}
|
||||
|
||||
const debouncedCounter = debounce(countClicks, 100)
|
||||
|
||||
// After three dialog opens, we disallow its closing
|
||||
const dialogOpenCount = ref(0)
|
||||
watch(showDialog, (newVal) => {
|
||||
if (!newVal) return
|
||||
dialogOpenCount.value++
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
if (activeUser.value) return
|
||||
// Note: not using the vue use equivalent as it does not detect viewer clicks
|
||||
// Note: viewer eevnt handlers seem to prevent default on right clicks
|
||||
document.addEventListener('click', countClicks)
|
||||
document.addEventListener('touchstart', countClicks)
|
||||
document.addEventListener('scroll', debouncedCounter)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (activeUser.value) return
|
||||
document.removeEventListener('click', countClicks)
|
||||
document.removeEventListener('touchstart', countClicks)
|
||||
document.removeEventListener('scroll', debouncedCounter)
|
||||
})
|
||||
</script>
|
||||
|
|
|
@ -3,10 +3,17 @@
|
|||
<template #top>
|
||||
<div class="flex items-center">
|
||||
<div class="flex items-center justify-between w-full">
|
||||
<div class="flex items-center gap-0.5 flex-grow select-none">
|
||||
<div
|
||||
v-if="activeUser"
|
||||
class="flex items-center gap-0.5 flex-grow select-none"
|
||||
>
|
||||
<Cog6ToothIcon class="h-5 w-5" />
|
||||
<span class="text-sm">Settings</span>
|
||||
</div>
|
||||
<div v-else class="flex items-center gap-0.5 flex-grow select-none">
|
||||
<UsersIcon class="h-5 w-5" />
|
||||
<span class="text-sm">Team</span>
|
||||
</div>
|
||||
<div class="flex items-center text-xs">
|
||||
{{ project.role?.split(':').reverse()[0] }}
|
||||
</div>
|
||||
|
@ -16,7 +23,7 @@
|
|||
<template #bottom>
|
||||
<div class="flex items-center justify-between mt-3">
|
||||
<UserAvatarGroup :users="teamUsers" class="max-w-[104px]" />
|
||||
<div>
|
||||
<div v-if="activeUser">
|
||||
<FormButton class="ml-2" @click="dialogOpen = true">
|
||||
{{ project.role === 'stream:owner' ? 'Manage' : 'View' }}
|
||||
</FormButton>
|
||||
|
@ -29,9 +36,11 @@
|
|||
</ProjectPageStatsBlock>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { Cog6ToothIcon } from '@heroicons/vue/24/outline'
|
||||
import { Cog6ToothIcon, UsersIcon } from '@heroicons/vue/24/outline'
|
||||
import { useActiveUser } from '~~/lib/auth/composables/activeUser'
|
||||
import { graphql } from '~~/lib/common/generated/gql'
|
||||
import type { ProjectPageStatsBlockTeamFragment } from '~~/lib/common/generated/gql/graphql'
|
||||
const { activeUser } = useActiveUser()
|
||||
|
||||
graphql(`
|
||||
fragment ProjectPageStatsBlockTeam on Project {
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
:project="project"
|
||||
/>
|
||||
<ProjectPageTeamDialogManagePermissions :project="project" />
|
||||
<ProjectPageTeamDialogWebhooks :project="project" />
|
||||
<ProjectPageTeamDialogWebhooks v-if="isOwner" :project="project" />
|
||||
<ProjectPageTeamDialogDangerZones
|
||||
v-if="isOwner || canLeaveProject"
|
||||
:project="project"
|
||||
|
|
|
@ -5,10 +5,11 @@
|
|||
>
|
||||
<!-- Add new thread bubble -->
|
||||
<ViewerAnchoredPointNewThread
|
||||
v-if="canPostComment"
|
||||
v-model="buttonState"
|
||||
:can-post-comment="canPostComment"
|
||||
class="z-[13]"
|
||||
@close="closeNewThread"
|
||||
@login="showLoginDialog = true"
|
||||
/>
|
||||
|
||||
<!-- Comment bubbles -->
|
||||
|
@ -22,6 +23,7 @@
|
|||
@update:expanded="onThreadExpandedChange"
|
||||
@next="(model) => openNextThread(model)"
|
||||
@prev="(model) => openPrevThread(model)"
|
||||
@login="showLoginDialog = true"
|
||||
/>
|
||||
|
||||
<!-- Active users -->
|
||||
|
@ -32,6 +34,13 @@
|
|||
class="z-[10]"
|
||||
/>
|
||||
|
||||
<AuthLoginPanel
|
||||
v-model:open="showLoginDialog"
|
||||
dialog-mode
|
||||
max-width="sm"
|
||||
subtitle="Join the conversation"
|
||||
/>
|
||||
|
||||
<!-- Active user avatars in navbar -->
|
||||
<Portal to="secondary-actions">
|
||||
<ViewerScope :state="state">
|
||||
|
@ -139,6 +148,8 @@ const {
|
|||
}
|
||||
} = useInjectedViewerInterfaceState()
|
||||
|
||||
const showLoginDialog = ref(false)
|
||||
|
||||
useViewerCommentBubblesProjection({ parentEl })
|
||||
|
||||
const { buttonState, closeNewThread } = useViewerNewThreadBubble({
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
</button>
|
||||
</div>
|
||||
<div
|
||||
v-if="modelValue.isExpanded"
|
||||
v-if="modelValue.isExpanded && canPostComment"
|
||||
ref="threadContainer"
|
||||
class="sm:absolute min-w-[200px] hover:bg-foundation transition bg-white/80 dark:bg-neutral-800/90 dark:hover:bg-neutral-800 backdrop-blur-sm sm:rounded-lg shadow-md"
|
||||
>
|
||||
|
@ -91,10 +91,12 @@ import { useThreadUtilities } from '~~/lib/viewer/composables/ui'
|
|||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', v: ViewerNewThreadBubbleModel): void
|
||||
(e: 'close'): void
|
||||
(e: 'login'): void
|
||||
}>()
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: ViewerNewThreadBubbleModel
|
||||
canPostComment?: Nullable<boolean>
|
||||
}>()
|
||||
|
||||
const { onKeyDownHandler, updateIsTyping, pauseAutomaticUpdates } =
|
||||
|
@ -114,6 +116,12 @@ const createThread = useSubmitComment()
|
|||
|
||||
const onThreadClick = () => {
|
||||
const newIsExpanded = !props.modelValue.isExpanded
|
||||
|
||||
if (!props.canPostComment) {
|
||||
emit('login')
|
||||
return
|
||||
}
|
||||
|
||||
if (!newIsExpanded) {
|
||||
updateIsTyping(false)
|
||||
}
|
||||
|
|
|
@ -151,6 +151,12 @@
|
|||
:model-value="modelValue"
|
||||
@submit="onNewReply"
|
||||
/>
|
||||
<div
|
||||
v-if="!canReply"
|
||||
class="p-3 flex flex-col items-center justify-center bg-foundation-2"
|
||||
>
|
||||
<FormButton full-width @click="$emit('login')">Reply</FormButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ViewerCommentsPortalOrDiv>
|
||||
|
@ -201,6 +207,7 @@ const emit = defineEmits<{
|
|||
(e: 'update:expanded', v: boolean): void
|
||||
(e: 'next', v: CommentBubbleModel): void
|
||||
(e: 'prev', v: CommentBubbleModel): void
|
||||
(e: 'login'): void
|
||||
}>()
|
||||
|
||||
const props = defineProps<{
|
||||
|
|
Загрузка…
Ссылка в новой задаче