Skip to content

Commit c316c10

Browse files
authored
Merge pull request #277 from DigitalSlideArchive/save-selected-directories
Store selected directories in localStorage
2 parents 9f36d51 + d81f4f5 commit c316c10

7 files changed

Lines changed: 159 additions & 106 deletions

File tree

client/src/HomePage.vue

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<script setup lang="ts">
2-
import { ref } from "vue";
2+
import { ref, onMounted } from "vue";
33
44
import { redactImages } from "./api/rest";
55
import { selectedDirectories } from "./store/directoryStore";
6-
import { useRedactionPlan } from "./store/imageStore";
6+
import { useRedactionPlan, updateTableData } from "./store/imageStore";
77
import { redactionStateFlags } from "./store/redactionStore";
88
99
import MenuSteps from "./components/MenuSteps.vue";
@@ -96,6 +96,19 @@ const forceRedact = () => {
9696
missingRulesModal.value.close();
9797
redact_images();
9898
};
99+
100+
onMounted(() => {
101+
if (selectedDirectories.value.inputDirectory) {
102+
updateTableData({
103+
directory: selectedDirectories.value.inputDirectory,
104+
rules: selectedDirectories.value.rulesetDirectory,
105+
limit: 50,
106+
offset: 0,
107+
update: false,
108+
});
109+
redactionStateFlags.value.showImageTable = true;
110+
}
111+
});
99112
</script>
100113

