Skip to content
Open
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
769 changes: 714 additions & 55 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"@nextcloud/notify_push": "^1.4.0",
"@nextcloud/router": "^3.2.0",
"@nextcloud/talk": "github:nextcloud-releases/spreed#v25.0.6",
"@nextcloud/viewer": "^2.0.0-beta.19",
"@nextcloud/vue": "^9.14.0",
"@vueuse/core": "^15.0.0",
"core-js": "^3.50.0",
Expand Down
15 changes: 0 additions & 15 deletions src/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,6 @@ declare module '@nextcloud/vue/components/*' {
const component: Component
export default component
}
declare module '@nextcloud/vue/composables/useHotKey' {
export function useHotKey(
keysOrFilter: string | string[] | ((event: KeyboardEvent) => boolean) | true,
callback: (event: KeyboardEvent) => void,
options?: {
push?: boolean
prevent?: boolean
stop?: boolean
ctrl?: boolean
alt?: boolean
shift?: boolean
caseSensitive?: boolean
},
): () => void
}

// Built-time constants
declare const IS_DESKTOP: true
Expand Down
6 changes: 2 additions & 4 deletions src/help/renderer/HelpApp.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { ref } from 'vue'
import NcButton from '@nextcloud/vue/components/NcButton'
import NcRichText from '@nextcloud/vue/components/NcRichText'
import AppWindow from '../../shared/components/AppWindow.vue'
import ButtonCopy from './ButtonCopy.vue'
import ButtonCopy from '../../shared/components/ButtonCopy.vue'
import TalkLogo from '../../../img/talk-icon-rounded.svg'
import { BUILD_CONFIG } from '../../shared/build.config.ts'
import { useDevMode } from '../../shared/useDevMode.ts'
Expand Down Expand Up @@ -68,9 +68,7 @@ function close() {
<div class="help__report-col">
<div class="help__report-heading-bar">
<h2>{{ t('talk_desktop', 'Diagnosis report') }}</h2>
<ButtonCopy :content="reportToCopy" variant="tertiary">
{{ t('talk_desktop', 'Copy report') }}
</ButtonCopy>
<ButtonCopy :label="t('talk_desktop', 'Copy report')" :content="reportToCopy" variant="tertiary" />
</div>
<NcRichText class="help__report" :text="report" useExtendedMarkdown />
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,35 @@

<script setup lang="ts">
import { t } from '@nextcloud/l10n'
import { ref } from 'vue'
import { computed, ref } from 'vue'
import NcButton from '@nextcloud/vue/components/NcButton'
import IconCheck from 'vue-material-design-icons/Check.vue'
import IconCopy from 'vue-material-design-icons/ContentCopy.vue'

type NcButtonProps = InstanceType<typeof NcButton>['$props']

const {
text = t('talk_desktop', 'Copy'),
label = t('talk_desktop', 'Copy'),
labelHidden = false,
content = undefined,
getContent = undefined,
variant = undefined,
} = defineProps<{
text?: string
label?: string
labelHidden?: boolean
content?: string
getContent?: () => string
variant?: NcButtonProps['variant']
}>()

const DELAY = 2000

const copied = ref(false)

const ariaLabel = computed(() => !copied.value ? label : t('talk_desktop', 'Copied'))

// TODO: either add toasts or announce status with a shared role="status" region

/**
* Copy the content to the clipboard
*/
Expand Down Expand Up @@ -51,24 +61,26 @@ async function copy() {
</script>

<template>
<NcButton @click="copy">
<NcButton
:aria-label="labelHidden ? ariaLabel : undefined"
:title="labelHidden ? ariaLabel : undefined"
:variant
@click="copy">
<template #icon>
<IconCheck v-if="copied" :size="20" />
<slot v-else name="icon" :size="20">
<IconCopy :size="20" />
</slot>
<IconCopy v-else :size="20" />
</template>

<span class="copy-button__content">
<span :class="{ 'copy-button__text-hidden': copied }">
<slot>
{{ text }}
</slot>
<template v-if="!labelHidden" #default>
<span class="copy-button__content">
<span :class="{ 'copy-button__text-hidden': copied }" :aria-hidden="copied">
{{ label }}
</span>
<span :class="{ 'copy-button__text-hidden': !copied }" :aria-hidden="!copied">
{{ t('talk_desktop', 'Copied') }}
</span>
</span>
<span :class="{ 'copy-button__text-hidden': !copied }">
{{ t('talk_desktop', 'Copied') }}
</span>
</span>
</template>
</NcButton>
</template>

Expand Down
16 changes: 8 additions & 8 deletions src/shared/useBrowserStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,22 @@ import { computed, shallowRef } from 'vue'
type NextcloudStorage = ReturnType<ReturnType<typeof getBuilder>['build']>

/**
* Create a reactive variable that is stored in the @nextcloud/browser-storage instance and is synced
* Create a reactive variable that is stored in the nextcloud-browser-storage instance and is synced
*
* @param browserStorage - @nextcloud/browser-storage instance
* @param browserStorage - nextcloud-browser-storage instance
* @param key - Key to store the value
* @param defaultValue - Default value
*/
export function useBrowserStorage<T extends unknown | null>(browserStorage: NextcloudStorage, key: string, defaultValue: T | null = null) {
const parse = (value: string | null) => value ? JSON.parse(value) as T : null
export function useBrowserStorage<T>(browserStorage: NextcloudStorage, key: string, defaultValue: T) {
const parse = (value: string | null) => value ? JSON.parse(value) as T : undefined

const _data = shallowRef<T | null>(parse(browserStorage.getItem(key)) ?? defaultValue)
const _data = shallowRef<T>(parse(browserStorage.getItem(key)) ?? defaultValue)

const data = computed<T | null>({
const data = computed<T>({
get: () => _data.value,
set: (value: T | null) => {
set: (value: T) => {
_data.value = value
if (value === null) {
if (value === null || value === undefined) {
browserStorage.removeItem(key)
} else {
browserStorage.setItem(key, JSON.stringify(value))
Expand Down
5 changes: 4 additions & 1 deletion src/talk/renderer/TalkDesktop.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,20 @@ import AppWindow from '../../shared/components/AppWindow.vue'
import TalkWrapper from './TalkWrapper/TalkWrapper.vue'
import TitleBar from './TitleBar/TitleBar.vue'
import { useNotificationsStore } from './notifications/notifications.store.js'
import { registerViewerHandlers } from './viewer/viewer.module.ts'

const isTalkInitialized = ref(false)
provide('talk:isInitialized', isTalkInitialized)

useNotificationsStore()

registerViewerHandlers()
</script>

<template>
<AppWindow>
<div id="skip-actions" />
<TitleBar id="header" />
<TitleBar />
<TalkWrapper @ready="isTalkInitialized = true" />
</AppWindow>
</template>
54 changes: 54 additions & 0 deletions src/talk/renderer/viewer/ViewerHandlerPdf.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<!--
- SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
- SPDX-License-Identifier: AGPL-3.0-or-later
-->

<script setup lang="ts">
import type { ViewerEmits, ViewerProps } from '@nextcloud/viewer'

import { computed, onBeforeMount, onBeforeUnmount, ref } from 'vue'
import { getFileContent } from './files.service.ts'

const props = defineProps<ViewerProps>()
const emit = defineEmits<ViewerEmits>()

const maxWidthPx = computed(() => props.maxWidth + 'px')
const maxHeightPx = computed(() => props.maxHeight + 'px')

const src = ref<string>()

onBeforeMount(async () => {
try {
src.value = URL.createObjectURL(await getFileContent(props.file, 'blob'))
} catch (error) {
emit('errored', error as Error)
}
})

onBeforeUnmount(() => {
if (src.value) {
URL.revokeObjectURL(src.value)
}
})
</script>

<template>
<div class="viewer-pdf">
<iframe
v-if="src"
:src
:title="file.displayname"
:width="maxWidth"
:height="maxHeight"
@load="emit('loaded')" />
</div>
</template>

<style scoped>
/* TODO: add stretch mode to @nextcloud/viewer to avoid setting size via JS */
.viewer-pdf {
width: v-bind(maxWidthPx);
height: v-bind(maxHeightPx);
overflow: hidden;
}
</style>
Loading
Loading