This commit is contained in:
Graham Steffaniak 2023-08-05 09:58:06 -05:00
parent 3dd3e01f4c
commit 6f83300f92
5 changed files with 135 additions and 36 deletions

View File

@ -1,11 +1,10 @@
{ {
"server":{
"port": 8080, "port": 8080,
"baseURL": "", "baseURL": "",
"address": "", "address": "",
"log": "stdout", "log": "stdout",
"database": "./database.db", "database": "./database.db",
"root": "/srv" "root": "/srv"
}
} }

View File

@ -2,8 +2,8 @@ package search
import ( import (
"regexp" "regexp"
"strings"
"strconv" "strconv"
"strings"
) )
var typeRegexp = regexp.MustCompile(`type:(\S+)`) var typeRegexp = regexp.MustCompile(`type:(\S+)`)
@ -26,22 +26,21 @@ var compressedFile = []string{
} }
type searchOptions struct { type searchOptions struct {
Conditions map[string]bool Conditions map[string]bool
Size int Size int
Terms []string Terms []string
} }
func ParseSearch(value string) *searchOptions { func ParseSearch(value string) *searchOptions {
opts := &searchOptions{ opts := &searchOptions{
Conditions: map[string]bool{ Conditions: map[string]bool{
"exact": strings.Contains(value, "case:exact"), "exact": strings.Contains(value, "case:exact"),
}, },
Terms: []string{}, Terms: []string{},
} }
// removes the options from the value // removes the options from the value
value = strings.Replace(value, "case:exact", "", -1) value = strings.Replace(value, "case:exact", "", -1)
value = strings.Replace(value, "case:exact", "", -1)
value = strings.TrimSpace(value) value = strings.TrimSpace(value)
types := typeRegexp.FindAllStringSubmatch(value, -1) types := typeRegexp.FindAllStringSubmatch(value, -1)
@ -51,15 +50,21 @@ func ParseSearch(value string) *searchOptions {
} }
filter := filterType[1] filter := filterType[1]
switch filter { switch filter {
case "image" : opts.Conditions["image"] = true case "image":
case "audio", "music" : opts.Conditions["audio"] = true opts.Conditions["image"] = true
case "video" : opts.Conditions["video"] = true case "audio", "music":
case "doc" : opts.Conditions["doc"] = true opts.Conditions["audio"] = true
case "archive" : opts.Conditions["archive"] = true case "video":
case "folder" : opts.Conditions["dir"] = true opts.Conditions["video"] = true
case "file" : opts.Conditions["dir"] = false case "doc":
opts.Conditions["doc"] = true
case "archive":
opts.Conditions["archive"] = true
case "folder":
opts.Conditions["dir"] = true
case "file":
opts.Conditions["dir"] = false
} }
if len(filter) < 8 { if len(filter) < 8 {
continue continue
} }
@ -74,8 +79,8 @@ func ParseSearch(value string) *searchOptions {
} }
if len(types) > 0 { if len(types) > 0 {
// Remove the fields from the search value. // Remove the fields from the search value, including added space
value = typeRegexp.ReplaceAllString(value, "") value = typeRegexp.ReplaceAllString(value+" ", "")
} }
if value == "" { if value == "" {
@ -91,9 +96,8 @@ func ParseSearch(value string) *searchOptions {
opts.Terms = []string{unique} opts.Terms = []string{unique}
return opts return opts
} }
re := regexp.MustCompile(` +`) value = strings.TrimSpace(value)
value = re.ReplaceAllString(value, " ") opts.Terms = strings.Split(value, "|")
opts.Terms = strings.Split(value, " ")
return opts return opts
} }
@ -112,4 +116,4 @@ func updateSize(given string) int {
} else { } else {
return size return size
} }
} }

View File

