Mercurial > libervia-web
view libervia/pages/photos/album/_browser/__init__.py @ 1330:b525fdcb393b
browser (photos/album): used biggest thumbnails instead of original image for slideshow:
biggest thumbnail should be adapted to fullscreen slideshow, and it's better to use than
original image which may have a size of several megabytes.
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 14 Aug 2020 09:31:32 +0200 |
parents | 0cbf86b1dcca |
children | fe353fceec38 |
line wrap: on
line source
from browser import document, window, bind, html, DOMNode, timer from javascript import JSON from bridge import Bridge from template import Template import dialog from slideshow import SlideShow files_service = window.files_service files_path = window.files_path bridge = Bridge() # file upload def on_progress(ev, photo_elt): if ev.lengthComputable: percent = int(ev.loaded/ev.total*100) update_progress(photo_elt, percent) def on_load(file_, photo_elt): update_progress(photo_elt, 100) photo_elt.classList.add("progress_finished") photo_elt.classList.remove("progress_started") photo_elt.select_one('.action_delete').bind("click", on_delete) print(f"file {file_.name} uploaded correctly") def on_error(failure, file_, photo_elt): # TODO: cleaner error notification window.alert(f"can't upload {file_.name}: failure") def update_progress(photo_elt, new_value): progress_elt = photo_elt.select_one("progress") progress_elt.value = new_value progress_elt.text = f"{new_value}%" def on_slot_cb(file_, upload_slot, photo_elt): put_url, get_url, headers = upload_slot xhr = window.XMLHttpRequest.new() xhr.open("PUT", put_url, True) xhr.upload.bind('progress', lambda ev: on_progress(ev, photo_elt)) xhr.upload.bind('load', lambda ev: on_load(file_, photo_elt)) xhr.upload.bind('error', lambda ev: on_error(xhr.response, file_, photo_elt)) xhr.setRequestHeader('Xmpp-File-Path', window.encodeURIComponent(files_path)) xhr.setRequestHeader('Xmpp-File-No-Http', "true") xhr.send(file_) def on_slot_eb(file_, failure, photo_elt): breakpoint() def upload_files(files): print(f"uploading {len(files)} files") photo_tpl = Template('photo/item.html') album_items = document['album_items'] for file_ in files: url = window.URL.createObjectURL(file_) photo_elt = photo_tpl.get_elt({ "file": { "name": file_.name, # we don't want to open the file on click, it's not yet the # uploaded URL "url": url, # we have no thumb yet, so we use the whole image # TODO: reduce image for preview "thumb_url": url, }, "uploading": True, }) photo_elt.classList.add("progress_started") album_items <= photo_elt bridge.fileHTTPUploadGetSlot( file_.name, file_.size, file_.type or '', files_service, callback=lambda upload_slot, file_=file_, photo_elt=photo_elt: on_slot_cb(file_, upload_slot, photo_elt), errback=lambda failure, file_=file_, photo_elt=photo_elt: on_slot_eb(file_, failure, photo_elt), ) @bind("#file_drop", "drop") def on_file_select(evt): evt.stopPropagation() evt.preventDefault() files = evt.dataTransfer.files upload_files(files) @bind("#file_drop", "dragover") def on_drag_over(evt): evt.stopPropagation() evt.preventDefault() evt.dataTransfer.dropEffect = 'copy' @bind("#file_input", "change") def on_file_input_change(evt): files = evt.currentTarget.files upload_files(files) # delete def file_delete_cb(item_elt, item): item_elt.classList.add("state_deleted") item_elt.bind("transitionend", lambda evt: item_elt.remove()) print(f"deleted {item['name']}") def file_delete_eb(failure, item_elt, item): # TODO: cleaner error notification window.alert(f"error while deleting {item['name']}: failure") def delete_ok(evt, notif_elt, item_elt, item): file_path = f"{files_path.rstrip('/')}/{item['name']}" bridge.fileSharingDelete( files_service, file_path, "", callback=lambda : file_delete_cb(item_elt, item), errback=lambda failure: file_delete_eb(failure, item_elt, item), ) def delete_cancel(evt, notif_elt, item_elt, item): notif_elt.remove() item_elt.classList.remove("selected_for_deletion") def on_delete(evt): evt.stopPropagation() target = evt.currentTarget item_elt = DOMNode(target.elt.closest('.item')) item_elt.classList.add("selected_for_deletion") item = JSON.parse(item_elt.dataset.item) dialog.Confirm( f"{item['name']!r} will be deleted, are you sure?", ok_label="delete", ).show( ok_cb=lambda evt, notif_elt: delete_ok(evt, notif_elt, item_elt, item), cancel_cb=lambda evt, notif_elt: delete_cancel(evt, notif_elt, item_elt, item), ) # slideshow @bind(".photo_thumb_click", "click") def photo_click(evt): evt.stopPropagation() evt.preventDefault() slideshow = SlideShow() target = evt.currentTarget clicked_item_elt = DOMNode(target.elt.closest('.item')) slideshow.attach() for idx, item_elt in enumerate(document.select('.item')): item = JSON.parse(item_elt.dataset.item) try: biggest_thumb = item['extra']['thumbnails'][-1] thumb_url = f"{cache_path}{biggest_thumb['filename']}" except (KeyError, IndexError) as e: print(f"Can't get full screen thumbnail URL: {e}") thumb_url = item['url'] slideshow.add_slide(html.IMG(src=thumb_url, Class="slide_img"), item) if item_elt == clicked_item_elt: slideshow.index = idx for elt in document.select('.action_delete'): elt.bind("click", on_delete) # manage def on_manager_close(manager_panel_elt): side_panel = manager_panel_elt.select_one('.invitation_manager_side_panel') side_panel.classList.remove('open') side_panel.bind("transitionend", lambda evt: manager_panel_elt.remove()) def _on_invitation_cb(field_elt, entity): print(f"invitation for {entity!r} sent successfully") submit_elt = document['invitation_submit'] submit_elt.disabled = False form_elt = document['invitation_form'] form_elt.disabled = False jids_elt = form_elt.select_one('*[name="jids"]') emails_elt = form_elt.select_one('*[name="emails"]') new = [d.strip() for d in field_elt.value.split('\n') if d and d.strip() != entity] field_elt.value = '\n'.join(new) if not jids_elt.value.strip() and not emails_elt.value.strip(): # FIXME: Q&D notification, needs to do this properly in a separated module with # some animations notifs_elt = document['invitation_notifications'] notification_elt = html.DIV(Class="notification is-success has-text-centered") notification_elt <= "invitations sent successfully" notifs_elt <= notification_elt timer.set_timeout(lambda: notification_elt.remove(), 5000) def invitationSimpleCreateCb(invitation_data, email): invitee_jid = invitation_data['jid'] album_name = files_path.rsplit('/')[-1] form_elt = document['invitation_form'] emails_elt = form_elt.select_one('*[name="emails"]') bridge.FISInvite( invitee_jid, files_service, "photos", "", files_path, album_name, '', callback=lambda: _on_invitation_cb(emails_elt, email), errback=lambda e: window.alert(f"invitation failed for {email}: {e}") ) def invite_by_email(email): guest_url_tpl = f'{window.URL.new("/g", document.baseURI).href}/{{uuid}}' bridge.invitationSimpleCreate( email, guest_url_tpl, '', callback=lambda data: invitationSimpleCreateCb(data, email), errback=lambda e: window.alert(f"can't send email invitation: {e}") ) def on_invitation_submit(evt, manager_panel_elt): evt.stopPropagation() evt.preventDefault() submit_elt = document['invitation_submit'] submit_elt.disabled = True form_elt = document['invitation_form'] form_elt.disabled = True jids_elt = form_elt.select_one('*[name="jids"]') emails_elt = form_elt.select_one('*[name="emails"]') jids = [j.strip() for j in jids_elt.value.split('\n') if j.strip()] emails = [e.strip() for e in emails_elt.value.split('\n') if e.strip()] album_name = files_path.rsplit('/')[-1] for entity_jid in jids: print(f"inviting {entity_jid}") bridge.FISInvite( entity_jid, files_service, "photos", "", files_path, album_name, '', callback=lambda entity=entity_jid: _on_invitation_cb(jids_elt, entity), errback=lambda e: window.alert(f"invitation failed: {e}") ) for email in emails: invite_by_email(email) print(f"{jids=}, {emails=}") def _FISAffiliationSetCb(affiliation_elt): affiliation_elt.remove() def on_affiliation_remove(entity_jid, affiliation_elt): bridge.FISAffiliationsSet( files_service, "", files_path, {entity_jid: "none"}, callback=lambda: _FISAffiliationSetCb(affiliation_elt), errback=lambda e: window.alert(f"can't remove affiliation: {e}") ) @bind("#button_manage", "click") def manage_click(evt): evt.stopPropagation() evt.preventDefault() manager_panel_tpl = Template('invitation/manager.html') manager_panel_elt = manager_panel_tpl.get_elt() document.body <= manager_panel_elt document['invitation_submit'].bind( "click", lambda evt: on_invitation_submit(evt, manager_panel_elt)) side_panel = manager_panel_elt.select_one('.invitation_manager_side_panel') timer.set_timeout(lambda: side_panel.classList.add("open"), 0) for close_elt in manager_panel_elt.select('.click_to_close'): close_elt.bind("click", lambda evt: on_manager_close(manager_panel_elt)) side_panel.bind("click", lambda evt: evt.stopPropagation()) affiliations = window.affiliations.to_dict() affiliation_tpl = Template('invitation/affiliation_item.html') for entity_jid, affiliation in affiliations.items(): affiliation_elt = affiliation_tpl.get_elt({ "entity_jid": entity_jid, "affiliation": affiliation, }) document['affiliations'] <= affiliation_elt for elt in affiliation_elt.select(".click_to_delete"): elt.bind( "click", lambda evt, entity_jid=entity_jid, affiliation_elt=affiliation_elt: on_affiliation_remove( entity_jid, affiliation_elt ) )