diff --git a/src/PhotoTableLine.css b/src/TableLine.css similarity index 72% rename from src/PhotoTableLine.css rename to src/TableLine.css index 5a5702e..1e2fd4f 100644 --- a/src/PhotoTableLine.css +++ b/src/TableLine.css @@ -1,27 +1,27 @@ -.PhotoTableLine_body { +.TableLine_body { border: 2px solid black; background-color: lightgrey; height:80px; } -.PhotoTableLine_thumb { +.TableLine_thumb { height: inherit; } -.PhotoTableLine_field { +.TableLine_field { border: 2px solid black; float: left; height: inherit; } -.PhotoTableLine_textsubfield { +.TableLine_textsubfield { display: flex; align-items: center; justify-content: center; height: inherit; } -.PhotoTableLine_textsubsubfield { +.TableLine_textsubsubfield { margin: 8px; border: 0 solid black; } diff --git a/src/database.js b/src/database.js index 7a0775f..04a0efb 100644 --- a/src/database.js +++ b/src/database.js @@ -15,7 +15,9 @@ export const DBSourceEnum = { export function alasql_async_queries(alasql_object, queries) { var p = Promise.resolve(null); for (let i = 0; i < queries.length; i++) { - p = p.then(() => { return alasql_object.promise(queries[i]); }); + p = p.then(() => { + return alasql_object.promise(queries[i]); + }); } return p; diff --git a/src/index.js b/src/index.js index ec8ac02..1f8f333 100644 --- a/src/index.js +++ b/src/index.js @@ -2,8 +2,8 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { Fetch } from './fetch.js'; import { ProvideDB, DBQueryConsole, DBQueryBar, DBTypeEnum, DBSourceEnum } from './database.js'; -import { PhotoView, PhotoThumbView, PhotoTableLine } from './media.js'; -import { do_image_query, image_query_with_where, user_query_from_search_string, maybe_image_query } from './queries.js'; +import { PhotoTableLine, MediaTableLine } from './media.js'; +import { do_image_query, do_album_query, image_query_with_where, user_query_from_search_string, maybe_image_query, maybe_album_query } from './queries.js'; import './index.css'; import { thisExpression } from '@babel/types'; @@ -80,11 +80,11 @@ export class ImageWhereConsole extends React.Component { } } -const PhotosTable = ({ photos }) => ( +const MediaTable = ({ items }) => ( <> { - photos.map((value, index) => { - return + items.map((value, index) => { + return value && }) } @@ -94,19 +94,26 @@ export class TestDBStringQuery extends React.Component { state = { string: false, query: false, - sql_query: false, - photos: false + sql_image_query: false, + sql_album_query: false, + photos: false, + albums: false, } onQueryChangeHandler = str => { this.setState({ string: str }); } onQuerySubmitHandler = () => { var q = user_query_from_search_string(this.state.string); - var sql_q = maybe_image_query(q); - this.setState({ query: q, sql_query: sql_q }); - do_image_query(sql_q, this.props.db, "/test_photos", "/test_photos_thumbs").then(photos => { + var sql_image_query = maybe_image_query(q); + var sql_album_query = maybe_album_query(q); + this.setState({ query: q, sql_image_query: sql_image_query, sql_album_query: sql_album_query, photos: false, albums: false }); + do_image_query(sql_image_query, this.props.db, "/test_photos", "/test_photos_thumbs").then(photos => { console.log(photos); this.setState({ done: true, photos: photos }); }); + do_album_query(sql_album_query, this.props.db, "/test_photos", "/test_photos_thumbs").then(albums => { + console.log(albums); + this.setState({ done: true, albums: albums }); + }); } render() { @@ -117,7 +124,9 @@ export class TestDBStringQuery extends React.Component { { this.state.photos &&

Found {this.state.photos.length} photos.

} - { this.state.photos && } + { this.state.photos && } + { this.state.albums &&

Found {this.state.albums.length} albums.

} + { this.state.albums && } ); diff --git a/src/media.js b/src/media.js index 23254c0..7df9578 100644 --- a/src/media.js +++ b/src/media.js @@ -1,7 +1,8 @@ import React from 'react'; -import './PhotoTableLine.css'; +import './TableLine.css'; +import { findAllByDisplayValue } from '@testing-library/dom'; -export class Media {} +export class Media { } export class Photo extends Media { state = { @@ -12,34 +13,74 @@ export class Photo extends Media { } } +export class Album extends Media { + state = { + id: false, + name: false, // Equal to the last item in the relativePath + relative_path: false, + } +} + export function create_photo(maybe_id, maybe_name, maybe_path, maybe_thumbnail_path) { var p = new Photo(); - if(maybe_id) { p.state.id = maybe_id; } - if(maybe_name) { p.state.name = maybe_name; } - if(maybe_path) { p.state.path = maybe_path; } - if(maybe_thumbnail_path) { p.state.thumbnailpath = maybe_thumbnail_path; } + if (maybe_id) { p.state.id = maybe_id; } + if (maybe_name) { p.state.name = maybe_name; } + if (maybe_path) { p.state.path = maybe_path; } + if (maybe_thumbnail_path) { p.state.thumbnailpath = maybe_thumbnail_path; } return p; } -export const PhotoView = ({ photo }) => ; +export function create_album(maybe_id, maybe_name, maybe_relative_path) { + var a = new Album(); + if (maybe_id) { a.state.id = maybe_id; } + if (maybe_name) { a.state.name = maybe_name; } + if (maybe_relative_path) { a.state.relative_path = maybe_relative_path; } + return a; +} + +export const PhotoView = ({ photo }) => ; export class PhotoThumbView extends React.Component { render() { return ( - <> - - - - + <> + + + + ); } } export const PhotoTableLine = ({ photo }) => ( <> -
-
-

{photo.state.name ? photo.state.name : "Unknown"}

-
+
+
+

{photo.state.name ? photo.state.name : "Unknown name"}

+
+ +) + +export const AlbumTableLine = ({ album }) => ( + <> +
+

Album

+

{album.state.name ? album.state.name : "Unknown name"}

+

{album.state.relative_path ? album.state.relative_path : "Unknown relative path"}

+
+ +) + +export const MediaTableLine = ({ media }) => ( + <> + { + (() => { + if (media instanceof Photo) { + return ; + } else if (media instanceof Album) { + return ; + } + })() + } ) \ No newline at end of file diff --git a/src/queries.js b/src/queries.js index 8a1c8dd..bc1fba6 100644 --- a/src/queries.js +++ b/src/queries.js @@ -1,5 +1,9 @@ -import { Photo, PhotoView, PhotoThumbView, create_photo } from './media.js'; +import { create_photo, create_album } from './media.js'; + +export function escape_regex(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string +} export function do_image_query(query, database, collection_path, collection_thumbs_path) { return new Promise(function (resolve, reject) { @@ -19,9 +23,26 @@ export function do_image_query(query, database, collection_path, collection_thum }); } +export function do_album_query(query, database, collection_path, collection_thumbs_path) { + return new Promise(function (resolve, reject) { + var queries = []; + queries.push(query); + database.queries_async(queries).then(res => { + var albums = []; + if (res && Array.isArray(res)) { + res.forEach(row => { + var album_name = new String(row["relativePath"]).substring(row["relativePath"].lastIndexOf('/') + 1); + albums.push(create_album(row["id"], album_name, row["relativePath"])); + }); + } + resolve(albums); + }); + }); +} + export const MatchTypeEnum = { MATCH_EQUALS: 1, - MATCH_LIKE: 2, + MATCH_REGEXP: 2, }; export const MatchAgainstEnum = { @@ -52,24 +73,6 @@ export class ConstFilter extends ResultFilter { } } -function match_column_name(result_type, match_against) { - if(match_against == MatchAgainstEnum.MATCH_IMAGE_NAME) { - return "Images.name"; - } - if(match_against == MatchAgainstEnum.MATCH_ALBUM_NAME) { - return "Album.name"; - } - if(match_against == MatchAgainstEnum.MATCH_RESULT_NAME) { - if(result_type == ResultTypeEnum.IMAGE) { - return match_column_name(result_type, MatchAgainstEnum.MATCH_IMAGE_NAME); - } - if(result_type == ResultTypeEnum.ALBUM) { - return match_column_name(result_type, MatchAgainstEnum.MATCH_ALBUM_NAME); - } - } - throw new Error("Could not get column name for matching properties passed."); -} - export class MatchingFilter extends ResultFilter { constructor(rtype, against, from, mtype, negate) { super(rtype); @@ -79,8 +82,8 @@ export class MatchingFilter extends ResultFilter { this.negate = negate; } - // What kind of filtering to apply - match_against = MatchAgainstEnum.MATCH_RESULT_NAME; + // What string to match against, e.g. "Image.name" (field) or "\"Image.name\"" (literal) + match_against = ""; // optional string used in the filtering match_from = ""; @@ -92,18 +95,17 @@ export class MatchingFilter extends ResultFilter { negate = false; to_sql_where() { - var match_against_str = match_column_name(this.result_type, this.match_against); var match_type_str = false; if(this.match_type == MatchTypeEnum.MATCH_EQUALS) { match_type_str = "="; - } else if(this.match_type == MatchTypeEnum.MATCH_LIKE) { - match_type_str = " LIKE "; + } else if(this.match_type == MatchTypeEnum.MATCH_REGEXP) { + match_type_str = " REGEXP "; } else { throw new Error('Unsupported match type: ' + this.match_type); } - return "(" + (this.negate ? "NOT " : "") + match_against_str + match_type_str + this.match_from + ")"; + return "(" + (this.negate ? "NOT " : "") + this.match_against + match_type_str + this.match_from + ")"; } } @@ -143,6 +145,11 @@ export function image_query_with_where(maybe_where) { return "SELECT Images.id, Images.name, Images.uniqueHash, Albums.relativePath FROM Images INNER JOIN Albums ON Images.album=Albums.id " + (maybe_where ? maybe_where : "") + ";"; } +// This query will return database entries with the fields "id" and "relativePath" for each matching album. +export function album_query_with_where(maybe_where) { + return "SELECT Albums.id, Albums.relativePath FROM Albums " + (maybe_where ? maybe_where : "") + ";"; +} + export function maybe_image_query(user_query) { var where = false; if(user_query.image_filter) { @@ -151,9 +158,31 @@ export function maybe_image_query(user_query) { return image_query_with_where(where); } +export function maybe_album_query(user_query) { + var where = false; + if(user_query.album_filter) { + where = "WHERE " + user_query.album_filter.to_sql_where(); + } + return album_query_with_where(where); +} + function filter_from_text_segment(result_type, segment) { - return new MatchingFilter(result_type, MatchAgainstEnum.MATCH_RESULT_NAME, - "\"" + segment['text'] + "\"", MatchTypeEnum.MATCH_EQUALS, segment['negated']); + var match_type = false; + var match_text = false; + var match_against = false; + + if(result_type == ResultTypeEnum.IMAGE) { + match_type = MatchTypeEnum.MATCH_EQUALS; + match_text = "\"" + segment['text'] + "\""; + match_against = "Images.name"; + } else if(result_type == ResultTypeEnum.ALBUM) { + // Match against the album "name", which is the basename of its relative path. + match_type = MatchTypeEnum.MATCH_REGEXP; + match_text = "\"" + '\/(.*\/)*' + escape_regex(segment['text']) + "\""; + match_against = "Albums.relativePath"; + } + + return new MatchingFilter(result_type, match_against,match_text, match_type, segment['negated']); } export function user_query_from_search_string(search_string) { @@ -176,6 +205,7 @@ export function user_query_from_search_string(search_string) { console.log(search_string); console.log(r); console.log(maybe_image_query(r)); + console.log(maybe_album_query(r)); return r; } \ No newline at end of file