@ -31,7 +31,11 @@
<p v-show="isEmpty && isRunning" id="renew"> <p v-show="isEmpty && isRunning" id="renew">
<i class="material-icons spin">autorenew</i> <i class="material-icons spin">autorenew</i>
</p> </p>
<p v-show="isEmpty && !isRunning">{{ text }}</p> <div v-show="isEmpty && !isRunning">
<div class="searchPrompt" v-show="isEmpty && !isRunning">
<p>No results found in indexed search.</p>
</div>
</div>
<template v-if="isEmpty"> <template v-if="isEmpty">
<template v-if="value.length === 0"> <template v-if="value.length === 0">
<div class="boxes"> <div class="boxes">
@ -50,7 +54,7 @@
</div> </div>
<div v-if="!isMobile && active" id="result-desktop" ref="result"> <div v-if="!isMobile && active" id="result-desktop" ref="result">
<div id="result-list"> <div id="result-list">
<div class="button" style="width: 100%"> <div class="button fluid">
Search Context: {{ getContext(this.$route.path) }} Search Context: {{ getContext(this.$route.path) }}
</div> </div>
<ul v-show="results.length > 0"> <ul v-show="results.length > 0">
@ -72,9 +76,18 @@
<p v-show="isEmpty && isRunning" id="renew"> <p v-show="isEmpty && isRunning" id="renew">
<i class="material-icons spin">autorenew</i> <i class="material-icons spin">autorenew</i>
</p> </p>
<p v-show="isEmpty && !isRunning">{{ text }}</p> <div class="searchPrompt" v-show="isEmpty && !isRunning">
<p>No results found in indexed search.</p>
<div class="helpButton" @click="toggleHelp()">Toggle Search Help</div>
</div>
<div class="helpText" v-if="showHelp">
Search additional terms separated by <code>|</code>, for example <code>"test|not"</code> searches for both terms independently
<p>Note: searching files by size may have significantly longer search times since it cannot rely on the index alone.
The search looks for only files that match all other conditions first, then checks the filesize and returns matching results.</p>
</div>
<template> <template>
<div v-show="results.length == 0" class="boxes"> <div class="boxes">
<ButtonGroup :buttons="folderSelect" @button-clicked="init" @remove-button-clicked="removeInit" /> <ButtonGroup :buttons="folderSelect" @button-clicked="init" @remove-button-clicked="removeInit" />
<ButtonGroup :buttons="typeSelect" @button-clicked="init" @remove-button-clicked="removeInit" /> <ButtonGroup :buttons="typeSelect" @button-clicked="init" @remove-button-clicked="removeInit" />
<ButtonGroup :buttons="sizeSelect" @button-clicked="init" @remove-button-clicked="removeInit" /> <ButtonGroup :buttons="sizeSelect" @button-clicked="init" @remove-button-clicked="removeInit" />
@ -86,6 +99,26 @@
</div> </div>
</template> </template>
<style>
.helpText{
padding:1em
}
.helpButton {
text-align: center;
background: var(--background);
background-color: lightgray;
padding: .25em;
border-radius: .25em;
}
.searchPrompt {
display: flex;
flex-direction: column;
align-content: center;
justify-content: center;
align-items: center;
}
</style>
<script> <script>
import ButtonGroup from "./ButtonGroup.vue"; import ButtonGroup from "./ButtonGroup.vue";
import { mapState, mapGetters, mapMutations } from "vuex"; import { mapState, mapGetters, mapMutations } from "vuex";
@ -110,6 +143,7 @@ export default {
name: "search", name: "search",
data: function () { data: function () {
return { return {
showHelp: false,
folderSelect: [ folderSelect: [
{ label: "Only Folders", value: "type:folder" }, { label: "Only Folders", value: "type:folder" },
{ label: "Only Files", value: "type:file" }, { label: "Only Files", value: "type:file" },
@ -182,7 +216,10 @@ export default {
}, },
isRunning() { isRunning() {
return this.ongoing; return this.ongoing;
} },
searchHelp() {
return this.showHelp
},
}, },
mounted() { mounted() {
window.addEventListener("resize", this.handleResize); window.addEventListener("resize", this.handleResize);
@ -271,6 +308,9 @@ export default {
} }
this.ongoing = false; this.ongoing = false;
}, },
toggleHelp(){
this.showHelp = !this.showHelp
}
}, },
}; };
</script> </script>

View File

@ -16,6 +16,10 @@
<i class="material-icons">note_add</i> <i class="material-icons">note_add</i>
<span>{{ $t("sidebar.newFile") }}</span> <span>{{ $t("sidebar.newFile") }}</span>
</button> </button>
<button id="upload-button" @click="upload($event)" class="action" :aria-label="$t('sidebar.upload')" >
<i class="material-icons">file_upload</i>
<span>Upload file</span>
</button>
</div> </div>
<div> <div>
<button class="action" @click="toSettings" :aria-label="$t('sidebar.settings')" :title="$t('sidebar.settings')"> <button class="action" @click="toSettings" :aria-label="$t('sidebar.settings')" :title="$t('sidebar.settings')">
@ -62,6 +66,7 @@
</template> </template>
<script> <script>
import { mapState, mapGetters } from "vuex"; import { mapState, mapGetters } from "vuex";
import * as upload from "@/utils/upload";
import * as auth from "@/utils/auth"; import * as auth from "@/utils/auth";
import { import {
version, version,
@ -132,6 +137,50 @@ export default {
help() { help() {
this.$store.commit("showHover", "help"); this.$store.commit("showHover", "help");
}, },
upload: function () {
if (
typeof window.DataTransferItem !== "undefined" &&
typeof DataTransferItem.prototype.webkitGetAsEntry !== "undefined"
) {
this.$store.commit("showHover", "upload");
} else {
document.getElementById("upload-input").click();
}
},
uploadInput(event) {
this.$store.commit("closeHovers");
let files = event.currentTarget.files;
let folder_upload =
files[0].webkitRelativePath !== undefined && files[0].webkitRelativePath !== "";
if (folder_upload) {
for (let i = 0; i < files.length; i++) {
let file = files[i];
files[i].fullPath = file.webkitRelativePath;
}
}
let path = this.$route.path.endsWith("/")
? this.$route.path
: this.$route.path + "/";
let conflict = upload.checkConflict(files, this.req.items);
if (conflict) {
this.$store.commit("showHover", {
prompt: "replace",
confirm: (event) => {
event.preventDefault();
this.$store.commit("closeHovers");
upload.handleFiles(files, path, true);
},
});
return;
}
upload.handleFiles(files, path);
},
logout: auth.logout, logout: auth.logout,
}, },
}; };

View File

@ -22,6 +22,13 @@
:label="$t('buttons.selectMultiple')" :label="$t('buttons.selectMultiple')"
@action="toggleMultipleSelection" @action="toggleMultipleSelection"
/> />
<action
v-if="headerButtons.download"
icon="file_download"
:label="$t('buttons.download')"
@action="download"
:counter="selectedCount"
/>
<action <action
v-if="headerButtons.share" v-if="headerButtons.share"
icon="share" icon="share"
@ -303,8 +310,8 @@ export default {
headerButtons() { headerButtons() {
return { return {
select: this.selectedCount > 0, select: this.selectedCount > 0,
upload: this.user.perm.create, upload: this.user.perm.create && this.selectedCount > 0,
download: this.user.perm.download, download: this.user.perm.download && this.selectedCount > 0,
shell: this.user.perm.execute && enableExec, shell: this.user.perm.execute && enableExec,
delete: this.selectedCount > 0 && this.user.perm.delete, delete: this.selectedCount > 0 && this.user.perm.delete,
rename: this.selectedCount === 1 && this.user.perm.rename, rename: this.selectedCount === 1 && this.user.perm.rename,