From 17fb6581d305732e2cf0add7f3444e1aa80aec5c Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Fri, 19 Mar 2021 19:10:26 +0100 Subject: split tile handles into individual files. add video subsection loop --- frontend/app/views/tile/components/tile.list.js | 142 ++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 frontend/app/views/tile/components/tile.list.js (limited to 'frontend/app/views/tile/components/tile.list.js') diff --git a/frontend/app/views/tile/components/tile.list.js b/frontend/app/views/tile/components/tile.list.js new file mode 100644 index 0000000..c455489 --- /dev/null +++ b/frontend/app/views/tile/components/tile.list.js @@ -0,0 +1,142 @@ +import React, { Component } from 'react' +// import { Link } from 'react-router-dom' +import { bindActionCreators } from 'redux' +import { connect } from 'react-redux' +import { ReactSortable } from "react-sortablejs" + +// import actions from 'app/actions' +import * as tileActions from '../../tile/tile.actions' +import * as pageActions from '../../page/page.actions' + +const DOUBLE_CLICK_THRESHOLD = 250 + +class TileList extends Component { + state = { + tiles: [], + lastTargetId: 0, + lastTimeStamp: 0, + } + + // store doubleclick state as a class property because ReactSortable calls setState promiscuously + didDoubleClick = false + + componentDidMount(prevProps) { + const { tiles } = this.props.page.show.res + const { pages } = this.props.graph.show.res + const pageTitles = pages.reduce((a,b) => { + a[b.id] = b.title + return a + }, {}) + this.setState({ tiles: tiles.slice(0).reverse(), pageTitles }) + } + + componentDidUpdate(prevProps, prevState) { + if (this.didDoubleClick) return + const { tiles } = this.state + if (prevState.tiles.length && !pageActions.isSameTileOrder(tiles, prevState.tiles)) { + this.props.pageActions.setTileSortOrder(tiles.slice(0).reverse()) + } + // since we store the full tiles here (reversed!), they might change from under us + // potentially later refactor to only use a sort order / lookup + else if (prevProps.page.show.res.tiles !== this.props.page.show.res.tiles) { + const tileLookup = this.props.page.show.res.tiles.reduce((a,b) => { + a[b.id] = b + return a + }, {}) + const newTiles = this.state.tiles.map(tile => { + return tileLookup[tile.id] + }) + this.setState({ tiles: newTiles }) + } + } + + handleChoose(e) { + const { lastTargetId, lastTimeStamp } = this.state + if (lastTimeStamp + && parseInt(e.item.dataset.id) === lastTargetId + && (e.timeStamp - lastTimeStamp) < DOUBLE_CLICK_THRESHOLD + ) { + // console.log('selected', lastTargetId) + this.didDoubleClick = true + this.props.pageActions.showEditTileForm(lastTargetId) + } else { + this.setState({ + lastTargetId: parseInt(e.item.dataset.id), + lastTimeStamp: e.timeStamp, + }) + } + } + + handleUpdate(newTiles) { + if (this.didDoubleClick) return + this.setState({ tiles: newTiles }) + } + + render() { + const { tiles, pageTitles } = this.state + return ( +
+ this.handleUpdate(newTiles)} + onChoose={e => this.handleChoose(e)} + > + {tiles.map(tile => ( + tile.type === 'image' + ? + : tile.type === 'text' + ? + : tile.type === 'link' + ? + : + ))} + +
+ ) + } +} + +const TileListImage = ({ tile }) => ( +
+
+
+) + +const TileListText = ({ tile }) => ( +
+ {(tile.settings.content || "").substr(0, 100)} +
+) + +const TileListLink = ({ tile, pageTitles }) => ( +
+ + {'Link: '} + {tile.target_page_id === -1 + ? 'External' + : !tile.target_page_id + ? 'No link specified!' + : tile.target_page_id in pageTitles + ? pageTitles[tile.target_page_id] + : 'Error, broken link!'} + +
+) + +const TileListMisc = ({ tile }) => ( +
+ {"Tile: "}{tile.type} +
+) + +const mapStateToProps = state => ({ + graph: state.graph, + page: state.page, +}) + +const mapDispatchToProps = dispatch => ({ + tileActions: bindActionCreators({ ...tileActions }, dispatch), + pageActions: bindActionCreators({ ...pageActions }, dispatch), +}) + +export default connect(mapStateToProps, mapDispatchToProps)(TileList) -- cgit v1.2.3-70-g09d2 From d621365d3632b294c2c47f424786415c01c4cdf5 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Sat, 20 Mar 2021 15:10:12 +0100 Subject: add popup forms --- frontend/app/views/tile/components/tile.form.js | 44 ++++++++++++++++++++++++- frontend/app/views/tile/components/tile.list.js | 8 +++++ 2 files changed, 51 insertions(+), 1 deletion(-) (limited to 'frontend/app/views/tile/components/tile.list.js') diff --git a/frontend/app/views/tile/components/tile.form.js b/frontend/app/views/tile/components/tile.form.js index 49b34b1..7d0780d 100644 --- a/frontend/app/views/tile/components/tile.form.js +++ b/frontend/app/views/tile/components/tile.form.js @@ -65,10 +65,14 @@ const CURSORS = [ const NO_LINK = 0 const EXTERNAL_LINK = -1 +const OPEN_POPUP_LINK = -2 +const CLOSE_POPUP_LINK = -2 const PAGE_LIST_TOP_OPTIONS = [ { name: NO_LINK, label: 'No link' }, { name: EXTERNAL_LINK, label: 'External link' }, - { name: -2, label: '──────────', disabled: true }, + { name: OPEN_POPUP_LINK, label: 'Open popup' }, + { name: CLOSE_POPUP_LINK, label: 'Close popup' }, + { name: -3, label: '──────────', disabled: true }, ] // target_page_id = Column(Integer, ForeignKey('page.id'), nullable=True) @@ -754,6 +758,10 @@ class TileForm extends Component { const { temporaryTile } = this.props const { pageList } = this.state const isExternalLink = temporaryTile.target_page_id === EXTERNAL_LINK + const isPopupLink = ( + temporaryTile.target_page_id === OPEN_POPUP_LINK || + temporaryTile.target_page_id === CLOSE_POPUP_LINK + ) return (
@@ -860,6 +868,40 @@ class TileForm extends Component { step={1} type='int' /> + + {temporaryTile.settings.is_popup && ( + + )} + + {temporaryTile.settings.wait_to_appear && ( + + )}
) } diff --git a/frontend/app/views/tile/components/tile.list.js b/frontend/app/views/tile/components/tile.list.js index c455489..9ceb999 100644 --- a/frontend/app/views/tile/components/tile.list.js +++ b/frontend/app/views/tile/components/tile.list.js @@ -123,6 +123,14 @@ const TileListLink = ({ tile, pageTitles }) => (
) +const TileListVideo = ({ tile }) => { + return ( +
+ {"Vido: "}{tile.settings.url} +
+ ) +} + const TileListMisc = ({ tile }) => (
{"Tile: "}{tile.type} -- cgit v1.2.3-70-g09d2 From 50d5c3c2f10725af8ebb6db47c209f7000abc8f4 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Sat, 20 Mar 2021 19:24:13 +0100 Subject: remove foreignkey constraint on target_page_id. toggle popups. show list of popups, if a tile link is open/close popup --- cli/app/sql/models/page.py | 6 ++-- cli/app/sql/models/tile.py | 2 +- ...03201916_remove_foreign_key_constraint_from_.py | 29 ++++++++++++++++++ frontend/app/types.js | 2 +- frontend/app/views/page/components/page.editor.js | 2 ++ frontend/app/views/page/page.actions.js | 14 +++++++++ frontend/app/views/page/page.container.js | 1 + frontend/app/views/page/page.reducer.js | 12 +++++++- frontend/app/views/tile/components/tile.form.js | 34 ++++++++++++++++++---- frontend/app/views/tile/components/tile.list.js | 4 +++ frontend/app/views/tile/components/tile.new.js | 1 + frontend/app/views/tile/handles/tile.link.js | 3 -- 12 files changed, 95 insertions(+), 15 deletions(-) create mode 100644 cli/app/sql/versions/202103201916_remove_foreign_key_constraint_from_.py (limited to 'frontend/app/views/tile/components/tile.list.js') diff --git a/cli/app/sql/models/page.py b/cli/app/sql/models/page.py index 2f7065b..35efa39 100644 --- a/cli/app/sql/models/page.py +++ b/cli/app/sql/models/page.py @@ -1,11 +1,11 @@ from sqlalchemy import create_engine, Table, Column, Text, String, Integer, DateTime, JSON, ForeignKey -from sqlalchemy.orm import relationship +from sqlalchemy.orm import relationship, foreign, remote import sqlalchemy.sql.functions as func from sqlalchemy_utc import UtcDateTime, utcnow from wtforms_alchemy import ModelForm from app.sql.common import db, Base, Session -# from app.sql.models.graph import Graph +from app.sql.models.tile import Tile from app.settings import app_cfg @@ -23,7 +23,7 @@ class Page(Base): updated_at = Column(UtcDateTime(), onupdate=utcnow()) tiles = relationship("Tile", foreign_keys="Tile.page_id", lazy='dynamic', order_by="asc(Tile.sort_order)") - backlinks = relationship("Tile", foreign_keys="Tile.target_page_id", lazy='dynamic') + backlinks = relationship("Tile", primaryjoin=id == foreign(Tile.target_page_id), lazy='dynamic') def toJSON(self): return { diff --git a/cli/app/sql/models/tile.py b/cli/app/sql/models/tile.py index 3f6ce31..ed4a5f8 100644 --- a/cli/app/sql/models/tile.py +++ b/cli/app/sql/models/tile.py @@ -18,7 +18,7 @@ class Tile(Base): id = Column(Integer, primary_key=True) graph_id = Column(Integer, ForeignKey('graph.id'), nullable=True) page_id = Column(Integer, ForeignKey('page.id'), nullable=True) - target_page_id = Column(Integer, ForeignKey('page.id'), nullable=True) + target_page_id = Column(Integer, nullable=True) type = Column(String(16, convert_unicode=True), nullable=False) sort_order = Column(Integer, default=0) settings = Column(JSON, default={}, nullable=True) diff --git a/cli/app/sql/versions/202103201916_remove_foreign_key_constraint_from_.py b/cli/app/sql/versions/202103201916_remove_foreign_key_constraint_from_.py new file mode 100644 index 0000000..ed19feb --- /dev/null +++ b/cli/app/sql/versions/202103201916_remove_foreign_key_constraint_from_.py @@ -0,0 +1,29 @@ +"""remove foreign key constraint from target_page_id + +Revision ID: 9b687880918d +Revises: 3f7df6bf63b8 +Create Date: 2021-03-20 19:16:21.582373 + +""" +from alembic import op +import sqlalchemy as sa +import sqlalchemy_utc + + +# revision identifiers, used by Alembic. +revision = '9b687880918d' +down_revision = '3f7df6bf63b8' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint('tile_ibfk_3', 'tile', type_='foreignkey') + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_foreign_key('tile_ibfk_3', 'tile', 'page', ['target_page_id'], ['id']) + # ### end Alembic commands ### diff --git a/frontend/app/types.js b/frontend/app/types.js index bcd8053..f0f1e27 100644 --- a/frontend/app/types.js +++ b/frontend/app/types.js @@ -15,7 +15,7 @@ export const page = crud_type('page', [ 'update_page_tile', 'set_tile_sort_order', 'update_tile_sort_order', 'show_tile_list', 'hide_tile_list', 'toggle_tile_list', - 'toggle_popups', + 'toggle_popups', 'load_popups', 'toggle_sidebar_side', ]) diff --git a/frontend/app/views/page/components/page.editor.js b/frontend/app/views/page/components/page.editor.js index 2f14ffb..03190e3 100644 --- a/frontend/app/views/page/components/page.editor.js +++ b/frontend/app/views/page/components/page.editor.js @@ -174,6 +174,8 @@ class PageEditor extends Component { return (
{res.tiles && res.tiles.map(tile => { + console.log(tile.type, tile.settings.is_popup) + if (!this.props.page.editor.showingPopups && tile.settings.is_popup) return if (temporaryTile && temporaryTile.id === tile.id) { tile = temporaryTile } diff --git a/frontend/app/views/page/page.actions.js b/frontend/app/views/page/page.actions.js index c584848..0ae38e0 100644 --- a/frontend/app/views/page/page.actions.js +++ b/frontend/app/views/page/page.actions.js @@ -52,6 +52,20 @@ export const toggleTileList = () => dispatch => { // Popups +export const loadPopups = (page, popups) => dispatch => { + const state = store.getState() + page = page || state.page.show.res + popups = popups || state.page.editor.popups + popups = page.tiles.reduce((acc, tile) => { + const { is_popup, popup_group } = tile.settings + if (is_popup) { + acc[popup_group] = acc[popup_group] || false + } + return acc + }, { ...popups }) + console.log(popups) + dispatch({ type: types.page.load_popups, popups }) +} export const togglePopups = () => dispatch => { dispatch({ type: types.page.toggle_popups }) } diff --git a/frontend/app/views/page/page.container.js b/frontend/app/views/page/page.container.js index decdf79..0ad9806 100644 --- a/frontend/app/views/page/page.container.js +++ b/frontend/app/views/page/page.container.js @@ -44,6 +44,7 @@ class PageContainer extends Component { this.props.pageActions.showGraphAndPageIfUnloaded(this.props.match.params) .then(data => { actions.site.setSiteTitle(data.res.title) + this.props.pageActions.loadPopups(data.res, {}) if (!data.res.tiles.length) { this.props.pageActions.showAddTileForm() } else { diff --git a/frontend/app/views/page/page.reducer.js b/frontend/app/views/page/page.reducer.js index b0c4553..a1f281a 100644 --- a/frontend/app/views/page/page.reducer.js +++ b/frontend/app/views/page/page.reducer.js @@ -11,6 +11,7 @@ const initialState = crudState('page', { tileList: false, showingPopups: true, sidebarOnRight: true, + popups: {}, }, options: { } @@ -202,7 +203,16 @@ export default function pageReducer(state = initialState, action) { ...state, editor: { ...state.editor, - togglePopups: !state.editor.togglePopups, + showingPopups: !state.editor.showingPopups, + } + } + + case types.page.load_popups: + return { + ...state, + editor: { + ...state.editor, + popups: action.popups, } } diff --git a/frontend/app/views/tile/components/tile.form.js b/frontend/app/views/tile/components/tile.form.js index 728bc05..b33f7b8 100644 --- a/frontend/app/views/tile/components/tile.form.js +++ b/frontend/app/views/tile/components/tile.form.js @@ -76,6 +76,12 @@ const PAGE_LIST_TOP_OPTIONS = [ { name: -99, label: '──────────', disabled: true }, ] +const NO_POPUP = 0 +const POPUP_LIST_TOP_OPTIONS = [ + { name: NO_POPUP, label: 'Select a popup group' }, + { name: -99, label: '──────────', disabled: true }, +] + // target_page_id = Column(Integer, ForeignKey('page.id'), nullable=True) // https://s3.amazonaws.com/i.asdf.us/im/1c/gradient_gold1-SpringGreen1_1321159749.jpg @@ -181,6 +187,7 @@ class TileForm extends Component { errorFields: new Set([]), modified: false, pageList: [], + popupList: [], } constructor(props){ @@ -211,7 +218,11 @@ class TileForm extends Component { ...PAGE_LIST_TOP_OPTIONS, ...linkPages.map(page => ({ name: page.id, label: page.path })) ] - this.setState({ pageList }) + let popupList = [ + ...POPUP_LIST_TOP_OPTIONS, + ...Object.keys(page.editor.popups).map(popup_group => ({ name: popup_group, label: popup_group })) + ] + this.setState({ pageList, popupList }) if (isNew) { const newTile = newImage({ id: "new", @@ -760,7 +771,7 @@ class TileForm extends Component { renderHyperlinkForm() { const { temporaryTile } = this.props - const { pageList } = this.state + const { pageList, popupList } = this.state const isExternalLink = temporaryTile.target_page_id === EXTERNAL_LINK const isPopupLink = ( temporaryTile.target_page_id === OPEN_POPUP_LINK || @@ -785,8 +796,8 @@ class TileForm extends Component { onChange={this.handleSettingsSelect} />
-
- {isExternalLink && + {isExternalLink && ( +
- } -
+
+ )} + {(temporaryTile.target_page_id === OPEN_POPUP_LINK || temporaryTile.target_page_id === CLOSE_POPUP_LINK) && ( +
+