mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-14 05:37:45 +02:00
* Configure who should be able to send invitation link Signed-off-by: Artem Savchenko <armisav@gmail.com> * Add user role select Signed-off-by: Artem Savchenko <armisav@gmail.com> * Limit default user roles Signed-off-by: Artem Savchenko <armisav@gmail.com> --------- Signed-off-by: Artem Savchenko <armisav@gmail.com>
245 lines
7.1 KiB
Svelte
245 lines
7.1 KiB
Svelte
<!--
|
|
// Copyright © 2022 Hardcore Engineering Inc.
|
|
//
|
|
// Licensed under the Eclipse Public License, Version 2.0 (the 'License');
|
|
// you may not use this file except in compliance with the License. You may
|
|
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an 'AS IS' BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
//
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
-->
|
|
<script lang="ts">
|
|
import { AccountRole, getCurrentAccount, hasAccountRole, Timestamp } from '@hcengineering/core'
|
|
import { copyTextToClipboard, createQuery } from '@hcengineering/presentation'
|
|
import setting, { RoleCapability } from '@hcengineering/setting'
|
|
import { getResource } from '@hcengineering/platform'
|
|
import { AnySvelteComponent, Button, EditBox, Grid, Label, Loading, MiniToggle, ticker } from '@hcengineering/ui'
|
|
import { createEventDispatcher } from 'svelte'
|
|
|
|
import login from '../plugin'
|
|
import { getInviteLink } from '../utils'
|
|
import InviteWorkspace from './icons/InviteWorkspace.svelte'
|
|
|
|
export let role: AccountRole | undefined = undefined
|
|
export let ignoreSettings: boolean = false
|
|
|
|
const dispatch = createEventDispatcher()
|
|
|
|
const query = createQuery()
|
|
const isSecureContext = window.isSecureContext
|
|
|
|
interface InviteParams {
|
|
expirationTime: number
|
|
emailMask: string
|
|
limit: number | undefined
|
|
}
|
|
|
|
$: !ignoreSettings &&
|
|
query.query(setting.class.InviteSettings, {}, (set) => {
|
|
if (set !== undefined && set.length > 0) {
|
|
expHours = set[0].expirationTime
|
|
emailMask = set[0].emailMask
|
|
limit = set[0].limit
|
|
if (role == null) {
|
|
role = set[0].defaultInviteRole ?? AccountRole.User
|
|
}
|
|
} else {
|
|
expHours = 48
|
|
limit = -1
|
|
if (role == null) {
|
|
role = AccountRole.User
|
|
}
|
|
}
|
|
|
|
if (limit === -1) noLimit = true
|
|
|
|
defaultValues = {
|
|
expirationTime: expHours,
|
|
emailMask,
|
|
limit
|
|
}
|
|
})
|
|
|
|
function setToDefault (): void {
|
|
expHours = defaultValues.expirationTime
|
|
emailMask = defaultValues.emailMask
|
|
limit = defaultValues.limit
|
|
}
|
|
|
|
async function getLink (expHours: number, mask: string, limit: number | undefined, role: AccountRole): Promise<void> {
|
|
loading = true
|
|
link = await getInviteLink(expHours, mask, limit ?? -1, role)
|
|
loading = false
|
|
}
|
|
|
|
let copiedTime: Timestamp | undefined
|
|
let copied = false
|
|
|
|
$: if (copiedTime !== undefined && copied && $ticker - copiedTime > 1000) {
|
|
copied = false
|
|
}
|
|
|
|
async function copy (): Promise<void> {
|
|
if (!isSecureContext) return
|
|
if (link === undefined) return
|
|
|
|
await copyTextToClipboard(link)
|
|
copied = true
|
|
copiedTime = Date.now()
|
|
}
|
|
|
|
let expHours: number = 48
|
|
let emailMask: string = ''
|
|
let limit: number | undefined = undefined
|
|
let useDefault: boolean | undefined = true
|
|
let noLimit: boolean = false
|
|
const currentAccount = getCurrentAccount()
|
|
const isOwnerOrMaintainer: boolean = hasAccountRole(currentAccount, AccountRole.Maintainer)
|
|
let userRoleSelectComponent: AnySvelteComponent | undefined
|
|
void getResource(setting.component.UserRoleSelect).then((component) => {
|
|
userRoleSelectComponent = component
|
|
})
|
|
let canGenerateInviteLinks = false
|
|
|
|
// Use shared HasRoleCapability function resource from setting package instead of setting-resources
|
|
void getResource(setting.function.HasRoleCapability).then((checkCapability) => {
|
|
if (checkCapability != null) {
|
|
void checkCapability(RoleCapability.GenerateInviteLink).then((value: boolean) => {
|
|
canGenerateInviteLinks = value
|
|
})
|
|
}
|
|
})
|
|
let defaultValues: InviteParams = {
|
|
expirationTime: 48,
|
|
emailMask: '',
|
|
limit: undefined
|
|
}
|
|
|
|
let link: string | undefined
|
|
let loading = false
|
|
|
|
function handleInviteRoleSelected (e: CustomEvent<AccountRole>): void {
|
|
role = e.detail
|
|
link = undefined
|
|
}
|
|
</script>
|
|
|
|
<div class="antiPopup popup" class:secure={isSecureContext}>
|
|
<div class="flex-between fs-title mb-9">
|
|
<Label label={login.string.InviteDescription} />
|
|
<InviteWorkspace size={'large'} />
|
|
</div>
|
|
{#if isOwnerOrMaintainer && !ignoreSettings}
|
|
<Grid column={1} rowGap={1.5}>
|
|
<MiniToggle
|
|
bind:on={useDefault}
|
|
label={login.string.UseWorkspaceInviteSettings}
|
|
on:click={() => {
|
|
setToDefault()
|
|
}}
|
|
/>
|
|
{#if !useDefault}
|
|
<EditBox
|
|
label={login.string.LinkValidHours}
|
|
bind:value={expHours}
|
|
format={'number'}
|
|
on:keypress={() => (link = undefined)}
|
|
disabled={useDefault || !isOwnerOrMaintainer}
|
|
/>
|
|
<MiniToggle bind:on={noLimit} label={login.string.NoLimit} on:change={() => noLimit && (limit = -1)} />
|
|
{#if !noLimit}
|
|
<EditBox
|
|
label={login.string.InviteLimit}
|
|
bind:value={limit}
|
|
format={'number'}
|
|
on:keypress={() => (link = undefined)}
|
|
disabled={useDefault || !isOwnerOrMaintainer}
|
|
/>
|
|
{/if}
|
|
{#if userRoleSelectComponent}
|
|
<svelte:component
|
|
this={userRoleSelectComponent}
|
|
selected={role ?? AccountRole.User}
|
|
on:selected={handleInviteRoleSelected}
|
|
/>
|
|
{/if}
|
|
{/if}
|
|
</Grid>
|
|
{/if}
|
|
{#if loading}
|
|
<Loading />
|
|
{:else if link !== undefined}
|
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
|
<div class="link" class:notSecure={!isSecureContext} class:over-underline={isSecureContext} on:click={copy}>
|
|
{link}
|
|
</div>
|
|
<div class="buttons">
|
|
<Button
|
|
label={login.string.Close}
|
|
size={'medium'}
|
|
kind={'primary'}
|
|
on:click={() => {
|
|
dispatch('close')
|
|
}}
|
|
/>
|
|
{#if isSecureContext}
|
|
<Button label={copied ? login.string.Copied : login.string.Copy} size={'medium'} on:click={copy} />
|
|
{/if}
|
|
</div>
|
|
{:else}
|
|
<div class="buttons">
|
|
<Button
|
|
label={login.string.GetLink}
|
|
size={'medium'}
|
|
kind={'primary'}
|
|
disabled={!canGenerateInviteLinks}
|
|
on:click={() => {
|
|
if (!canGenerateInviteLinks) return
|
|
const effectiveLimit = limit ?? 0
|
|
if (effectiveLimit > 0 || noLimit) {
|
|
void getLink(expHours, emailMask, limit, role ?? AccountRole.User)
|
|
}
|
|
}}
|
|
/>
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
|
|
<style lang="scss">
|
|
.popup {
|
|
display: flex;
|
|
flex-direction: column;
|
|
padding: 1.75rem;
|
|
width: 30rem;
|
|
max-width: 40rem;
|
|
background: var(--popup-bg-color);
|
|
border-radius: 1.25rem;
|
|
user-select: none;
|
|
box-shadow: var(--popup-shadow);
|
|
|
|
.link {
|
|
margin: 1.75rem 0 0;
|
|
overflow-wrap: break-word;
|
|
|
|
&.notSecure {
|
|
user-select: text;
|
|
}
|
|
}
|
|
|
|
.buttons {
|
|
margin-top: 1.75rem;
|
|
flex-shrink: 0;
|
|
display: grid;
|
|
grid-auto-flow: column;
|
|
direction: rtl;
|
|
justify-content: flex-start;
|
|
align-items: center;
|
|
column-gap: 0.5rem;
|
|
}
|
|
}
|
|
</style>
|