+
+
+
+
+ `;
}
@@ -156,40 +162,44 @@ interface Upload{
return html`
-
+
+ ${this.t("ui.upload")}
+
+ ${this.t("info.useStandalone")}
+
+ Tools
+ ${this.t("ui.searchScene")} +
+
- ${(recentScenes.some(s=>myScenes.indexOf(s) == -1))? html`${this.t("ui.mtimeSection")}
+
+
+ ${this.t("ui.name")}
+ ${this.t("ui.mtime")}
+
+ ${repeat([
+ ...scenes.slice(0, 8),
+ ],({name})=>name , (scene)=>this.renderSceneCompact(scene))}
- `: null}
-
-
`: null}
-
- ${this.t("ui.ctimeSection")}
-
- ${repeat([
- ...recentScenes,
- ],({name})=>name , (scene)=>this.renderScene(mode, scene))}
-
-
`}
diff --git a/source/ui/screens/List.ts b/source/ui/screens/List.ts
index 06dbda83..151d1713 100644
--- a/source/ui/screens/List.ts
+++ b/source/ui/screens/List.ts
@@ -6,6 +6,7 @@ import "../composants/UploadButton";
import "./LandingPage";
import "../composants/SceneCard";
import "../composants/ListItem";
+import "../composants/TagList";
import spinnerImage from "../assets/images/spinner.svg";
@@ -15,6 +16,7 @@ import { repeat } from "lit-html/directives/repeat";
import "../composants/TaskButton";
import { withScenes, Scene, sorts, OrderBy } from "../state/withScenes";
+import { navigate } from "../state/router";
interface Upload{
name :string;
@@ -28,6 +30,9 @@ interface Upload{
export default class List extends withScenes( withUser( i18n( LitElement )))
{
+ @property({attribute:true, type: String})
+ search ?:string;
+
@property({type: Object})
uploads :{[name :string]:{
error ?:{code?:number,message:string},
@@ -133,6 +138,7 @@ interface Upload{
}
protected render() :TemplateResult {
+ console.log("Render :", window.location.href);
if(!this.isUser){
return html`${this.t("ui.mtimeSection")}
-
-
- ${this.t("ui.name")}
- ${this.t("ui.author")}
- ${this.t("ui.mtime")}
-
- ${repeat([
- ...scenes.slice(0, 8),
- ],({name})=>name , (scene)=>this.renderSceneCompact(scene))}
+
+
+
+
+ `: (myScenes.length > 0) ?
+ html`
+
+ ${(recentScenes.some(s=>myScenes.indexOf(s) == -1))? html`${this.t("ui.myScenes")}
+ ${uploads.length !== 0? html`
+ ${myScenes.map((scene)=>this.renderScene(mode, scene))}
+
+ `: null}
+
+
`: null}
+
${this.t("ui.ctimeSection")}
+
+ ${repeat([
+ ...recentScenes,
+ ],({name})=>name , (scene)=>this.renderScene(mode, scene))}
+
+
-
-
-
+
+
+
-
+
-
-
- ${this.t("ui.upload")}
-
-
- ${this.t("info.useStandalone")}
+
- ${this.t("ui.newScene")}
-
+ ${this.t("ui.sortBy")}
+
- ${(this.selection.length)?html`
-
-
`: null}
- ${this.t("ui.tools")}
- - Download Zip - -
-
- ${this.t("ui.sortBy")}
-
+
+
+
`;
}
@@ -251,8 +262,7 @@ interface Upload{
onSearchChange = (ev)=>{
ev.preventDefault();
- this.match = ev.target.value;
- this.fetchScenes()
+ navigate(this, null, {search: ev.target.value});
console.log("list items find : ",this.list)
}
diff --git a/source/ui/screens/SceneHistory.ts b/source/ui/screens/SceneHistory.ts
index 03b489ed..388c9127 100644
--- a/source/ui/screens/SceneHistory.ts
+++ b/source/ui/screens/SceneHistory.ts
@@ -5,6 +5,7 @@ import Notification from "../composants/Notification";
import "../composants/Button";
import "../composants/Spinner";
import "../composants/Size";
+import "../composants/TagList";
import { nothing } from "lit-html";
import i18n from "../state/translate";
@@ -12,6 +13,7 @@ import { withUser } from "../state/auth";
import { navigate } from "../state/router";
import Modal from "../composants/Modal";
import { AccessType, Scene } from "state/withScenes";
+import HttpError from "../state/HttpError";
const AccessTypes = [
@@ -196,10 +198,19 @@ class SceneVersion{
let scene = encodeURIComponent(this.name);
return html`
+
-
+
+
+ ${(this.selection.length)?html`
+
+
+
`: null}
+ ${this.t("ui.tools")}
+ + Download Zip + +
${this.error? html`
`}
+
Error
${this.error} @@ -205,6 +215,7 @@ interface Upload{${this.name}
-
-
- Total size:
- )=>{
+ const tag = e.detail.toLowerCase(); //Will get sanitized server-side but it won't hurt to fix it here
+ const currentTags = new Set([...this.scene.tags]);
+ console.log("Tags : ", tag, currentTags.has(tag), currentTags);
+ if(currentTags.has(tag)){
+ return Notification.show(`The scene already has a tag named ${tag}`, "warning", 4000);
+ }
+ currentTags.add(tag);
+ this.scene = {...this.scene, tags: Array.from(currentTags)};
+ this.setTags(this.scene.tags).catch(e=>{
+ console.error(e);
+ Notification.show(`Failed to add scene tag ${tag}: ${e.message}`, "error", 6000);
+ const tags = new Set(this.scene.tags);
+ tags.delete(tag);
+ this.scene = {...this.scene, tags: Array.from(tags) };
+ });
+ }
+ const rmTag = (e:CustomEvent)=>{
+ const tag = e.detail;
+ const currentTags = new Set([...this.scene.tags]);
+ currentTags.delete(tag);
+ this.scene = {...this.scene, tags: Array.from(currentTags)};
+ this.setTags(this.scene.tags).catch(e=>{
+ console.error(e);
+ Notification.show(`Failed to remove scene tag ${tag}: ${e.message}`, "error", 6000);
+ const tags = new Set(this.scene.tags);
+ tags.add(tag);
+ this.scene = {...this.scene, tags: Array.from(tags) };
+ });
+ }
+ return html` `
+ }
+
renderPermissions(){
- console.log("Can : ", this.can("admin"), this.scene);
return html`
diff --git a/source/ui/screens/Tags.ts b/source/ui/screens/Tags.ts
new file mode 100644
index 00000000..97c524fc
--- /dev/null
+++ b/source/ui/screens/Tags.ts
@@ -0,0 +1,131 @@
+import { LitElement, customElement, html, property } from "lit-element";
+
+import Notification from "../composants/Notification";
+
+import "../composants/TagList";
+import "../composants/SceneCard";
+import Notifications from "../composants/Notification";
+import { Scene } from "../state/withScenes";
+import { withUser } from "../state/auth";
+
+export interface Tag{
+ name :string;
+ size :number;
+}
+
+
+@customElement("tags-screen")
+export default class TagsScreen extends withUser(LitElement){
+
+ @property({attribute: false, type: Array})
+ tags :Tag[] = [];
+
+ @property({attribute:false, type: Number})
+ selected :number = -1;
+
+ @property({attribute:false, type: Array})
+ scenes :Scene[] = [];
+
+ #c:AbortController = new AbortController();
+
+ constructor()
+ {
+ super();
+ }
+ createRenderRoot() {
+ return this;
+ }
+
+ public connectedCallback(): void {
+ super.connectedCallback();
+ this.fetchTags();
+ }
+
+
+ public disconnectedCallback(): void {
+ this.#c.abort();
+ }
+
+
+ public update(changedProperties: Map
Total size:
- ${articles.size} article${(1 < articles.size?"s":"")}
+ +
+
+
+
-
+ ${this.renderTags()}
+
+
+
+ Total size:
+
+
Total size:
+ ${articles.size} article${(1 < articles.size?"s":"")}
+
${this.can("write")?html`
${this.t("ui.editScene")}
@@ -208,9 +219,11 @@ class SceneVersion{
${this.t("ui.downloadScene")}
+
+
${this.renderPermissions()}
+
@@ -223,8 +236,55 @@ class SceneVersion{
`;
}
+
+ async setTags(tags :string[]){
+ return await fetch(`/api/v1/scenes/${encodeURIComponent(this.name)}`, {
+ method: "PATCH",
+ headers: {"Content-Type": "application/json"},
+ body: JSON.stringify({tags})
+ }).then(async r =>{
+ await HttpError.okOrThrow(r);
+ this.scene = await r.json();
+ });
+ }
+
+
+ renderTags(){
+
+ const addTag = (e:CustomEvent