Merge lp:~mttronchetti/novacut/history into lp:novacut

Proposed by Matteo Ronchetti on 2012-06-12
Status: Merged
Merged at revision: 271
Proposed branch: lp:~mttronchetti/novacut/history
Merge into: lp:novacut
Diff against target: 356 lines (+198/-40)
5 files modified
novacut-gtk (+11/-4)
novacut/schema.py (+1/-0)
ui/bucket.js (+16/-0)
ui/projects.html (+96/-3)
ui/projects.js (+74/-33)
To merge this branch: bzr merge lp:~mttronchetti/novacut/history
Reviewer Review Type Date Requested Status
Jason Gerard DeRose 2012-06-12 Approve on 2012-06-23
Review via email: mp+109928@code.launchpad.net

Description of the Change

Added lazy delete for project with a cool drop down menu called history

To post a comment you must log in.
lp:~mttronchetti/novacut/history updated on 2012-06-15
272. By Matteo Ronchetti on 2012-06-15

added possibility to duplicate a slice with d button on keyboard

273. By Matteo Ronchetti on 2012-06-15

added possibility to duplicate a slice with d button on keyboard

Jason Gerard DeRose (jderose) wrote :

Matteo,

First of all, thanks for jumping into Novacut full-force! Looks like you really have a handle on interacting with CouchDB, from both JavaScript and Python. And as lots of this lacks documentation still, that's no small feat!

First problem I encountered is the "history" DB isn't getting created if it doesn't already exist. Second, my gut feeling is we don't want to store this is a 2nd database, basically because it creates a lot of consistency and sync issues.

From what I've learned so far from working with CouchDB, you really don't want the state of a "thing" to span multiple documents, and you especially don't want it to span multiple databases. Updates to documents can arrive at one node in a different order than they were saved in another, and the problem gets much more complicated if you're talking about multiple databases.

I think instead we should have some flag attribute on the novacut/project documents. Say a hypothetical "isdeleted" attribute. And we'll make the view that lists project on the starting novacut page show all novacut/project documents such that `!doc.isdeleted` and make the History menu show all novacut/project documents such that `doc.isdeleted`.

Does that make sense? Lets discuss the schema when we're next both on IRC, okay?

Thanks again!

review: Needs Fixing
lp:~mttronchetti/novacut/history updated on 2012-06-19
274. By Matteo Ronchetti on 2012-06-19

changed the way projects are removed

Jason Gerard DeRose (jderose) wrote :

Matteo,

Okay, this is a lot better now, thanks! I'm going to merge this because I want you to have code in this release, but fair warning... this still needs some more work, and I reserve the right to use my "executive powers" to potentially make same changes later :P

Things I really like:

1) Using the same visual representation for the project inside the History/Trash... makes it much clearer what the "thing" is in there, nice work

2) Dragging out of the History/Trash to undelete... simple, and fits with the interaction patterns we use elsewhere

Things I think need improvement:

1) As it stands, "History" is a confusing term for what this menu does, and I personally don't think this is the place to expose your project's history of snapshots (that should be inside the project).

2) I don't like using onmouseover to show the History menu. Despite it's popularity in a lot of web sites lately (Google+ and Vimeo are big offenders in my mind), I think using onmouseover to reveal something is fundamentally bad UX. It also making it difficult to keep our interactions consistent between touch and mouse (but I don't think that's the biggest reason to avoid it).

