feat: Implement WebcamFileSource for life webcam capture #12
					 7 changed files with 55 additions and 20 deletions
				
			
		
							
								
								
									
										20
									
								
								frontend/package-lock.json
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										20
									
								
								frontend/package-lock.json
									
										
									
										generated
									
									
									
								
							| 
						 | 
				
			
			@ -13,6 +13,7 @@
 | 
			
		|||
        "js-nacl": "^1.4.0",
 | 
			
		||||
        "moment": "^2.29.4",
 | 
			
		||||
        "vue": "^3.2.47",
 | 
			
		||||
        "vue-multiselect": "^2.1.7",
 | 
			
		||||
        "vue-router": "^4.1.6",
 | 
			
		||||
        "vuex": "^4.1.0"
 | 
			
		||||
      },
 | 
			
		||||
| 
						 | 
				
			
			@ -2062,14 +2063,14 @@
 | 
			
		|||
      "integrity": "sha512-i/I1Omf6lADjVBlwJpQifZOePV15snHny9w04+lc71+3t8PyWuLC/7clyoOSHOBNGXFe2PAGxmTiZ+Z4HWsPyw=="
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/vite": {
 | 
			
		||||
      "version": "4.3.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.1.tgz",
 | 
			
		||||
      "integrity": "sha512-EPmfPLAI79Z/RofuMvkIS0Yr091T2ReUoXQqc5ppBX/sjFRhHKiPPF/R46cTdoci/XgeQpB23diiJxq5w30vdg==",
 | 
			
		||||
      "version": "4.3.9",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.9.tgz",
 | 
			
		||||
      "integrity": "sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "esbuild": "^0.17.5",
 | 
			
		||||
        "postcss": "^8.4.21",
 | 
			
		||||
        "rollup": "^3.20.2"
 | 
			
		||||
        "postcss": "^8.4.23",
 | 
			
		||||
        "rollup": "^3.21.0"
 | 
			
		||||
      },
 | 
			
		||||
      "bin": {
 | 
			
		||||
        "vite": "bin/vite.js"
 | 
			
		||||
| 
						 | 
				
			
			@ -2234,6 +2235,15 @@
 | 
			
		|||
        "@vue/shared": "3.2.47"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/vue-multiselect": {
 | 
			
		||||
      "version": "2.1.7",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/vue-multiselect/-/vue-multiselect-2.1.7.tgz",
 | 
			
		||||
      "integrity": "sha512-KIegcN+Ntwg3cbkY/jhw2s/+XJUM0Lpi/LcKFYCS8PrZHcWBl2iKCVze7ZCnRj3w8H7/lUJ9v7rj9KQiNxApBw==",
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">= 4.0.0",
 | 
			
		||||
        "npm": ">= 3.0.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/vue-router": {
 | 
			
		||||
      "version": "4.1.6",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.1.6.tgz",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,6 +13,7 @@
 | 
			
		|||
    "js-nacl": "^1.4.0",
 | 
			
		||||
    "moment": "^2.29.4",
 | 
			
		||||
    "vue": "^3.2.47",
 | 
			
		||||
    "vue-multiselect": "^2.1.7",
 | 
			
		||||
    "vue-router": "^4.1.6",
 | 
			
		||||
    "vuex": "^4.1.0"
 | 
			
		||||
  },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -190,12 +190,11 @@ class ServerSetUnion {
 | 
			
		|||
 | 
			
		||||
    async get(auth, target) {
 | 
			
		||||
        try {
 | 
			
		||||
            const ret = await this.serverSets.reduce(async (acc, serverset) => {
 | 
			
		||||
            return await this.serverSets.reduce(async (acc, serverset) => {
 | 
			
		||||
                return acc.then(async (acc) => {
 | 
			
		||||
                    return acc.concat(await serverset.get(auth, target))
 | 
			
		||||
                })
 | 
			
		||||
            }, Promise.resolve([]))
 | 
			
		||||
            return ret
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            throw new Error('all servers failed')
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -203,12 +202,11 @@ class ServerSetUnion {
 | 
			
		|||
 | 
			
		||||
    async post(auth, target, data) {
 | 
			
		||||
        try {
 | 
			
		||||
            const ret = await this.serverSets.reduce(async (acc, serverset) => {
 | 
			
		||||
            return await this.serverSets.reduce(async (acc, serverset) => {
 | 
			
		||||
                return acc.then(async (acc) => {
 | 
			
		||||
                    return acc.concat(await serverset.post(auth, target, data))
 | 
			
		||||
                })
 | 
			
		||||
            }, Promise.resolve([]))
 | 
			
		||||
            return ret
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            throw new Error('all servers failed')
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -216,12 +214,11 @@ class ServerSetUnion {
 | 
			
		|||
 | 
			
		||||
    async patch(auth, target, data) {
 | 
			
		||||
        try {
 | 
			
		||||
            const ret = await this.serverSets.reduce(async (acc, serverset) => {
 | 
			
		||||
            return await this.serverSets.reduce(async (acc, serverset) => {
 | 
			
		||||
                return acc.then(async (acc) => {
 | 
			
		||||
                    return acc.concat(await serverset.patch(auth, target, data))
 | 
			
		||||
                })
 | 
			
		||||
            }, Promise.resolve([]))
 | 
			
		||||
            return ret
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            throw new Error('all servers failed')
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -229,12 +226,11 @@ class ServerSetUnion {
 | 
			
		|||
 | 
			
		||||
    async put(auth, target, data) {
 | 
			
		||||
        try {
 | 
			
		||||
            const ret = await this.serverSets.reduce(async (acc, serverset) => {
 | 
			
		||||
            return await this.serverSets.reduce(async (acc, serverset) => {
 | 
			
		||||
                return acc.then(async (acc) => {
 | 
			
		||||
                    return acc.concat(await serverset.put(auth, target, data))
 | 
			
		||||
                })
 | 
			
		||||
            }, Promise.resolve([]))
 | 
			
		||||
            return ret
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            throw new Error('all servers failed')
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -242,12 +238,11 @@ class ServerSetUnion {
 | 
			
		|||
 | 
			
		||||
    async delete(auth, target) {
 | 
			
		||||
        try {
 | 
			
		||||
            const ret = await this.serverSets.reduce(async (acc, serverset) => {
 | 
			
		||||
            return await this.serverSets.reduce(async (acc, serverset) => {
 | 
			
		||||
                return acc.then(async (acc) => {
 | 
			
		||||
                    return acc.concat(await serverset.delete(auth, target))
 | 
			
		||||
                })
 | 
			
		||||
            }, Promise.resolve([]))
 | 
			
		||||
            return ret
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            throw new Error('all servers failed')
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,6 +23,7 @@ export default createStore({
 | 
			
		|||
        unreachable_neighbors: new NeighborsCache(),
 | 
			
		||||
        tags: [],
 | 
			
		||||
        properties: [],
 | 
			
		||||
        files: [],
 | 
			
		||||
    },
 | 
			
		||||
    mutations: {
 | 
			
		||||
        setUser(state, user) {
 | 
			
		||||
| 
						 | 
				
			
			@ -67,6 +68,9 @@ export default createStore({
 | 
			
		|||
        setProperties(state, properties) {
 | 
			
		||||
            state.properties = properties;
 | 
			
		||||
        },
 | 
			
		||||
        setFiles(state, files) {
 | 
			
		||||
            state.files = files;
 | 
			
		||||
        },
 | 
			
		||||
        logout(state) {
 | 
			
		||||
            state.user = null;
 | 
			
		||||
            state.token = null;
 | 
			
		||||
| 
						 | 
				
			
			@ -236,6 +240,22 @@ export default createStore({
 | 
			
		|||
            // TODO implement
 | 
			
		||||
            console.log('declining friend ' + args)
 | 
			
		||||
        },
 | 
			
		||||
        async fetchFiles({state, commit, dispatch, getters}) {
 | 
			
		||||
            if (state.last_load.files > Date.now() - 1000 * 60 * 60 * 24) {
 | 
			
		||||
                return state.files
 | 
			
		||||
            }
 | 
			
		||||
            const servers = await dispatch('getHomeServers')
 | 
			
		||||
            const data = await servers.get(getters.signAuth, '/api/files/')
 | 
			
		||||
            commit('setFiles', data)
 | 
			
		||||
            state.last_load.files = Date.now()
 | 
			
		||||
            return data
 | 
			
		||||
        },
 | 
			
		||||
        async pushFile({state, dispatch, getters}, {file}) {
 | 
			
		||||
            const servers = await dispatch('getHomeServers')
 | 
			
		||||
            const data = await servers.post(getters.signAuth, '/api/files/', file)
 | 
			
		||||
            state.files.push(data)
 | 
			
		||||
            return data
 | 
			
		||||
        },
 | 
			
		||||
        async fetchTags({state, commit, dispatch, getters}) {
 | 
			
		||||
            if (state.last_load.tags > Date.now() - 1000 * 60 * 60 * 24) {
 | 
			
		||||
                return state.tags
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,6 +29,10 @@
 | 
			
		|||
                                <input type="number" class="form-control" id="quantity" name="quantity"
 | 
			
		||||
                                       placeholder="Enter quantity" v-model="item.owned_quantity">
 | 
			
		||||
                            </div>
 | 
			
		||||
                            <div class="mb-3">
 | 
			
		||||
                                <label for="image" class="form-label">Image</label>
 | 
			
		||||
                                <combined-file-field :value="item.files"></combined-file-field>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="card">
 | 
			
		||||
                            <button type="submit" class="btn btn-primary" @click="updateInventoryItem(item)">Update
 | 
			
		||||
| 
						 | 
				
			
			@ -47,6 +51,7 @@ import {mapActions, mapGetters} from "vuex";
 | 
			
		|||
import BaseLayout from "@/components/BaseLayout.vue";
 | 
			
		||||
import TagField from "@/components/TagField.vue";
 | 
			
		||||
import PropertyField from "@/components/PropertyField.vue";
 | 
			
		||||
import CombinedFileField from "@/components/CombinedFileField.vue";
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    name: "InventoryEdit",
 | 
			
		||||
| 
						 | 
				
			
			@ -54,6 +59,7 @@ export default {
 | 
			
		|||
        BaseLayout,
 | 
			
		||||
        TagField,
 | 
			
		||||
        PropertyField,
 | 
			
		||||
        CombinedFileField,
 | 
			
		||||
        ...BIcons
 | 
			
		||||
    },
 | 
			
		||||
    props: {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,12 +29,10 @@
 | 
			
		|||
                                <input type="number" class="form-control" id="quantity" name="quantity"
 | 
			
		||||
                                       placeholder="Enter quantity" v-model="item.owned_quantity">
 | 
			
		||||
                            </div>
 | 
			
		||||
                            <!-- TODO -->
 | 
			
		||||
                            <!--div class="mb-3">
 | 
			
		||||
                            <div class="mb-3">
 | 
			
		||||
                                <label for="image" class="form-label">Image</label>
 | 
			
		||||
                                <input type="text" class="form-control" id="image" name="image"
 | 
			
		||||
                                       placeholder="Enter image" v-model="item.image">
 | 
			
		||||
                            </div-->
 | 
			
		||||
                                <combined-file-field :value="item.files"></combined-file-field>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="card">
 | 
			
		||||
                            <button type="submit" class="btn btn-primary"
 | 
			
		||||
| 
						 | 
				
			
			@ -54,6 +52,7 @@ import {mapActions} from "vuex";
 | 
			
		|||
import BaseLayout from "@/components/BaseLayout.vue";
 | 
			
		||||
import TagField from "@/components/TagField.vue";
 | 
			
		||||
import PropertyField from "@/components/PropertyField.vue";
 | 
			
		||||
import CombinedFileField from "@/components/CombinedFileField.vue";
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    name: "InventoryNew",
 | 
			
		||||
| 
						 | 
				
			
			@ -61,6 +60,7 @@ export default {
 | 
			
		|||
        BaseLayout,
 | 
			
		||||
        TagField,
 | 
			
		||||
        PropertyField,
 | 
			
		||||
        CombinedFileField,
 | 
			
		||||
        ...BIcons
 | 
			
		||||
    },
 | 
			
		||||
    data() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -46,6 +46,9 @@ export default defineConfig({
 | 
			
		|||
            '^/static/': {
 | 
			
		||||
                target: "http://127.0.0.1:8000/",
 | 
			
		||||
            },
 | 
			
		||||
            '^/media/': {
 | 
			
		||||
                target: "http://127.0.0.1:8000/",
 | 
			
		||||
            },
 | 
			
		||||
            '^/wiki/': {
 | 
			
		||||
                target: "http://127.0.0.1:8080/",
 | 
			
		||||
                rewrite: (path) => path.replace(/^\/wiki/, ''),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue