Merge lp:~mttronchetti/novacut/history into lp:novacut
- history
- Merge into trunk
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jason Gerard DeRose | Approve | ||
Review via email: mp+109928@code.launchpad.net |
Commit message
Description of the change
Added lazy delete for project with a cool drop down menu called history
- 272. By Matteo Ronchetti
-
added possibility to duplicate a slice with d button on keyboard
- 273. By Matteo Ronchetti
-
added possibility to duplicate a slice with d button on keyboard
- 274. By Matteo Ronchetti
-
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!
Preview Diff
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) { |
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!