#!/usr/bin/env python3 # -*- coding:utf-8 -*- from pscript import RawJS from flexx import flx from ipfsdocs.placeholder import PlaceHolder from ipfsdocs.common import ( URL, positivemod, set_to_localstorage, get_from_localstorage, ) from ipfsdocs.log import info class Slide(flx.Layout): index = flx.IntProp(0, settable=True) thumbnail_width = flx.IntProp(1024, settable=True) CSS = """ .slide_placeholder { margin: auto; } .flx-Slide { overflow-x: scroll; } /* from https://www.w3schools.com/howto/tryit.asp?filename=tryhow_css_hide_scrollbar_keep_func */ .flx-Slide::-webkit-scrollbar { display: none; } /* Hide scrollbar for IE, Edge and Firefox */ .flx-Slide { -ms-overflow-style: none; /* IE and Edge */ scrollbar-width: none; /* Firefox */ } """ def _create_dom(self): return [ flx.create_element("div"), ] def _render_dom(self): return [ flx.create_element( "table", {}, flx.create_element( "tr", {}, [ flx.create_element( "td", {}, child.outernode ) for child in self.children ] ) ), ] @flx.reaction("index") def save_index(self): set_to_localstorage("slide_index", {"slide_index": self.index}) @flx.reaction("index") def pause_video(self, *events): event = events[-1] if event.old_value != event.new_value and event.old_value is not None: self.placeholders[event.old_value].pause_video() def is_visible(self): return any([placeholder.visible for placeholder in self.placeholders]) @flx.reaction( "root.state.autoplay", "placeholders*.visible", "placeholders*.stopped_video", "placeholders*.ended_video", "placeholders*.playing_video", "root.jssays.contextmenu", "root.jssays.contextmenu_exit", "root.jssays.pinching_placeholder", "root.jssays.pinched_placeholder", "root.frontend.inmodal", ) def update_autoplay(self, *events): event = events[-1] if ( self.root.state.autoplay and not event.type == "playing_video" and not event.type == "contextmenu" and not event.type == "pinching_placeholder" and not self.root.frontend.inmodal and self.is_visible() ): self.start_autoplay_timer() else: self.stop_autoplay_timer() def start_autoplay_timer(self): global window if self.autoplay_interval is not None: window.clearInterval(self.autoplay_interval) self.autoplay_interval = window.setInterval(self.next, 60000) def stop_autoplay_timer(self): global window window.clearInterval(self.autoplay_interval) self.autoplay_interval = None @flx.reaction("key_down") def on_key_down(self, *events): if self.root.frontend.inmodal: return for event in events: if event.key == "ArrowRight": self.next() elif event.key == "ArrowLeft": self.previous() elif event.key == " ": self.placeholders[self.index].toggle_selection() elif event.key == "d": self.placeholders[self.index].ask_delete() elif event.key == "t": self.placeholders[self.index].toggle_video() def page_next(self): self.set_index(0) self.root.state.next_page() def page_previous(self): self.set_index(self.root.state.number_per_page - 1) self.root.state.previous_page() def next(self): index = self.index + 1 if ( self.root.state.in_last_page and index >= self.root.state.number_of_elements_on_this_page ): self.goto_first() elif index >= self.root.state.number_per_page: self.page_next() else: self.set_index(index) def previous(self): index = self.index - 1 if ( self.root.state.in_first_page and index < 0 ): self.goto_last() elif index < 0: self.page_previous() else: self.set_index(index) def goto_last(self): index = positivemod( self.root.state.number_of_elements - 1, self.root.state.number_per_page ) self.set_index(index) self.root.state.goto_last_page() def goto_first(self): self.set_index(0) self.root.state.goto_first_page() @flx.reaction("index", "placeholders*.size") def scroll_to_current_index(self, *ev): # the scroll may be impacted by the index change or the window resizing self.scroll_to_placeholder( self.placeholders[self.index] ) @flx.reaction("index") def adjust_load(self): for i, placeholder in enumerate(self.placeholders): placeholder.set_load( self.index - 2 <= i <= self.index + 2 ) def scroll_to_placeholder(self, placeholder): node = placeholder.node RawJS("$")(self.outernode).animate( { "scrollLeft": node.offsetLeft, }, 100, "swing", ) @flx.reaction("size") def tell_size(self): for placeholder in self.placeholders: placeholder.apply_style(f"height: {self.size[1]}px; width: {self.size[0]}px; ") self.scroll_to_current_index() @flx.reaction("root.state.focused_cid") def update_index_on_focus(self, *events): for index, placeholder in enumerate(self.placeholders): if placeholder.cid == self.root.state.focused_cid: self.set_index(index) @flx.reaction("root.state.query_results", "root.state.page", "root.state.number_per_page") def new_query_results(self, *events): global undefined, moment for i, placeholder in enumerate(self.placeholders): row = self.root.state.query_results[i + (self.root.state.page * self.root.state.number_per_page)] placeholder.show() if row == undefined: placeholder.set_html("Nothing else") else: placeholder.set_url(f'{row["cid"]}?filename={row["filename"]}') placeholder.set_cid(row["cid"]) placeholder.set_webcid(row["web_cid"]) placeholder.set_filename(row["filename"]) placeholder.set_mimetype(row["mimetype"]) placeholder.update_tags(row["tags"]) title = f"{moment(row['date']).format('LLLL')}: {row['description']}" placeholder.set_description(title) if row["class"] == "photo": placeholder.set_html( f'' f' ' ) elif row["class"] == "video": placeholder.set_html( f'' f' ' ) else: placeholder.set_html(f"euh: {row['class']}") def init(self): self.placeholders = [] self.setup_placeholders() self.node.onscroll = self.onscroll self.idle_timer = None self.autoplay_interval = None self.set_index( get_from_localstorage("slide_index")["slide_index"] or 0 ) def on_swipeleft(self): if self.root.frontend.inmodal: return self.next() def on_swiperight(self): if self.root.frontend.inmodal: return self.previous() def intersect_debug(self, entries): for entry in entries: entry.target.flx_parent.set_visible(entry.isIntersecting) entry.target.flx_parent.set_intersection_ratio( entry.intersectionRatio ) @flx.reaction("root.state.number_per_page") def setup_placeholders(self): global Hammer global IntersectionObserver self.observer = IntersectionObserver( self.intersect_debug, { "root": self.node, "threshold": 0.5, "rootMargin": "100%", }, ) for placeholder in self.placeholders: placeholder.dispose() with self: self.placeholders = [ PlaceHolder(custom_css_class="slide_placeholder") for i in range(self.root.state.number_per_page) ] for placeholder in self.placeholders: self.observer.observe(placeholder.node) placeholder.hammer.on("swipeleft", lambda: self.on_swipeleft()) placeholder.hammer.on("swiperight", lambda: self.on_swiperight())