101114
<template>
@@ -139,8 +152,8 @@ const forceRedact = () => {
139152
:modal-id="'inputDirectory'"
140153
:title="'Input Directory'"
141154
@update-image-list="
142-
(redactionStateFlags.showImageTable = true),
143-
(redactionStateFlags.redactionComplete = false)
155+
((redactionStateFlags.showImageTable = true),
156+
(redactionStateFlags.redactionComplete = false))
144157
"
145158
/>
146159
<FileBrowser

client/src/components/FileBrowser.vue

Lines changed: 37 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
<script setup lang="ts">
2-
import { ref, Ref, onMounted, onBeforeUnmount, nextTick } from "vue";
3-
import { getDirectoryInfo } from "../api/rest";
4-
import { selectedDirectories } from "../store/directoryStore";
5-
import { useRedactionPlan } from "../store/imageStore";
6-
import { redactionStateFlags } from "../store/redactionStore";
7-
import { DirectoryData, Path } from "../store/types";
2+
import { ref, onMounted, onBeforeUnmount } from "vue";
3+
import { selectedDirectories, updateDirectories, directoryData, loadingData, calculateVisibleItems, visibleImages, remainingImages } from "../store/directoryStore";
4+
import { updateTableData } from "../store/imageStore";
85
96
const props = defineProps({
107
modalId: {
@@ -21,91 +18,17 @@ const modal = ref();
2118
defineExpose({ modal });
2219
defineEmits(["update-image-list"]);
2320
24-
const directoryData: Ref<DirectoryData> = ref({
25-
directory: "",
26-
ancestors: [],
27-
children: [],
28-
childrenImages: [],
29-
childrenYaml: [],
30-
});
31-
32-
const loadingData = ref(false);
33-
34-
const updateDirectories = async (currentDirectory?: string) => {
35-
directoryData.value.children = [];
36-
directoryData.value.childrenImages = [];
37-
directoryData.value.childrenYaml = [];
38-
const timeout = setTimeout(() => {
39-
loadingData.value = true;
40-
}, 100);
41-
42-
const data = await getDirectoryInfo(currentDirectory);
43-
clearTimeout(timeout);
44-
loadingData.value = false;
45-
directoryData.value = await {
46-
...data,
47-
children: data.child_directories,
48-
childrenImages: data.child_images,
49-
childrenYaml: data.child_yaml_files,
50-
};
51-
loadingData.value = false;
52-
calculateVisibleItems();
53-
};
54-
updateDirectories();
55-
5621
const closeModal = () => {
5722
modal.value.close();
5823
};
5924
60-
const updateTableData = () => {
61-
redactionStateFlags.value.redactionSnackbar = false;
62-
useRedactionPlan.updateImageData({
63-
directory: selectedDirectories.value.inputDirectory,
64-
rules: selectedDirectories.value.rulesetDirectory,
65-
limit: 50,
66-
offset: 0,
67-
update: false,
68-
});
69-
};
70-
const updateSelectedDirectories = async (path: string) => {
25+
const updateSelectedDirectories = (path: string) => {
7126
selectedDirectories.value[props.modalId] = path;
27+
localStorage.setItem('inputDirectory', selectedDirectories.value.inputDirectory);
28+
localStorage.setItem('outputDirectory', selectedDirectories.value.outputDirectory);
29+
localStorage.setItem('rulesetDirectory', selectedDirectories.value.rulesetDirectory);
7230
};
7331
74-
const visibleImages: Ref<Path[]> = ref([]);
75-
const remainingImages = ref(0);
76-
77-
const calculateVisibleItems = () => {
78-
const menuTop = document.querySelector(".menu-top");
79-
const listContainer = document.querySelector(".list-container");
80-
// Determine and set the height of the list container
81-
listContainer?.setAttribute(
82-
"style",
83-
`height: calc(100% - (${menuTop?.clientHeight}px + 3.5rem))`,
84-
);
85-
86-
nextTick(() => {
87-
const listItems = listContainer?.querySelectorAll("li");
88-
const containerHeight = listContainer?.clientHeight;
89-
const listHeight = ref(0);
90-
const visibleItems = ref(0);
91-
// Determine the height of each list item
92-
const listItemHeight = listItems && listItems[0] ? listItems[0].clientHeight : 0;
93-
94-
directoryData.value.childrenImages.forEach(() => {
95-
listHeight.value += listItemHeight;
96-
if (containerHeight && listHeight.value < containerHeight) {
97-
visibleItems.value += 1;
98-
}
99-
});
100-
101-
visibleImages.value = directoryData.value.childrenImages.slice(
102-
0,
103-
visibleItems.value,
104-
);
105-
remainingImages.value =
106-
directoryData.value.childrenImages.length - visibleItems.value;
107-
});
108-
};
10932
onMounted(() => {
11033
calculateVisibleItems();
11134
window.addEventListener("resize", calculateVisibleItems);
@@ -129,9 +52,17 @@ onBeforeUnmount(() => {
12952
class="btn btn-primary float-right text-white uppercase"
13053
type="button"
13154
@click="
132-
$emit('update-image-list'),
133-
closeModal(),
134-
title !== 'Output Directory' ? updateTableData() : ''
55+
($emit('update-image-list'),
56+
closeModal(),
57+
title !== 'Output Directory'
58+
? updateTableData({
59+
directory: selectedDirectories.inputDirectory,
60+
rules: selectedDirectories.rulesetDirectory,
61+
limit: 50,
62+
offset: 0,
63+
update: false,
64+
})
65+
: '')
13566
"
13667
>
13768
Select
@@ -153,8 +84,8 @@ onBeforeUnmount(() => {
15384
v-else
15485
class="text-blue-700"
15586
@click="
156-
updateDirectories(ancestor.path),
157-
updateSelectedDirectories(ancestor.path)
87+
(updateDirectories(ancestor.path),
88+
updateSelectedDirectories(ancestor.path))
15889
"
15990
>
16091
{{ ancestor.name ? ancestor.name : "/" }}
@@ -232,7 +163,23 @@ onBeforeUnmount(() => {
232163
</div>
233164
</div>
234165
<form method="dialog" class="modal-backdrop w-screen h-screen absolute">
235-
<button>close</button>
166+
<button
167+
@click="
168+
($emit('update-image-list'),
169+
closeModal(),
170+
title !== 'Output Directory'
171+
? updateTableData({
172+
directory: selectedDirectories.inputDirectory,
173+
rules: selectedDirectories.rulesetDirectory,
174+
limit: 50,
175+
offset: 0,
176+
update: false,
177+
})
178+
: '')
179+
"
180+
>
181+
close
182+
</button>
236183
</form>
237184
</dialog>
238185
</template>

client/src/components/MenuSteps.vue

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
<script setup lang="ts">
2-
import { selectedDirectories } from "../store/directoryStore";
2+
import {
3+
selectedDirectories,
4+
updateDirectories,
5+
} from "../store/directoryStore";
36
import { useRedactionPlan } from "../store/imageStore";
47
58
const props = defineProps({
@@ -31,10 +34,13 @@ const props = defineProps({
3134
3235
const openModal = () => {
3336
props.stepTitle.includes("Input")
34-
? props.inputModal.modal.showModal()
37+
? (props.inputModal.modal.showModal(),
38+
updateDirectories(selectedDirectories.value.inputDirectory))
3539
: props.stepTitle.includes("Output")
36-
? props.outputModal.modal.showModal()
37-
: props.rulesetModal.modal.showModal();
40+
? (props.outputModal.modal.showModal(),
41+
updateDirectories(selectedDirectories.value.outputDirectory))
42+
: (props.rulesetModal.modal.showModal(),
43+
updateDirectories(selectedDirectories.value.rulesetDirectory));
3844
};
3945
4046
const clearRuleset = () => {

client/src/store/directoryStore.ts

Lines changed: 84 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,87 @@
1-
import { ref, Ref } from "vue";
2-
import { SelectedDirectories } from "./types";
1+
import { ref, Ref, nextTick } from "vue";
2+
import { SelectedDirectories, DirectoryData, Path } from "./types";
3+
import { getDirectoryInfo } from "../api/rest";
4+
5+
const storedDirectories = {
6+
inputDirectory: localStorage.getItem("inputDirectory"),
7+
outputDirectory: localStorage.getItem("outputDirectory"),
8+
rulesetDirectory: localStorage.getItem("rulesetDirectory"),
9+
};
310

411
export const selectedDirectories: Ref<SelectedDirectories> = ref({
5-
inputDirectory: "",
6-
outputDirectory: "",
7-
rulesetDirectory: "",
12+
inputDirectory: storedDirectories.inputDirectory
13+
? storedDirectories.inputDirectory
14+
: "",
15+
outputDirectory: storedDirectories.outputDirectory
16+
? storedDirectories.outputDirectory
17+
: "",
18+
rulesetDirectory: storedDirectories.rulesetDirectory
19+
? storedDirectories.rulesetDirectory
20+
: "",
21+
});
22+
23+
export const directoryData: Ref<DirectoryData> = ref({
24+
directory: "",
25+
ancestors: [],
26+
children: [],
27+
childrenImages: [],
28+
childrenYaml: [],
829
});
30+
31+
export const loadingData = ref(false);
32+
33+
export const updateDirectories = async (currentDirectory?: string) => {
34+
directoryData.value.children = [];
35+
directoryData.value.childrenImages = [];
36+
directoryData.value.childrenYaml = [];
37+
const timeout = setTimeout(() => {
38+
loadingData.value = true;
39+
}, 100);
40+
const data = await getDirectoryInfo(currentDirectory);
41+
clearTimeout(timeout);
42+
loadingData.value = false;
43+
directoryData.value = await {
44+
...data,
45+
children: data.child_directories,
46+
childrenImages: data.child_images,
47+
childrenYaml: data.child_yaml_files,
48+
};
49+
loadingData.value = false;
50+
calculateVisibleItems();
51+
};
52+
53+
export const visibleImages: Ref<Path[]> = ref([]);
54+
export const remainingImages = ref(0);
55+
56+
export const calculateVisibleItems = () => {
57+
const menuTop = document.querySelector(".menu-top");
58+
const listContainer = document.querySelector(".list-container");
59+
// Determine and set the height of the list container
60+
listContainer?.setAttribute(
61+
"style",
62+
`height: calc(100% - (${menuTop?.clientHeight}px + 3.5rem))`,
63+
);
64+
65+
nextTick(() => {
66+
const listItems = listContainer?.querySelectorAll("li");
67+
const containerHeight = listContainer?.clientHeight;
68+
const listHeight = ref(0);
69+
const visibleItems = ref(0);
70+
// Determine the height of each list item
71+
const listItemHeight = listItems && listItems[0] ? listItems[0].clientHeight : 0;
72+
73+
directoryData.value.childrenImages.forEach(() => {
74+
listHeight.value += listItemHeight;
75+
if (containerHeight && listHeight.value < containerHeight) {
76+
visibleItems.value += 1;
77+
}
78+
});
79+
80+
visibleImages.value = directoryData.value.childrenImages.slice(
81+
0,
82+
visibleItems.value,
83+
);
84+
remainingImages.value =
85+
directoryData.value.childrenImages.length - visibleItems.value;
86+
});
87+
};

client/src/store/imageStore.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { reactive } from "vue";
22
import { imagePlanResponse, ImagePlanParams } from "./types";
33
import { getRedactionPlan, getImages } from "../api/rest";
44
import { selectedDirectories } from "./directoryStore";
5+
import { redactionStateFlags } from "./redactionStore";
56

67
export const useRedactionPlan = reactive({
78
imageRedactionPlan: {} as imagePlanResponse,
@@ -42,3 +43,8 @@ export const useRedactionPlan = reactive({
4243
this.imageRedactionPlan = {} as imagePlanResponse;
4344
},
4445
});
46+
47+
export const updateTableData = (params: ImagePlanParams) => {
48+
redactionStateFlags.value.redactionSnackbar = false;
49+
useRedactionPlan.updateImageData(params);
50+
};

imagedephi/gui/api/api.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ def select_directory(
3434
):
3535
directory_path = Path(directory)
3636
# TODO: if input_directory is specified but an empty string, it gets instantiated as the CWD
37-
if not directory_path.is_dir():
38-
raise HTTPException(status_code=404, detail="Input directory not a directory")
37+
if not directory_path.exists():
38+
raise HTTPException(status_code=404, detail="Input directory not found")
3939

4040
def image_url(path: str, key: str) -> str:
4141
params = {"file_name": str(directory_path / path), "image_key": key}
@@ -142,6 +142,8 @@ def get_redaction_plan(
142142
raise HTTPException(status_code=404, detail="Input directory not found")
143143

144144
if rules_path:
145+
if not Path(rules_path).exists():
146+
raise HTTPException(status_code=404, detail="Rules file not found")
145147
return show_redaction_plan(
146148
input_path, override_rules=Path(rules_path), limit=limit, offset=offset, update=update
147149
)._asdict()

tests/test_gui.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def test_gui_select_directory_not_found(
3838
)
3939

4040
assert response.status_code == 404
41-
assert response.json() == {"detail": "Input directory not a directory"}
41+
assert response.json() == {"detail": "Input directory not found"}
4242

4343

4444
def test_gui_redact(

0 commit comments

Comments
 (0)