Compare commits
9 Commits
feat/node-
...
fix/defaul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
850954efdf | ||
|
|
bf65654126 | ||
|
|
27ea170d85 | ||
|
|
6cf0c427a7 | ||
|
|
b7a3ae231b | ||
|
|
f8ac6dfeb1 | ||
|
|
7d2d335b37 | ||
|
|
76875db865 | ||
|
|
96e2d87b57 |
@@ -1,4 +1,4 @@
|
|||||||
FROM node:18.8.0 AS builder
|
FROM node:20.12.2 AS builder
|
||||||
|
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
|||||||
@@ -121,6 +121,7 @@ describe('AdminService', () => {
|
|||||||
NOT: {
|
NOT: {
|
||||||
inviteeEmail: {
|
inviteeEmail: {
|
||||||
in: [dbAdminUsers[0].email],
|
in: [dbAdminUsers[0].email],
|
||||||
|
mode: 'insensitive',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -229,7 +230,10 @@ describe('AdminService', () => {
|
|||||||
|
|
||||||
expect(mockPrisma.invitedUsers.deleteMany).toHaveBeenCalledWith({
|
expect(mockPrisma.invitedUsers.deleteMany).toHaveBeenCalledWith({
|
||||||
where: {
|
where: {
|
||||||
inviteeEmail: { in: [invitedUsers[0].inviteeEmail] },
|
inviteeEmail: {
|
||||||
|
in: [invitedUsers[0].inviteeEmail],
|
||||||
|
mode: 'insensitive',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(result).toEqualRight(true);
|
expect(result).toEqualRight(true);
|
||||||
|
|||||||
@@ -89,12 +89,17 @@ export class AdminService {
|
|||||||
adminEmail: string,
|
adminEmail: string,
|
||||||
inviteeEmail: string,
|
inviteeEmail: string,
|
||||||
) {
|
) {
|
||||||
if (inviteeEmail == adminEmail) return E.left(DUPLICATE_EMAIL);
|
if (inviteeEmail.toLowerCase() == adminEmail.toLowerCase()) {
|
||||||
|
return E.left(DUPLICATE_EMAIL);
|
||||||
|
}
|
||||||
if (!validateEmail(inviteeEmail)) return E.left(INVALID_EMAIL);
|
if (!validateEmail(inviteeEmail)) return E.left(INVALID_EMAIL);
|
||||||
|
|
||||||
const alreadyInvitedUser = await this.prisma.invitedUsers.findFirst({
|
const alreadyInvitedUser = await this.prisma.invitedUsers.findFirst({
|
||||||
where: {
|
where: {
|
||||||
inviteeEmail: inviteeEmail,
|
inviteeEmail: {
|
||||||
|
equals: inviteeEmail,
|
||||||
|
mode: 'insensitive',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (alreadyInvitedUser != null) return E.left(USER_ALREADY_INVITED);
|
if (alreadyInvitedUser != null) return E.left(USER_ALREADY_INVITED);
|
||||||
@@ -159,7 +164,7 @@ export class AdminService {
|
|||||||
try {
|
try {
|
||||||
await this.prisma.invitedUsers.deleteMany({
|
await this.prisma.invitedUsers.deleteMany({
|
||||||
where: {
|
where: {
|
||||||
inviteeEmail: { in: inviteeEmails },
|
inviteeEmail: { in: inviteeEmails, mode: 'insensitive' },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return E.right(true);
|
return E.right(true);
|
||||||
@@ -189,6 +194,7 @@ export class AdminService {
|
|||||||
NOT: {
|
NOT: {
|
||||||
inviteeEmail: {
|
inviteeEmail: {
|
||||||
in: userEmailObjs.map((user) => user.email),
|
in: userEmailObjs.map((user) => user.email),
|
||||||
|
mode: 'insensitive',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -299,7 +299,10 @@ export class ShortcodeService implements UserDataHandler, OnModuleInit {
|
|||||||
where: userEmail
|
where: userEmail
|
||||||
? {
|
? {
|
||||||
User: {
|
User: {
|
||||||
email: userEmail,
|
email: {
|
||||||
|
equals: userEmail,
|
||||||
|
mode: 'insensitive',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
|
|||||||
@@ -75,12 +75,13 @@ export class TeamInvitationService {
|
|||||||
if (!isEmailValid) return E.left(INVALID_EMAIL);
|
if (!isEmailValid) return E.left(INVALID_EMAIL);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const teamInvite = await this.prisma.teamInvitation.findUniqueOrThrow({
|
const teamInvite = await this.prisma.teamInvitation.findFirstOrThrow({
|
||||||
where: {
|
where: {
|
||||||
teamID_inviteeEmail: {
|
inviteeEmail: {
|
||||||
inviteeEmail: inviteeEmail,
|
equals: inviteeEmail,
|
||||||
teamID: teamID,
|
mode: 'insensitive',
|
||||||
},
|
},
|
||||||
|
teamID,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ beforeEach(() => {
|
|||||||
describe('UserService', () => {
|
describe('UserService', () => {
|
||||||
describe('findUserByEmail', () => {
|
describe('findUserByEmail', () => {
|
||||||
test('should successfully return a valid user given a valid email', async () => {
|
test('should successfully return a valid user given a valid email', async () => {
|
||||||
mockPrisma.user.findUniqueOrThrow.mockResolvedValueOnce(user);
|
mockPrisma.user.findFirst.mockResolvedValueOnce(user);
|
||||||
|
|
||||||
const result = await userService.findUserByEmail(
|
const result = await userService.findUserByEmail(
|
||||||
'dwight@dundermifflin.com',
|
'dwight@dundermifflin.com',
|
||||||
@@ -158,7 +158,7 @@ describe('UserService', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should return a null user given a invalid email', async () => {
|
test('should return a null user given a invalid email', async () => {
|
||||||
mockPrisma.user.findUniqueOrThrow.mockRejectedValueOnce('NotFoundError');
|
mockPrisma.user.findFirst.mockResolvedValueOnce(null);
|
||||||
|
|
||||||
const result = await userService.findUserByEmail('jim@dundermifflin.com');
|
const result = await userService.findUserByEmail('jim@dundermifflin.com');
|
||||||
expect(result).resolves.toBeNone;
|
expect(result).resolves.toBeNone;
|
||||||
|
|||||||
@@ -62,16 +62,16 @@ export class UserService {
|
|||||||
* @returns Option of found User
|
* @returns Option of found User
|
||||||
*/
|
*/
|
||||||
async findUserByEmail(email: string): Promise<O.None | O.Some<AuthUser>> {
|
async findUserByEmail(email: string): Promise<O.None | O.Some<AuthUser>> {
|
||||||
try {
|
const user = await this.prisma.user.findFirst({
|
||||||
const user = await this.prisma.user.findUniqueOrThrow({
|
where: {
|
||||||
where: {
|
email: {
|
||||||
email: email,
|
equals: email,
|
||||||
|
mode: 'insensitive',
|
||||||
},
|
},
|
||||||
});
|
},
|
||||||
return O.some(user);
|
});
|
||||||
} catch (error) {
|
if (!user) return O.none;
|
||||||
return O.none;
|
return O.some(user);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -43,12 +43,19 @@
|
|||||||
@click="invokeAction('modals.support.toggle')"
|
@click="invokeAction('modals.support.toggle')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex">
|
<div
|
||||||
|
class="flex"
|
||||||
|
:class="{
|
||||||
|
'flex-row-reverse gap-2':
|
||||||
|
workspaceSelectorFlagEnabled && !currentUser,
|
||||||
|
}"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
v-if="currentUser === null"
|
v-if="currentUser === null"
|
||||||
class="inline-flex items-center space-x-2"
|
class="inline-flex items-center space-x-2"
|
||||||
>
|
>
|
||||||
<HoppButtonSecondary
|
<HoppButtonSecondary
|
||||||
|
v-if="!workspaceSelectorFlagEnabled"
|
||||||
:icon="IconUploadCloud"
|
:icon="IconUploadCloud"
|
||||||
:label="t('header.save_workspace')"
|
:label="t('header.save_workspace')"
|
||||||
class="!focus-visible:text-emerald-600 !hover:text-emerald-600 hidden h-8 border border-emerald-600/25 bg-emerald-500/10 !text-emerald-500 hover:border-emerald-600/20 hover:bg-emerald-600/20 focus-visible:border-emerald-600/20 focus-visible:bg-emerald-600/20 md:flex"
|
class="!focus-visible:text-emerald-600 !hover:text-emerald-600 hidden h-8 border border-emerald-600/25 bg-emerald-500/10 !text-emerald-500 hover:border-emerald-600/20 hover:bg-emerald-600/20 focus-visible:border-emerald-600/20 focus-visible:bg-emerald-600/20 md:flex"
|
||||||
@@ -60,18 +67,22 @@
|
|||||||
@click="invokeAction('modals.login.toggle')"
|
@click="invokeAction('modals.login.toggle')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="inline-flex items-center space-x-2">
|
<TeamsMemberStack
|
||||||
<TeamsMemberStack
|
v-else-if="
|
||||||
v-if="
|
currentUser !== null &&
|
||||||
workspace.type === 'team' &&
|
workspace.type === 'team' &&
|
||||||
selectedTeam &&
|
selectedTeam &&
|
||||||
selectedTeam.teamMembers.length > 1
|
selectedTeam.teamMembers.length > 1
|
||||||
"
|
"
|
||||||
:team-members="selectedTeam.teamMembers"
|
:team-members="selectedTeam.teamMembers"
|
||||||
show-count
|
show-count
|
||||||
class="mx-2"
|
class="mx-2"
|
||||||
@handle-click="handleTeamEdit()"
|
@handle-click="handleTeamEdit()"
|
||||||
/>
|
/>
|
||||||
|
<div
|
||||||
|
v-if="workspaceSelectorFlagEnabled || currentUser"
|
||||||
|
class="inline-flex items-center space-x-2"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
class="flex h-8 divide-x divide-emerald-600/25 rounded border border-emerald-600/25 bg-emerald-500/10 focus-within:divide-emerald-600/20 focus-within:border-emerald-600/20 focus-within:bg-emerald-600/20 hover:divide-emerald-600/20 hover:border-emerald-600/20 hover:bg-emerald-600/20"
|
class="flex h-8 divide-x divide-emerald-600/25 rounded border border-emerald-600/25 bg-emerald-500/10 focus-within:divide-emerald-600/20 focus-within:border-emerald-600/20 focus-within:bg-emerald-600/20 hover:divide-emerald-600/20 hover:border-emerald-600/20 hover:bg-emerald-600/20"
|
||||||
>
|
>
|
||||||
@@ -84,6 +95,7 @@
|
|||||||
/>
|
/>
|
||||||
<HoppButtonSecondary
|
<HoppButtonSecondary
|
||||||
v-if="
|
v-if="
|
||||||
|
currentUser &&
|
||||||
workspace.type === 'team' &&
|
workspace.type === 'team' &&
|
||||||
selectedTeam &&
|
selectedTeam &&
|
||||||
selectedTeam?.myRole === 'OWNER'
|
selectedTeam?.myRole === 'OWNER'
|
||||||
@@ -124,7 +136,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</tippy>
|
</tippy>
|
||||||
<span class="px-2">
|
<span v-if="currentUser" class="px-2">
|
||||||
<tippy
|
<tippy
|
||||||
interactive
|
interactive
|
||||||
trigger="click"
|
trigger="click"
|
||||||
@@ -259,6 +271,13 @@ import {
|
|||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Feature flag to enable the workspace selector login conversion
|
||||||
|
*/
|
||||||
|
const workspaceSelectorFlagEnabled = computed(
|
||||||
|
() => !!platform.platformFeatureFlags.workspaceSwitcherLogin?.value
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Once the PWA code is initialized, this holds a method
|
* Once the PWA code is initialized, this holds a method
|
||||||
* that can be called to show the user the installation
|
* that can be called to show the user the installation
|
||||||
@@ -380,6 +399,8 @@ const inviteTeam = (team: { name: string }, teamID: string) => {
|
|||||||
|
|
||||||
// Show the workspace selected team invite modal if the user is an owner of the team else show the default invite modal
|
// Show the workspace selected team invite modal if the user is an owner of the team else show the default invite modal
|
||||||
const handleInvite = () => {
|
const handleInvite = () => {
|
||||||
|
if (!currentUser.value) return invokeAction("modals.login.toggle")
|
||||||
|
|
||||||
if (
|
if (
|
||||||
workspace.value.type === "team" &&
|
workspace.value.type === "team" &&
|
||||||
workspace.value.teamID &&
|
workspace.value.teamID &&
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col space-y-2">
|
<div class="flex flex-col space-y-2">
|
||||||
<div class="flex flex-col px-4 pt-2">
|
<div v-if="isTooltipComponent" class="flex flex-col px-4 pt-2">
|
||||||
<h2 class="inline-flex pb-1 font-semibold text-secondaryDark">
|
<h2 class="inline-flex pb-1 font-semibold text-secondaryDark">
|
||||||
{{ t("settings.interceptor") }}
|
{{ t("settings.interceptor") }}
|
||||||
</h2>
|
</h2>
|
||||||
@@ -19,6 +19,9 @@
|
|||||||
:value="interceptor.interceptorID"
|
:value="interceptor.interceptorID"
|
||||||
:label="unref(interceptor.name(t))"
|
:label="unref(interceptor.name(t))"
|
||||||
:selected="interceptorSelection === interceptor.interceptorID"
|
:selected="interceptorSelection === interceptor.interceptorID"
|
||||||
|
:class="{
|
||||||
|
'!px-0 hover:bg-transparent': !isTooltipComponent,
|
||||||
|
}"
|
||||||
@change="interceptorSelection = interceptor.interceptorID"
|
@change="interceptorSelection = interceptor.interceptorID"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -39,6 +42,15 @@ import { InterceptorService } from "~/services/interceptor.service"
|
|||||||
|
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
|
|
||||||
|
withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
isTooltipComponent?: boolean
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
isTooltipComponent: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const interceptorService = useService(InterceptorService)
|
const interceptorService = useService(InterceptorService)
|
||||||
|
|
||||||
const interceptorSelection =
|
const interceptorSelection =
|
||||||
|
|||||||
@@ -36,16 +36,6 @@
|
|||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="space-y-4 py-4">
|
|
||||||
<div class="flex items-center">
|
|
||||||
<HoppSmartToggle
|
|
||||||
:on="extensionEnabled"
|
|
||||||
@change="extensionEnabled = !extensionEnabled"
|
|
||||||
>
|
|
||||||
{{ t("settings.extensions_use_toggle") }}
|
|
||||||
</HoppSmartToggle>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@@ -55,34 +45,12 @@ import IconCheckCircle from "~icons/lucide/check-circle"
|
|||||||
import { useI18n } from "@composables/i18n"
|
import { useI18n } from "@composables/i18n"
|
||||||
import { ExtensionInterceptorService } from "~/platform/std/interceptors/extension"
|
import { ExtensionInterceptorService } from "~/platform/std/interceptors/extension"
|
||||||
import { useService } from "dioc/vue"
|
import { useService } from "dioc/vue"
|
||||||
import { computed } from "vue"
|
|
||||||
import { InterceptorService } from "~/services/interceptor.service"
|
|
||||||
import { platform } from "~/platform"
|
|
||||||
|
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
|
|
||||||
const interceptorService = useService(InterceptorService)
|
|
||||||
const extensionService = useService(ExtensionInterceptorService)
|
const extensionService = useService(ExtensionInterceptorService)
|
||||||
|
|
||||||
const extensionVersion = extensionService.extensionVersion
|
const extensionVersion = extensionService.extensionVersion
|
||||||
const hasChromeExtInstalled = extensionService.chromeExtensionInstalled
|
const hasChromeExtInstalled = extensionService.chromeExtensionInstalled
|
||||||
const hasFirefoxExtInstalled = extensionService.firefoxExtensionInstalled
|
const hasFirefoxExtInstalled = extensionService.firefoxExtensionInstalled
|
||||||
|
|
||||||
const extensionEnabled = computed({
|
|
||||||
get() {
|
|
||||||
return (
|
|
||||||
interceptorService.currentInterceptorID.value ===
|
|
||||||
extensionService.interceptorID
|
|
||||||
)
|
|
||||||
},
|
|
||||||
set(active) {
|
|
||||||
if (active) {
|
|
||||||
interceptorService.currentInterceptorID.value =
|
|
||||||
extensionService.interceptorID
|
|
||||||
} else {
|
|
||||||
interceptorService.currentInterceptorID.value =
|
|
||||||
platform.interceptors.default
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -8,16 +8,6 @@
|
|||||||
:label="t('app.proxy_privacy_policy')"
|
:label="t('app.proxy_privacy_policy')"
|
||||||
/>.
|
/>.
|
||||||
</div>
|
</div>
|
||||||
<div class="space-y-4 py-4">
|
|
||||||
<div class="flex items-center">
|
|
||||||
<HoppSmartToggle
|
|
||||||
:on="proxyEnabled"
|
|
||||||
@change="proxyEnabled = !proxyEnabled"
|
|
||||||
>
|
|
||||||
{{ t("settings.proxy_use_toggle") }}
|
|
||||||
</HoppSmartToggle>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center space-x-2 py-4">
|
<div class="flex items-center space-x-2 py-4">
|
||||||
<HoppSmartInput
|
<HoppSmartInput
|
||||||
v-model="PROXY_URL"
|
v-model="PROXY_URL"
|
||||||
@@ -50,7 +40,6 @@ import { computed } from "vue"
|
|||||||
import { useService } from "dioc/vue"
|
import { useService } from "dioc/vue"
|
||||||
import { InterceptorService } from "~/services/interceptor.service"
|
import { InterceptorService } from "~/services/interceptor.service"
|
||||||
import { proxyInterceptor } from "~/platform/std/interceptors/proxy"
|
import { proxyInterceptor } from "~/platform/std/interceptors/proxy"
|
||||||
import { platform } from "~/platform"
|
|
||||||
|
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
@@ -59,23 +48,11 @@ const interceptorService = useService(InterceptorService)
|
|||||||
|
|
||||||
const PROXY_URL = useSetting("PROXY_URL")
|
const PROXY_URL = useSetting("PROXY_URL")
|
||||||
|
|
||||||
const proxyEnabled = computed({
|
const proxyEnabled = computed(
|
||||||
get() {
|
() =>
|
||||||
return (
|
interceptorService.currentInterceptorID.value ===
|
||||||
interceptorService.currentInterceptorID.value ===
|
proxyInterceptor.interceptorID
|
||||||
proxyInterceptor.interceptorID
|
)
|
||||||
)
|
|
||||||
},
|
|
||||||
set(active) {
|
|
||||||
if (active) {
|
|
||||||
interceptorService.currentInterceptorID.value =
|
|
||||||
proxyInterceptor.interceptorID
|
|
||||||
} else {
|
|
||||||
interceptorService.currentInterceptorID.value =
|
|
||||||
platform.interceptors.default
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const clearIcon = refAutoReset<typeof IconRotateCCW | typeof IconCheck>(
|
const clearIcon = refAutoReset<typeof IconRotateCCW | typeof IconCheck>(
|
||||||
IconRotateCCW,
|
IconRotateCCW,
|
||||||
|
|||||||
@@ -59,7 +59,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="!loading && teamListAdapterError"
|
v-else-if="teamListAdapterError"
|
||||||
class="flex flex-col items-center py-4"
|
class="flex flex-col items-center py-4"
|
||||||
>
|
>
|
||||||
<icon-lucide-help-circle class="svg-icons mb-4" />
|
<icon-lucide-help-circle class="svg-icons mb-4" />
|
||||||
@@ -85,7 +85,7 @@ import { useColorMode } from "@composables/theming"
|
|||||||
import { GetMyTeamsQuery } from "~/helpers/backend/graphql"
|
import { GetMyTeamsQuery } from "~/helpers/backend/graphql"
|
||||||
import IconDone from "~icons/lucide/check"
|
import IconDone from "~icons/lucide/check"
|
||||||
import { useLocalState } from "~/newstore/localstate"
|
import { useLocalState } from "~/newstore/localstate"
|
||||||
import { defineActionHandler } from "~/helpers/actions"
|
import { defineActionHandler, invokeAction } from "~/helpers/actions"
|
||||||
import { WorkspaceService } from "~/services/workspace.service"
|
import { WorkspaceService } from "~/services/workspace.service"
|
||||||
import { useService } from "dioc/vue"
|
import { useService } from "dioc/vue"
|
||||||
import { useElementVisibility, useIntervalFn } from "@vueuse/core"
|
import { useElementVisibility, useIntervalFn } from "@vueuse/core"
|
||||||
@@ -157,8 +157,8 @@ const switchToTeamWorkspace = (team: GetMyTeamsQuery["myTeams"][number]) => {
|
|||||||
workspaceService.changeWorkspace({
|
workspaceService.changeWorkspace({
|
||||||
teamID: team.id,
|
teamID: team.id,
|
||||||
teamName: team.name,
|
teamName: team.name,
|
||||||
role: team.myRole,
|
|
||||||
type: "team",
|
type: "team",
|
||||||
|
role: team.myRole,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,12 +174,16 @@ watch(
|
|||||||
(user) => {
|
(user) => {
|
||||||
if (!user) {
|
if (!user) {
|
||||||
switchToPersonalWorkspace()
|
switchToPersonalWorkspace()
|
||||||
|
teamListadapter.dispose()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const displayModalAdd = (shouldDisplay: boolean) => {
|
const displayModalAdd = (shouldDisplay: boolean) => {
|
||||||
|
if (!currentUser.value) return invokeAction("modals.login.toggle")
|
||||||
|
|
||||||
showModalAdd.value = shouldDisplay
|
showModalAdd.value = shouldDisplay
|
||||||
|
teamListadapter.fetchList()
|
||||||
}
|
}
|
||||||
|
|
||||||
defineActionHandler("modals.team.new", () => {
|
defineActionHandler("modals.team.new", () => {
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ export default class TeamListAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public dispose() {
|
public dispose() {
|
||||||
|
this.teamList$.next([])
|
||||||
this.isDispose = true
|
this.isDispose = true
|
||||||
clearTimeout(this.timeoutHandle as any)
|
clearTimeout(this.timeoutHandle as any)
|
||||||
this.timeoutHandle = null
|
this.timeoutHandle = null
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import { cloneDeep, defaultsDeep, has } from "lodash-es"
|
import { cloneDeep, defaultsDeep, has } from "lodash-es"
|
||||||
import { Observable } from "rxjs"
|
import { Observable } from "rxjs"
|
||||||
import { distinctUntilChanged, pluck } from "rxjs/operators"
|
import { distinctUntilChanged, pluck } from "rxjs/operators"
|
||||||
import { nextTick } from "vue"
|
|
||||||
import { platform } from "~/platform"
|
|
||||||
import type { KeysMatching } from "~/types/ts-utils"
|
import type { KeysMatching } from "~/types/ts-utils"
|
||||||
import DispatchingStore, { defineDispatchers } from "./DispatchingStore"
|
import DispatchingStore, { defineDispatchers } from "./DispatchingStore"
|
||||||
|
|
||||||
@@ -70,63 +68,52 @@ export type SettingsDef = {
|
|||||||
HAS_OPENED_SPOTLIGHT: boolean
|
HAS_OPENED_SPOTLIGHT: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getDefaultSettings = (): SettingsDef => {
|
export const getDefaultSettings = (): SettingsDef => ({
|
||||||
const defaultSettings: SettingsDef = {
|
syncCollections: true,
|
||||||
syncCollections: true,
|
syncHistory: true,
|
||||||
syncHistory: true,
|
syncEnvironments: true,
|
||||||
syncEnvironments: true,
|
|
||||||
|
|
||||||
WRAP_LINES: {
|
WRAP_LINES: {
|
||||||
httpRequestBody: true,
|
httpRequestBody: true,
|
||||||
httpResponseBody: true,
|
httpResponseBody: true,
|
||||||
httpHeaders: true,
|
httpHeaders: true,
|
||||||
httpParams: true,
|
httpParams: true,
|
||||||
httpUrlEncoded: true,
|
httpUrlEncoded: true,
|
||||||
httpPreRequest: true,
|
httpPreRequest: true,
|
||||||
httpTest: true,
|
httpTest: true,
|
||||||
httpRequestVariables: true,
|
httpRequestVariables: true,
|
||||||
graphqlQuery: true,
|
graphqlQuery: true,
|
||||||
graphqlResponseBody: true,
|
graphqlResponseBody: true,
|
||||||
graphqlHeaders: false,
|
graphqlHeaders: false,
|
||||||
graphqlVariables: false,
|
graphqlVariables: false,
|
||||||
graphqlSchema: true,
|
graphqlSchema: true,
|
||||||
importCurl: true,
|
importCurl: true,
|
||||||
codeGen: true,
|
codeGen: true,
|
||||||
cookie: true,
|
cookie: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
CURRENT_INTERCEPTOR_ID: "",
|
// Set empty because interceptor module will set the default value
|
||||||
|
CURRENT_INTERCEPTOR_ID: "",
|
||||||
|
|
||||||
// TODO: Interceptor related settings should move under the interceptor systems
|
// TODO: Interceptor related settings should move under the interceptor systems
|
||||||
PROXY_URL: "https://proxy.hoppscotch.io/",
|
PROXY_URL: "https://proxy.hoppscotch.io/",
|
||||||
URL_EXCLUDES: {
|
URL_EXCLUDES: {
|
||||||
auth: true,
|
auth: true,
|
||||||
httpUser: true,
|
httpUser: true,
|
||||||
httpPassword: true,
|
httpPassword: true,
|
||||||
bearerToken: true,
|
bearerToken: true,
|
||||||
oauth2Token: true,
|
oauth2Token: true,
|
||||||
},
|
},
|
||||||
THEME_COLOR: "indigo",
|
THEME_COLOR: "indigo",
|
||||||
BG_COLOR: "system",
|
BG_COLOR: "system",
|
||||||
TELEMETRY_ENABLED: true,
|
TELEMETRY_ENABLED: true,
|
||||||
EXPAND_NAVIGATION: false,
|
EXPAND_NAVIGATION: false,
|
||||||
SIDEBAR: true,
|
SIDEBAR: true,
|
||||||
SIDEBAR_ON_LEFT: false,
|
SIDEBAR_ON_LEFT: false,
|
||||||
COLUMN_LAYOUT: true,
|
COLUMN_LAYOUT: true,
|
||||||
|
|
||||||
HAS_OPENED_SPOTLIGHT: false,
|
HAS_OPENED_SPOTLIGHT: false,
|
||||||
}
|
})
|
||||||
|
|
||||||
// Wait for platform to initialize before setting CURRENT_INTERCEPTOR_ID
|
|
||||||
nextTick(() => {
|
|
||||||
applySetting(
|
|
||||||
"CURRENT_INTERCEPTOR_ID",
|
|
||||||
platform?.interceptors.default || "browser"
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
return defaultSettings
|
|
||||||
}
|
|
||||||
|
|
||||||
type ApplySettingPayload = {
|
type ApplySettingPayload = {
|
||||||
[K in keyof SettingsDef]: {
|
[K in keyof SettingsDef]: {
|
||||||
|
|||||||
@@ -98,6 +98,12 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="space-y-8 p-8 md:col-span-2">
|
<div class="space-y-8 p-8 md:col-span-2">
|
||||||
|
<section class="flex flex-col space-y-2">
|
||||||
|
<h4 class="font-semibold text-secondaryDark">
|
||||||
|
{{ t("settings.interceptor") }}
|
||||||
|
</h4>
|
||||||
|
<AppInterceptor :is-tooltip-component="false" />
|
||||||
|
</section>
|
||||||
<section v-for="[id, settings] in interceptorsWithSettings" :key="id">
|
<section v-for="[id, settings] in interceptorsWithSettings" :key="id">
|
||||||
<h4 class="font-semibold text-secondaryDark">
|
<h4 class="font-semibold text-secondaryDark">
|
||||||
{{ settings.entryTitle(t) }}
|
{{ settings.entryTitle(t) }}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { InspectorsPlatformDef } from "./inspectors"
|
|||||||
import { ServiceClassInstance } from "dioc"
|
import { ServiceClassInstance } from "dioc"
|
||||||
import { IOPlatformDef } from "./io"
|
import { IOPlatformDef } from "./io"
|
||||||
import { SpotlightPlatformDef } from "./spotlight"
|
import { SpotlightPlatformDef } from "./spotlight"
|
||||||
|
import { Ref } from "vue"
|
||||||
|
|
||||||
export type PlatformDef = {
|
export type PlatformDef = {
|
||||||
ui?: UIPlatformDef
|
ui?: UIPlatformDef
|
||||||
@@ -45,6 +46,11 @@ export type PlatformDef = {
|
|||||||
* If a value is not given, then the value is assumed to be true
|
* If a value is not given, then the value is assumed to be true
|
||||||
*/
|
*/
|
||||||
promptAsUsingCookies?: boolean
|
promptAsUsingCookies?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to show the A/B testing workspace switcher click login flow or not
|
||||||
|
*/
|
||||||
|
workspaceSwitcherLogin?: Ref<boolean>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1260,7 +1260,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hoppscotch-desktop"
|
name = "hoppscotch-desktop"
|
||||||
version = "24.3.2"
|
version = "24.3.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cocoa 0.25.0",
|
"cocoa 0.25.0",
|
||||||
"hex_color",
|
"hex_color",
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ createHoppApp("#app", {
|
|||||||
history: historyDef,
|
history: historyDef,
|
||||||
},
|
},
|
||||||
interceptors: {
|
interceptors: {
|
||||||
default: "proxy",
|
default: "browser",
|
||||||
interceptors: [
|
interceptors: [
|
||||||
{ type: "standalone", interceptor: browserInterceptor },
|
{ type: "standalone", interceptor: browserInterceptor },
|
||||||
{ type: "standalone", interceptor: proxyInterceptor },
|
{ type: "standalone", interceptor: proxyInterceptor },
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM node:18-alpine3.19 as base_builder
|
FROM node:20-alpine3.19 as base_builder
|
||||||
|
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user