mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-25 20:42:56 +03:00
Instant office links (#6121)
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
parent
1b598b9ad8
commit
807426e0b2
@ -534,17 +534,17 @@ export async function getBlobURL (blob: Blob): Promise<string> {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function copyTextToClipboard (text: string): Promise<void> {
|
||||
export async function copyTextToClipboard (text: string | Promise<string>): Promise<void> {
|
||||
try {
|
||||
// Safari specific behavior
|
||||
// see https://bugs.webkit.org/show_bug.cgi?id=222262
|
||||
const clipboardItem = new ClipboardItem({
|
||||
'text/plain': Promise.resolve(text)
|
||||
'text/plain': text instanceof Promise ? text : Promise.resolve(text)
|
||||
})
|
||||
await navigator.clipboard.write([clipboardItem])
|
||||
} catch {
|
||||
// Fallback to default clipboard API implementation
|
||||
await navigator.clipboard.writeText(text)
|
||||
await navigator.clipboard.writeText(text instanceof Promise ? await text : text)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,13 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { OK, setMetadata, Severity, Status } from '@hcengineering/platform'
|
||||
import { fetchMetadataLocalStorage, getCurrentLocation, navigate, setMetadataLocalStorage } from '@hcengineering/ui'
|
||||
import {
|
||||
fetchMetadataLocalStorage,
|
||||
getCurrentLocation,
|
||||
Location,
|
||||
navigate,
|
||||
setMetadataLocalStorage
|
||||
} from '@hcengineering/ui'
|
||||
|
||||
import { checkJoined, join, signUpJoin } from '../utils'
|
||||
import Form from './Form.svelte'
|
||||
@ -85,6 +91,19 @@
|
||||
setMetadataLocalStorage(login.metadata.LoginTokens, tokens)
|
||||
setMetadataLocalStorage(login.metadata.LoginEndpoint, result.endpoint)
|
||||
setMetadataLocalStorage(login.metadata.LoginEmail, result.email)
|
||||
|
||||
if (location.query?.navigateUrl != null) {
|
||||
try {
|
||||
const loc = JSON.parse(decodeURIComponent(location.query.navigateUrl)) as Location
|
||||
if (loc.path[1] === result.workspace) {
|
||||
navigate(loc)
|
||||
return
|
||||
}
|
||||
} catch (err: any) {
|
||||
// Json parse error could be ignored
|
||||
}
|
||||
}
|
||||
|
||||
navigate({ path: [workbenchId, result.workspace] })
|
||||
}
|
||||
}
|
||||
@ -126,6 +145,17 @@
|
||||
setMetadataLocalStorage(login.metadata.LoginEndpoint, result.endpoint)
|
||||
setMetadataLocalStorage(login.metadata.LoginEmail, result.email)
|
||||
|
||||
if (location.query?.navigateUrl != null) {
|
||||
try {
|
||||
const loc = JSON.parse(decodeURIComponent(location.query.navigateUrl)) as Location
|
||||
if (loc.path[1] === result.workspace) {
|
||||
navigate(loc)
|
||||
return
|
||||
}
|
||||
} catch (err: any) {
|
||||
// Json parse error could be ignored
|
||||
}
|
||||
}
|
||||
navigate({ path: [workbenchId, result.workspace] })
|
||||
}
|
||||
}
|
||||
|
@ -247,7 +247,7 @@ export async function getAccount (doNavigate: boolean = true): Promise<LoginInfo
|
||||
|
||||
export async function selectWorkspace (
|
||||
workspace: string,
|
||||
token: string | null | undefined
|
||||
token?: string | null | undefined
|
||||
): Promise<[Status, WorkspaceLoginInfo | undefined]> {
|
||||
const accountsUrl = getMetadata(login.metadata.AccountsUrl)
|
||||
|
||||
@ -449,7 +449,8 @@ export async function getInviteLink (
|
||||
expHours: number,
|
||||
mask: string,
|
||||
limit: number | undefined,
|
||||
role: AccountRole
|
||||
role: AccountRole,
|
||||
navigateUrl?: string
|
||||
): Promise<string> {
|
||||
const inviteId = await getInviteLinkId(expHours, mask, limit ?? -1, role)
|
||||
const loc = getCurrentLocation()
|
||||
@ -459,6 +460,9 @@ export async function getInviteLink (
|
||||
loc.query = {
|
||||
inviteId
|
||||
}
|
||||
if (navigateUrl !== undefined) {
|
||||
loc.query.navigateUrl = navigateUrl
|
||||
}
|
||||
loc.fragment = undefined
|
||||
|
||||
const url = locationToUrl(loc)
|
||||
|
@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { Ref, Doc, AccountRole } from '@hcengineering/core'
|
||||
import { AccountRole, Doc, Ref } from '@hcengineering/core'
|
||||
import type { Asset, IntlString, Metadata, Plugin, Resource, Status } from '@hcengineering/platform'
|
||||
import { plugin } from '@hcengineering/platform'
|
||||
import type { AnyComponent } from '@hcengineering/ui'
|
||||
@ -80,7 +80,13 @@ export default plugin(loginId, {
|
||||
function: {
|
||||
SendInvite: '' as Resource<(email: string, personId?: Ref<Doc>, role?: AccountRole) => Promise<void>>,
|
||||
GetInviteLink: '' as Resource<
|
||||
(expHours: number, mask: string, limit: number | undefined, role: AccountRole) => Promise<string>
|
||||
(
|
||||
expHours: number,
|
||||
mask: string,
|
||||
limit: number | undefined,
|
||||
role: AccountRole,
|
||||
navigateUrl?: string
|
||||
) => Promise<string>
|
||||
>,
|
||||
LeaveWorkspace: '' as Resource<(email: string) => Promise<void>>,
|
||||
ChangePassword: '' as Resource<(oldPassword: string, password: string) => Promise<void>>,
|
||||
|
@ -16,30 +16,31 @@
|
||||
import { PersonAccount } from '@hcengineering/contact'
|
||||
import { AccountRole, getCurrentAccount, hasAccountRole } from '@hcengineering/core'
|
||||
import login from '@hcengineering/login'
|
||||
import love, { Room, RoomType, isOffice, roomAccessIcon } from '@hcengineering/love'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import { copyTextToClipboard, getClient } from '@hcengineering/presentation'
|
||||
import {
|
||||
IconUpOutline,
|
||||
ModernButton,
|
||||
PopupInstance,
|
||||
SplitButton,
|
||||
eventToHTMLElement,
|
||||
getCurrentLocation,
|
||||
showPopup,
|
||||
PopupInstance,
|
||||
type CompAndProps,
|
||||
type AnySvelteComponent
|
||||
type AnySvelteComponent,
|
||||
type CompAndProps
|
||||
} from '@hcengineering/ui'
|
||||
import view from '@hcengineering/view'
|
||||
import love, { Room, RoomType, isOffice, roomAccessIcon } from '@hcengineering/love'
|
||||
import plugin from '../plugin'
|
||||
import { currentRoom, myInfo, myOffice } from '../stores'
|
||||
import {
|
||||
isCameraEnabled,
|
||||
isConnected,
|
||||
isFullScreen,
|
||||
isMicEnabled,
|
||||
isRecording,
|
||||
isRecordingAvailable,
|
||||
isSharingEnabled,
|
||||
isFullScreen,
|
||||
leaveRoom,
|
||||
record,
|
||||
screenSharing,
|
||||
@ -116,10 +117,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function getLink (): Promise<string> {
|
||||
const roomInfo = await client.findOne(love.class.RoomInfo, { room: room._id })
|
||||
if (roomInfo !== undefined) {
|
||||
const navigateUrl = getCurrentLocation()
|
||||
navigateUrl.query = {
|
||||
meetId: roomInfo._id
|
||||
}
|
||||
const func = await getResource(login.function.GetInviteLink)
|
||||
return await func(24 * 30, '', -1, AccountRole.Guest, JSON.stringify(navigateUrl))
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
async function copyGuestLink (): Promise<void> {
|
||||
const getLink = await getResource(login.function.GetInviteLink)
|
||||
const link = await getLink(24 * 30, '', -1, AccountRole.Guest)
|
||||
await copyTextToClipboard(link)
|
||||
await copyTextToClipboard(getLink())
|
||||
linkCopied = true
|
||||
clearTimeout(linkTimeout)
|
||||
linkTimeout = setTimeout(() => {
|
||||
|
@ -13,14 +13,18 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Contact, Person } from '@hcengineering/contact'
|
||||
import { personByIdStore } from '@hcengineering/contact-resources'
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { deviceOptionsStore as deviceInfo } from '@hcengineering/ui'
|
||||
import { Floor as FloorType, Office, Room, isOffice } from '@hcengineering/love'
|
||||
import { activeFloor, floors, rooms } from '../stores'
|
||||
import love, { Floor as FloorType, Office, Room, RoomInfo, isOffice } from '@hcengineering/love'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { deviceOptionsStore as deviceInfo, getCurrentLocation, navigate } from '@hcengineering/ui'
|
||||
import { onMount } from 'svelte'
|
||||
import { activeFloor, floors, infos, invites, myInfo, myRequests, rooms } from '../stores'
|
||||
import { tryConnect } from '../utils'
|
||||
import Floor from './Floor.svelte'
|
||||
import FloorConfigure from './FloorConfigure.svelte'
|
||||
import Floors from './Floors.svelte'
|
||||
import { Contact, Person } from '@hcengineering/contact'
|
||||
|
||||
function getRooms (rooms: Room[], floor: Ref<FloorType>): Room[] {
|
||||
return rooms.filter((p) => p.floor === floor)
|
||||
@ -36,6 +40,28 @@
|
||||
.map((p) => (p as Office).person) as Ref<Person>[]
|
||||
|
||||
$: $deviceInfo.replacedPanel = replacedPanel
|
||||
|
||||
onMount(async () => {
|
||||
const loc = getCurrentLocation()
|
||||
const { meetId, ...query } = loc.query ?? {}
|
||||
if (meetId != null) {
|
||||
loc.query = Object.keys(query).length === 0 ? undefined : query
|
||||
navigate(loc, true)
|
||||
const client = getClient()
|
||||
const info = await client.findOne(love.class.RoomInfo, { _id: meetId as Ref<RoomInfo> })
|
||||
if (info === undefined) return
|
||||
const room = $rooms.find((p) => p._id === info.room)
|
||||
if (room === undefined) return
|
||||
tryConnect(
|
||||
$personByIdStore,
|
||||
$myInfo,
|
||||
room,
|
||||
$infos.filter((p) => p.room === room._id),
|
||||
$myRequests,
|
||||
$invites
|
||||
)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<Floors bind:floor={selectedFloor} bind:configure />
|
||||
|
@ -17,7 +17,7 @@
|
||||
import { Avatar, personByIdStore } from '@hcengineering/contact-resources'
|
||||
import { IdMap, getCurrentAccount } from '@hcengineering/core'
|
||||
import { ParticipantInfo, Room, RoomAccess, RoomType } from '@hcengineering/love'
|
||||
import { Icon, Label, eventToHTMLElement, showPopup, showTooltip } from '@hcengineering/ui'
|
||||
import { Icon, Label, eventToHTMLElement, showPopup } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import love from '../plugin'
|
||||
import { invites, myInfo, myRequests } from '../stores'
|
||||
|
@ -1,11 +1,6 @@
|
||||
import { Analytics } from '@hcengineering/analytics'
|
||||
import contact, { getName, type Person, type PersonAccount } from '@hcengineering/contact'
|
||||
import core, { concatLink, getCurrentAccount, type IdMap, type Ref, type Space } from '@hcengineering/core'
|
||||
import { getEmbeddedLabel, getMetadata, type IntlString } from '@hcengineering/platform'
|
||||
import presentation, { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { getCurrentLocation, navigate, type DropdownTextItem } from '@hcengineering/ui'
|
||||
import { KrispNoiseFilter, isKrispNoiseFilterSupported } from '@livekit/krisp-noise-filter'
|
||||
import { BackgroundBlur, type BackgroundOptions, type ProcessorWrapper } from '@livekit/track-processors'
|
||||
import {
|
||||
RequestStatus,
|
||||
RoomAccess,
|
||||
@ -18,15 +13,20 @@ import {
|
||||
type ParticipantInfo,
|
||||
type Room
|
||||
} from '@hcengineering/love'
|
||||
import { getEmbeddedLabel, getMetadata, type IntlString } from '@hcengineering/platform'
|
||||
import presentation, { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { getCurrentLocation, navigate, type DropdownTextItem } from '@hcengineering/ui'
|
||||
import { KrispNoiseFilter, isKrispNoiseFilterSupported } from '@livekit/krisp-noise-filter'
|
||||
import { BackgroundBlur, type BackgroundOptions, type ProcessorWrapper } from '@livekit/track-processors'
|
||||
import {
|
||||
ConnectionState,
|
||||
Room as LKRoom,
|
||||
LocalAudioTrack,
|
||||
type LocalTrack,
|
||||
LocalVideoTrack,
|
||||
RoomEvent,
|
||||
Track,
|
||||
type AudioCaptureOptions,
|
||||
type LocalTrack,
|
||||
type LocalTrackPublication,
|
||||
type RemoteParticipant,
|
||||
type RemoteTrack,
|
||||
@ -568,7 +568,10 @@ export async function tryConnect (
|
||||
const currentPerson = personByIdStore.get((me as PersonAccount).person)
|
||||
if (currentPerson === undefined) return
|
||||
const client = getClient()
|
||||
|
||||
// guests can't join without invite
|
||||
if (!client.getHierarchy().hasMixin(currentPerson, contact.mixin.Employee)) return
|
||||
|
||||
if (room._id === currentInfo?.room) return
|
||||
if (room.access === RoomAccess.DND) return
|
||||
const thisRoomRequest = currentRequests.find((p) => p.room === room._id)
|
||||
|
@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import contact, { Employee, Person, PersonAccount, getName, formatName } from '@hcengineering/contact'
|
||||
import contact, { Employee, Person, PersonAccount, formatName, getName } from '@hcengineering/contact'
|
||||
import core, {
|
||||
Account,
|
||||
Ref,
|
||||
@ -25,7 +25,6 @@ import core, {
|
||||
TxUpdateDoc,
|
||||
UserStatus
|
||||
} from '@hcengineering/core'
|
||||
import { TriggerControl } from '@hcengineering/server-core'
|
||||
import love, {
|
||||
Invite,
|
||||
JoinRequest,
|
||||
@ -36,10 +35,11 @@ import love, {
|
||||
isOffice,
|
||||
loveId
|
||||
} from '@hcengineering/love'
|
||||
import { createPushNotification, isAllowed } from '@hcengineering/server-notification-resources'
|
||||
import notification from '@hcengineering/notification'
|
||||
import { workbenchId } from '@hcengineering/workbench'
|
||||
import { translate } from '@hcengineering/platform'
|
||||
import { TriggerControl } from '@hcengineering/server-core'
|
||||
import { createPushNotification, isAllowed } from '@hcengineering/server-notification-resources'
|
||||
import { workbenchId } from '@hcengineering/workbench'
|
||||
|
||||
export async function OnEmployee (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
||||
const actualTx = TxProcessor.extractTx(tx) as TxMixin<Person, Employee>
|
||||
@ -202,11 +202,15 @@ function setDefaultRoomAccess (info: ParticipantInfo, roomInfos: RoomInfo[], con
|
||||
const oldRoomInfo = roomInfos.find((ri) => ri.persons.includes(info.person))
|
||||
if (oldRoomInfo !== undefined) {
|
||||
oldRoomInfo.persons = oldRoomInfo.persons.filter((p) => p !== info.person)
|
||||
res.push(
|
||||
control.txFactory.createTxUpdateDoc(love.class.RoomInfo, core.space.Workspace, oldRoomInfo._id, {
|
||||
persons: oldRoomInfo.persons
|
||||
})
|
||||
)
|
||||
if (oldRoomInfo.persons.length === 0) {
|
||||
res.push(control.txFactory.createTxRemoveDoc(oldRoomInfo._class, oldRoomInfo.space, oldRoomInfo._id))
|
||||
} else {
|
||||
res.push(
|
||||
control.txFactory.createTxUpdateDoc(love.class.RoomInfo, core.space.Workspace, oldRoomInfo._id, {
|
||||
persons: oldRoomInfo.persons
|
||||
})
|
||||
)
|
||||
}
|
||||
if (oldRoomInfo.persons.length === 0) {
|
||||
const resetAccessTx = control.txFactory.createTxUpdateDoc(
|
||||
oldRoomInfo.isOffice ? love.class.Office : love.class.Room,
|
||||
|
Loading…
Reference in New Issue
Block a user