Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use NcReferenceList in whiteboard #73

Merged
merged 3 commits into from
Aug 6, 2024
Merged
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
1,860 changes: 1,851 additions & 9 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 @@ -26,6 +26,7 @@
"@nextcloud/initial-state": "^2.2.0",
"@nextcloud/l10n": "^3.1.0",
"@nextcloud/router": "^3.0.1",
"@nextcloud/vue": "^8.14.0",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"jsonwebtoken": "^9.0.2",
Expand Down
29 changes: 28 additions & 1 deletion src/App.scss
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,35 @@
}
}

.widgets--list{
height: 100%;
}

.widgets--list > div{
height: 100% !important;
}

.widgets--list > div > div{
height: 100% !important;
}

.widget-custom {
height: 100%;
margin: 0 !important;
}

.widget-file--interactive{
height: 100% !important;
max-height: 100% !important;
min-height: unset !important;
}

.text-menubar--ready{
backdrop-filter: unset !important;
-webkit-backdrop-filter: unset !important;
}

.App {
text-align: center;
position: relative;
height: 100%;
width: 100%;
Expand Down
3 changes: 3 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import type {
ExcalidrawInitialDataState,
} from '@excalidraw/excalidraw/types/types'
import { Collab } from './collaboration/collab'
import Embeddable from './Embeddable'
import type { ResolvablePromise } from '@excalidraw/excalidraw/types/utils'
import type { NonDeletedExcalidrawElement } from '@excalidraw/excalidraw/types/element/types'
interface WhiteboardAppProps {
Expand Down Expand Up @@ -126,6 +127,8 @@ export default function App({ fileId, isEmbedded }: WhiteboardAppProps) {
<div className="App">
<div className="excalidraw-wrapper">
<Excalidraw
validateEmbeddable={() => true}
renderEmbeddable={ Embeddable }
excalidrawAPI={(api: ExcalidrawImperativeAPI) => {
console.log(api)
console.log('Setting API')
Expand Down
16 changes: 16 additions & 0 deletions src/Embeddable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { NcReferenceList } from '@nextcloud/vue/dist/Components/NcRichText.js'

import VueWrapper from './VueWrapper'

/**
*
* @param props componentProps and component to be rendered in Vue
*/
export default function(props) {
const referenceProps = { text: props.link, limit: 1, interactive: true }
return React.createElement(VueWrapper, { componentProps: referenceProps, component: NcReferenceList })
}
50 changes: 50 additions & 0 deletions src/VueWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import Vue from 'vue'

const VueWrapper = function(
{ componentProps, component }) {
const vueRef = React.useRef(null)
const [vueInstance, setVueInstance] = React.useState(undefined)

React.useEffect(() => {
/**
*
*/
async function createVueInstance() {
}

createVueInstance()

setVueInstance(new Vue({
el: vueRef.current,
data() {
return {
props: componentProps,
}
},
render(h) {
return h(component, {
props: this.props,
})
},
}))

return () => {
vueInstance?.$destroy()
}
}, [])

React.useEffect(() => {
if (vueInstance) {
const keys = Object.keys(componentProps)
keys.forEach(key => { vueInstance.props[key] = componentProps[key] })
}
}, [Object.values(componentProps)])

return <div id="vue-component" ref={vueRef}></div>
}

export default VueWrapper
37 changes: 37 additions & 0 deletions src/collaboration/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import type { ExcalidrawElement } from '@excalidraw/excalidraw/types/element/types'
import type { AppState } from '@excalidraw/excalidraw/types/types'
import { isObject } from 'lodash'

/**
* Hashes elements' versionNonce (using djb2 algo). Order of elements matters.
Expand All @@ -16,6 +17,35 @@ export const hashElementsVersion = (
return elements.reduce((acc, el) => acc + el.version, 0)
}

/**
*
* @param obj1 object 1 to compare
* @param obj2 object 2 to compare
* @param exceptions keys that are not required to be equal
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function isDeepEqual(obj1:any, obj2: any, exceptions: Array<string>) {

const keys1 = Object.keys(obj1)
const keys2 = Object.keys(obj2)

if (keys1.length !== keys2.length) return false

for (const key of keys1) {
const val1 = obj1[key]
const val2 = obj2[key]

const areObjects = isObject(val1) && isObject(val2)

if ((areObjects && !isDeepEqual(val1, val2, exceptions)) || (!areObjects && val1 !== val2)) {
if (!exceptions.includes(key)) {
return false
}
}
}
return true
}

/**
* decides if remote or local element should be kept
* @param localAppState state of the app
Expand All @@ -38,6 +68,13 @@ export function shouldDiscardRemoteElement(localAppState: Readonly<AppState>, lo
) {
return true
}

// embeddables get updated a lot by the excalidraw library when collaboration is active
// we need to filter out useless updates to not rerender every millisecond.
// current master of the excalidraw library probably fixes this issue but it's not available in excalidraw@latest (0.17.6)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

checking if the objects are DeepEqual is a workaround. Not entirely certain, but I do think if we could use the lib like excalidraw.com does here: https://github.com/excalidraw/excalidraw/blob/master/excalidraw-app/collab/Collab.tsx#L751 we would not need this workaround. However I couldn't figure out how to use the master version of excalidraws library in our build process.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, being able to build from master or another branch/a fork there is something that could certainly be useful, but we can check that separately

if (localElement && localElement.type === 'embeddable' && isDeepEqual(localElement, remoteElement, ['versionNonce', 'version', 'updated', 'validated'])) {
return true
}
return false
}

Expand Down
Empty file removed src/register.ts
Empty file.