Anyway, thanks again and talk to you more once you get back from the UK!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'novacut-gtk'
2--- novacut-gtk 2012-06-08 16:59:15 +0000
3+++ novacut-gtk 2012-06-19 17:00:25 +0000
4@@ -126,6 +126,7 @@
5 'render_job': ['job_id'],
6 'job_rendered': ['job_id', 'file_id', 'link'],
7 'delete_project': ['project_id'],
8+ 'sos_project': ['project_id'],
9 }
10
11 __Renderer = None
12@@ -161,6 +162,7 @@
13 hub.connect('hash_job', self.on_hash_job)
14 hub.connect('render_job', self.on_render_job)
15 hub.connect('delete_project', self.on_delete_project)
16+ hub.connect('sos_project', self.on_sos_project)
17
18 def post_env_init(self):
19 views.init_views(self.db, views.novacut_main)
20@@ -181,12 +183,17 @@
21
22 def on_delete_project(self, hub, project_id):
23 print('delete_project', project_id)
24+ doc = self.db.get(project_id)
25+ doc['isdeleted'] = True
26+ self.db.post(doc)
27+
28+ def on_sos_project(self,hub,project_id):
29+ print('sos_project', project_id)
30 doc = self.db.get(project_id)
31 print(doc)
32- db = self.server.database(schema.project_db_name(project_id))
33- db.delete()
34- db = self.server
35- db.delete('novacut-0',project_id,rev=doc['_rev'])
36+ doc['isdeleted'] = False
37+ self.db.post(doc)
38+
39
40 def on_load_project(self, hub, project_id):
41 print('load_project', project_id)
42
43=== modified file 'novacut/schema.py'
44--- novacut/schema.py 2012-03-18 07:38:42 +0000
45+++ novacut/schema.py 2012-06-19 17:00:25 +0000
46@@ -370,6 +370,7 @@
47 'atime': ts,
48 'db_name': project_db_name(_id),
49 'title': title,
50+ 'isdeleted': False,
51 }
52
53
54
55=== modified file 'ui/bucket.js'
56--- ui/bucket.js 2012-04-25 04:01:29 +0000
57+++ ui/bucket.js 2012-06-19 17:00:25 +0000
58@@ -1735,6 +1735,19 @@
59 }
60 },
61
62+ duplicate_selected: function(dnd) {
63+ var element = $(UI.selected);
64+ var doc = UI.session.get_doc(element.id);
65+ var ndoc = create_slice(doc.node.src,doc.node.stop.frame);
66+ ndoc.node.start.frame = doc.node.start.frame;
67+ UI.session.save(ndoc);
68+ var slice = new Slice(UI.session, ndoc);
69+ slice.x = 64;
70+ slice.y = 36;
71+ UI.bucket.appendChild(slice.element);
72+ UI.sequence.do_reorder();
73+ },
74+
75 first: function() {
76 var element = $(UI.selected);
77 if (element && element.parentNode) {
78@@ -1850,6 +1863,9 @@
79 UI.player.hold_and_resume();
80 }
81 },
82+ 'U+0044': function(event) {
83+ UI.duplicate_selected(event);
84+ },
85
86 // The David Fulde key
87 // aka Backspace aka Big Delete on a mac keyboard
88
89=== modified file 'ui/projects.html'
90--- ui/projects.html 2012-03-06 18:12:44 +0000
91+++ ui/projects.html 2012-06-19 17:00:25 +0000
92@@ -9,16 +9,109 @@
93 <script src="/_apps/dmedia/common.js"></script>
94 <script src="novacut.js"></script>
95 <script src="projects.js"></script>
96+ <script>
97+ function dragstart(ev){
98+ ev.dataTransfer.setData("Text", ev.target.id);
99+ ev.dataTransfer.effectAllowed = 'move';
100+ }
101+ function enter(ev){
102+ ev.target.setAttribute("style","background-color: rgba(255,255,255,0.15); height: 2000px;width = 2000px;");
103+ return false;
104+ }
105+ function leave(ev){
106+ ev.target.setAttribute("style","height: 2000px;width = 2000px");
107+ return false;
108+ }
109+ function d(ev){
110+ ev.preventDefault();
111+ ev.target.setAttribute("style","height: 2000px;width = 2000px;");
112+ var data=ev.dataTransfer.getData("Text");
113+ Hub.send('sos_project', data);
114+ element = document.getElementById(data);
115+ element.parentNode.removeChild(element);
116+ var doc = novacut.get_sync(data)
117+ UI.add_item(data,doc.title,doc.time,countFiles(data));
118+ return false;
119+ }
120+ function over(ev){
121+ ev.preventDefault();
122+ }
123+ </script>
124+ <style>
125+ .el{
126+ cursor: move;
127+ }
128+#menu{
129+ float: right;
130+ padding: 3px 8px 0 0;
131+ list-style: none;
132+ cursor: default;
133+}
134+
135+#menu li{
136+ float: left;
137+ padding: 0 0 10px 0;
138+ position: relative;
139+}
140+
141+#menu li:hover > a{
142+ color: #fafafa;
143+}
144+#menu li:hover > ul{
145+ display: block;
146+}
147+
148+#menu ul{ /*container*/
149+ min-width: 320px;
150+ min-height: 26px;
151+ list-style: none;
152+ margin: 0px -269px;
153+ padding: 4px;
154+ display: none;
155+ position: absolute;
156+ top: 35px;
157+ left: 0;
158+ z-index: 99999;
159+ background: #444;
160+ border-radius: 10px;
161+ box-shadow: 1px 1px 10px rgba(0,0,0,0.5);
162+}
163+#menu ul li{ /* projects*/
164+ float: none;
165+ margin: 10px;
166+ padding: 0;
167+ display: block;
168+ cursor: move;
169+}
170+#menu ul:after{
171+ content: '';
172+ position: absolute;
173+ left: 284px;
174+ top: -15px;
175+ width: 0;
176+ height: 0;
177+ border-left: 10px solid transparent;
178+ border-right: 10px solid transparent;
179+ border-bottom: 15px solid;
180+}
181+
182+#menu ul:after{
183+ border-bottom-color: #444;
184+}
185+
186+ </style>
187 </head>
188 <body>
189 <form id="new_project" class="head">
190 <div class="grid_row">
191 <input placeholder="New project name" type="text" class="grid_4" autofocus>
192 <button class="grid_4" disabled>Create Project</button>
193+ <ul id="menu"><li>
194+ <a>History</a>
195+ <ul id="list"></ul>
196+ </li></ul>
197 </div>
198 </form>
199-
200- <ul id="projects"></ul>
201-
202+<ul style="height: 200px;width = 200px;" id="projects" ondragenter="enter(event)" ondragleave="leave(event)" ondrop="d(event)" ondragover="over(event);"></ul>
203 </body>
204 </html>
205
206=== modified file 'ui/projects.js'
207--- ui/projects.js 2012-06-08 16:59:15 +0000
208+++ ui/projects.js 2012-06-19 17:00:25 +0000
209@@ -5,22 +5,35 @@
210 window.location.assign('cutter.html#' + project_id);
211 }
212
213+function countFiles(project_id){
214+ var pdb = new couch.Database("novacut-0-" + project_id.toLowerCase());
215+ try{
216+ var filecount = pdb.view_sync('doc', 'type', {key: 'dmedia/file'}).rows[0].value;
217+ }
218+ catch(e){
219+ var filecount = 0;
220+ }
221+ return filecount;
222+}
223
224 var UI = {
225 init: function() {
226 UI.form = $('new_project');
227 UI.input = UI.form.getElementsByTagName('input')[0];
228 UI.button = UI.form.getElementsByTagName('button')[0];
229- UI.project = new Project(novacut);
230- UI.items = new Items('projects');
231+ //UI.project = new Project(novacut);
232+ //UI.items = new Items('projects');
233
234 UI.form.onsubmit = UI.on_submit;
235 UI.input.oninput = UI.on_input;
236
237+ UI.hist = document.getElementById('list');
238+ UI.proj = document.getElementById('projects');
239 UI.load_items();
240 },
241
242 load_items: function() {
243+ //UI.add_history("qwe","nome",3333,3);
244 console.log('load_items');
245 novacut.view(UI.on_items, 'project', 'title');
246 },
247@@ -28,28 +41,58 @@
248 on_items: function(req) {
249 var rows = req.read().rows;
250 console.log(rows.length);
251- UI.items.replace(rows,
252- function(row, items) {
253- var pdb = new couch.Database("novacut-0-" + row.id.toLowerCase());
254- try{
255- var filecount = pdb.view_sync('doc', 'type', {key: 'dmedia/file'}).rows[0].value;
256- }
257- catch(e){
258- var filecount = 0;
259- }
260-
261- var li = $el('li', {'class': 'project', 'id': row.id});
262-
263- var thumb = $el('div', {'class': 'thumbnail'});
264- thumb.style.backgroundImage = "url(/_apps/dmedia/novacut-avatar-192.png)";//novacut.att_css_url(row.id);
265-
266- var info = $el('div', {'class': 'info'});
267- info.appendChild(
268- $el('p', {'textContent': row.key, 'class': 'title'})
269- );
270-
271- info.appendChild(
272- $el('p', {'textContent': format_date(row.value)})
273+ for(var b in rows){
274+ var a = rows[b]
275+ var pdb = new couch.Database("novacut-0-" + a.id.toLowerCase());
276+ try{
277+ var filecount = pdb.view_sync('doc', 'type', {key: 'dmedia/file'}).rows[0].value;
278+ }
279+ catch(e){
280+ var filecount = 0;
281+ }
282+ var doc = novacut.get_sync(a.id)
283+ if(!doc.isdeleted) UI.add_item(a.id,a.key,a.value,filecount);
284+ else UI.add_history(a.id,a.key,a.value,filecount);
285+ }
286+ },
287+
288+ add_history: function(id,name,date,filecount){
289+ var li = $el('li', {'class': 'project', 'id': id});
290+ li.setAttribute('draggable', 'true');
291+ li.setAttribute('ondragstart', 'dragstart(event)');
292+ var thumb = $el('div', {'class': 'thumbnail'});
293+ thumb.style.backgroundImage = "url(/_apps/dmedia/novacut-avatar-192.png)";//novacut.att_css_url(row.id);
294+
295+ var info = $el('div', {'class': 'info'});
296+ info.appendChild(
297+ $el('p', {'textContent': name, 'class': 'title'})
298+ );
299+
300+ info.appendChild(
301+ $el('p', {'textContent': format_date(date)})
302+ );
303+
304+ info.appendChild(
305+ $el('p', {'textContent': filecount + ' files'})
306+ );
307+
308+ li.appendChild(thumb);
309+ li.appendChild(info);
310+ UI.hist.appendChild(li);
311+ },
312+
313+ add_item: function(id,name,date,filecount) {
314+ var li = $el('li', {'class': 'project', 'id': id});
315+ var thumb = $el('div', {'class': 'thumbnail'});
316+ thumb.style.backgroundImage = "url(/_apps/dmedia/novacut-avatar-192.png)";//novacut.att_css_url(row.id);
317+
318+ var info = $el('div', {'class': 'info'});
319+ info.appendChild(
320+ $el('p', {'textContent': name, 'class': 'title'})
321+ );
322+
323+ info.appendChild(
324+ $el('p', {'textContent': format_date(date)})
325 );
326
327 info.appendChild(
328@@ -62,21 +105,19 @@
329 del.setAttribute('src', 'delete.png');
330 del.setAttribute('align', 'right');
331 del.onclick = function(){
332- this.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode);
333- Hub.send('delete_project', row.id)
334+ this.parentNode.parentNode.removeChild(this.parentNode);
335+ Hub.send('delete_project', id)
336+ var doc = novacut.get_sync(id)
337+ UI.add_history(id,doc.title,doc.time,countFiles(id));
338 }
339 li.appendChild(del);
340 thumb.onclick = function() {
341- Hub.send('load_project', row.id)
342+ Hub.send('load_project', id)
343 }
344 info.onclick = function() {
345- Hub.send('load_project', row.id)
346+ Hub.send('load_project', id)
347 }
348-
349- return li;
350- }
351- );
352- UI.items.select(UI.project.id);
353+ UI.proj.appendChild(li);
354 },
355
356 on_input: function(event) {

Subscribers

People subscribed via source and target branches

to all changes:
to status/vote changes: