Commit 95084675 authored by Matthieu Le Corre's avatar Matthieu Le Corre

add Changelog and move save & load from editor to proto

Signed-off-by: Matthieu Le Corre's avatarMatthieu Le Corre <matthieu.lecorre@univ-nantes.fr>
parent dfcaab6b
## 0.0.1 UNRELEASED
### Added
- initial public released
\ No newline at end of file
WhiteBoard
---
# WhiteBoard
A colloborative whiteboard for nextcloud based on [literally canvas](http://literallycanvas.com/)
- Online collaborative edition
A collaborative whiteboard for nextcloud based on [literally canvas](http://literallycanvas.com/)
- Online collaborative drawing
- Only with private share for now
- you can share directly from the app
- you can acces to tchat whithin app
![screenshot](./screenshot.png)
\ No newline at end of file
![screenshot](./screenshot.png)
## Warning
This app is based on [literally canvas](http://literallycanvas.com/) which means that I'll not be able to change lot's of things in he drawing capabilities ! If you know a better open source whiteboard js library, let me known I'll be happy to switch.
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -22,22 +22,21 @@
namespace OCA\whiteboard\Controller ;
use OCP\AppFramework\Controller ;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\IRequest;
;
use OCP\ICacheFactory;
use OCA\whiteboard\Collaboration\CollaborationEngine ;
class CollaborationController extends Controller {
/**
* @NoAdminRequired
*
**/
public function __construct($AppName,IRequest $request,ICacheFactory $cacheFactory, CollaborationEngine $engine) {
public function __construct($AppName, IRequest $request, CollaborationEngine $engine) {
parent::__construct($AppName, $request);
//$this->id = $request->getParam("id") ;
$this->engine = $engine ;
}
......@@ -91,7 +90,11 @@ class CollaborationController extends Controller {
*
**/
public function getSteps($id) {
return $this->engine->getSteps($id) ;
try {
return new DataResponse($this->engine->getSteps($id)) ;
} catch (DoesNotExistException $ex) {
return new DataResponse($ex.message,Http::STATUS_NO_CONTENT) ;
}
}
/**
......
......@@ -19,7 +19,7 @@
-->
<template>
<Content :id="app" :app-name="appName">
<Content :id="appContent" :app-name="appName">
<button class="icon-close" @click="close" />
<button class="icon-save" @click="save" />
<button class="icon-menu-sidebar" @click="sidebar" />
......@@ -33,7 +33,7 @@
menu-position="right" />
</li>
</ul>
<AppContent :id="apped">
<AppContent :id="appEditor">
Loading {{ appName }} ...
</AppContent>
</Content>
......@@ -47,23 +47,27 @@ import { emit } from '@nextcloud/event-bus'
export default {
name: 'PrototypeView',
components: {
Content,
AppContent,
Avatar,
},
props: {
appName: String,
filename: String,
context: Object,
app: String,
apped: String,
appContent: String,
appEditor: String,
},
data: function() {
return {
userList: this.$parent.userList,
}
},
methods: {
isOffline: function(user) {
const now = Math.floor(Date.now() / 1000) - 30
......@@ -73,12 +77,15 @@ export default {
return false
}
},
save() {
emit(this.appName + '::saveClick')
},
close() {
emit(this.appName + '::closeClick')
},
sidebar() {
if (!document.getElementById('app-sidebar')) {
OCA.Files.Sidebar.open(this.context.dir + '/' + this.filename)
......@@ -87,6 +94,11 @@ export default {
}
},
},
destroyed: function() {
document.getElementById('app-content-' + this.appName).remove()
document.getElementById('app-navigation').classList.remove('hidden')
},
}
</script>
......
......@@ -41,16 +41,18 @@ export default {
this.context = context
this.init().then(function(data) {
// console.log('CE : Collaboration started for ' + self.appName)
self.addUser()
// because we may arrive in an allready running session
// get all steps from last last save
self.getSteps().then(function(steps) {
steps.data.forEach(step => emit(self.appName + '::externalAddStep', step))
})
// get all steps from last save
self.getSteps()
.then(function(steps) {
steps.data.forEach(step => emit(self.appName + '::externalAddStep', step))
}).catch(function(error) {
console.debug(error.reponse)
})
// get user connected to the file
// get users connected to the file
self.getUserList().then(function(users) {
emit(self.appName + '::usersListChanged', users)
})
......@@ -160,25 +162,33 @@ export default {
})
}
}).catch(function(Err) {
if (axios.isCancel(Err)) {
console.debug(Err.message)
} else {
console.debug('LP Error', Err)
}
})
},
longPull: function() {
const CancelToken = axios.CancelToken
this.longPullCancelToken = CancelToken.source()
const url = generateUrl('apps/' + this.appName + '/collaboration/pushstep')
// console.log('CE : Poll again ...')
const ajx = axios.get(url, {
cancelToken: this.longPullCancelToken.token,
params: {
id: this.id,
user: getCurrentUser().uid,
},
})
return ajx
},
stopCommunication: function() {
this.communicationStarted = false
this.longPullCancelToken.cancel('Closing Collaborative Engine ...')
},
}
......@@ -20,22 +20,18 @@
*
*/
import { emit } from '@nextcloud/event-bus'
import { linkTo, generateUrl } from '@nextcloud/router'
import axios from '@nextcloud/axios'
import { linkTo } from '@nextcloud/router'
export default {
name: 'editor',
start: function(appName, filename, context) {
start: function(APP_NAME, content) {
const self = this
this.appname = appName
this.filename = filename
this.context = context
this.appname = APP_NAME
this.init().then(function() {
self.loadContent()
self.loadContent(content)
self.setupCallbacks()
})
......@@ -55,42 +51,15 @@ export default {
},
// load whiteboard
loadContent: function() {
const self = this
const url = generateUrl('apps/' + this.appname + '/file/load')
axios.get(url, {
params: {
path: this.context.dir + '/' + this.filename,
},
}).then(function(content) {
// console.log("WB : loaded") ;
if (content.data.trim() !== '') {
self.whiteboard.loadSnapshot(JSON.parse(content.data))
}
})
loadContent: function(content) {
if (content.data.trim() !== '') {
this.whiteboard.loadSnapshot(JSON.parse(content.data))
}
},
// save whiteboard
saveContent: function() {
const self = this
const url = generateUrl('apps/' + this.appname + '/file/save')
axios.post(url, {
content: JSON.stringify(this.whiteboard.getSnapshot()),
path: this.context.dir + '/' + this.filename,
}).then(function(content) {
OC.Notification.showTemporary('File saved')
const payload = {
'type': 'save',
'step': 'NA',
}
emit(self.appname + '::editorAddStep', payload)
})
getSave: function() {
return this.whiteboard.getSnapshot()
},
// setup callback
......
......@@ -20,8 +20,9 @@
import editor from './editor.js'
import collaborationEngine from './collaboration.js'
import { subscribe, unsubscribe } from '@nextcloud/event-bus'
import { imagePath } from '@nextcloud/router'
import { emit, subscribe, unsubscribe } from '@nextcloud/event-bus'
import { imagePath, generateUrl } from '@nextcloud/router'
import axios from '@nextcloud/axios'
import Vue from 'vue'
import PrototypeView from './PrototypeView'
......@@ -55,6 +56,9 @@ export default {
const self = this
this.filename = filename
this.context = context
const container = document.createElement('div')
container.id = 'app-content-' + this.APP_NAME
......@@ -76,8 +80,8 @@ export default {
appName: this.APP_NAME,
filename: filename,
context: context,
app: 'app-content-whiteboard',
apped: 'whiteboard-editor',
appContent: 'app-content-' + this.APP_NAME,
appEditor: this.APP_NAME + '-editor',
},
}
),
......@@ -85,10 +89,11 @@ export default {
this.vm.$mount(container)
subscribe(this.APP_NAME + '::saveClick', function() {
// TODO unsubscribe !!
subscribe(this.APP_NAME + '::saveClick', this.SC = () => {
self.saveEdit()
})
subscribe(this.APP_NAME + '::closeClick', function() {
subscribe(this.APP_NAME + '::closeClick', this.CC = () => {
self.stopEdit()
})
......@@ -150,9 +155,11 @@ export default {
const self = this
// start the editor
this.ED = editor
this.ED.start(this.APP_NAME, filename, context)
this.loadContent().then(function(content) {
// start the editor
self.ED = editor
self.ED.start(self.APP_NAME, content)
})
// start the collaboration Engine
this.CE = collaborationEngine
......@@ -161,8 +168,7 @@ export default {
// subscribtion to event bus
// local changes => send to Engine
subscribe(this.APP_NAME + '::editorAddStep', this.EDS = (data) => {
self.CE.sendStep(data).then(function() {
})
self.CE.sendStep(data)
})
// engine sent us changes => forward to editor
......@@ -180,13 +186,16 @@ export default {
// stop editing
stopEdit: function() {
// save the content
this.ED.saveContent()
this.saveEdit()
// unsubscribe from bus event
unsubscribe(this.APP_NAME + '::editorAddStep', this.EDS)
unsubscribe(this.APP_NAME + '::externalAddStep', this.EAS)
unsubscribe(this.APP_NAME + '::usersListChanged', this.ECU)
unsubscribe(this.APP_NAME + '::closeClick', this.CC)
unsubscribe(this.APP_NAME + '::closeClick', this.SC)
// stop collaboration Engine
this.CE.stop()
......@@ -196,13 +205,40 @@ export default {
// remove app container
this.vm.$destroy()
document.getElementById('app-content-' + this.APP_NAME).remove()
document.getElementById('app-navigation').classList.remove('hidden')
},
// load content
loadContent: function() {
// const self = this
const url = generateUrl('apps/' + this.APP_NAME + '/file/load')
return axios.get(url, {
params: {
path: this.context.dir + '/' + this.filename,
},
})
},
// save edit
saveEdit: function() {
this.ED.saveContent()
const content = this.ED.getSave()
const self = this
const url = generateUrl('apps/' + this.APP_NAME + '/file/save')
axios.post(url, {
content: JSON.stringify(content),
path: this.context.dir + '/' + this.filename,
}).then(function() {
OC.Notification.showTemporary('File saved')
const payload = {
'type': 'save',
'step': 'NA',
}
emit(self.APP_NAME + '::editorAddStep', payload)
})
},
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment