Skip to content

Commit

Permalink
Migrate Share to TS
Browse files Browse the repository at this point in the history
  • Loading branch information
ingalls committed Dec 2, 2024
1 parent 0d2a272 commit 1947b34
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 139 deletions.
10 changes: 5 additions & 5 deletions api/web/src/components/CloudTAK/Menu/Package.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
v-else-if='error'
:err='error'
/>
<template v-else-if='mode === "share"'>
<template v-else-if='mode === "share" && shareFeat'>
<div class='overflow-auto'>
<Share
:feats='[shareFeat]'
Expand Down Expand Up @@ -99,7 +99,7 @@
<script setup lang='ts'>
import { ref, computed, watch, onMounted } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import type { Server, Package, Import } from '../../../../src/types.ts';
import type { Server, Package, Import, Feature } from '../../../../src/types.ts';
import { std, stdurl } from '../../../../src/std.ts';
import Share from '../util/Share.vue';
import timeDiff from '../../../timediff.ts';
Expand Down Expand Up @@ -132,8 +132,8 @@ watch(route, async () => {
const profile = profileStore.profile;
const shareFeat = computed(() => {
if (!profile || !pkg.value || !server.value) return
const shareFeat = computed<Feature | undefined>(() => {
if (!profile || !pkg.value || !server.value) return;
return {
type: 'Feature',
Expand All @@ -155,7 +155,7 @@ const shareFeat = computed(() => {
type: 'Point',
coordinates: [0, 0, 0]
}
}
} as Feature;
});
Expand Down
14 changes: 6 additions & 8 deletions api/web/src/components/CloudTAK/util/SelectFeats.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
v-for='select in selected.values()'
class='col-12'
>
<Feature
<DisplayFeature
:feature='select'
delete-action='emit'
@delete='selected.delete(select.properties.id)'
Expand Down Expand Up @@ -136,7 +136,8 @@

<script setup lang='ts'>
import { ref } from 'vue';
import Feature from './Feature.vue';
import DisplayFeature from './Feature.vue';
import type { Feature } from '../../../../src/types.ts';
import { useCOTStore } from '../../../../src/stores/cots.ts';
import {
IconPackageExport,
Expand All @@ -158,12 +159,9 @@ import ShareToPackage from './ShareToPackage.vue';
const cotStore = useCOTStore();
const props = defineProps({
selected: {
type: Object,
required: true
}
});
const props = defineProps<{
selected: Map<string, Feature>
}>();
enum ShareType {
NONE = 'none',
Expand Down
251 changes: 125 additions & 126 deletions api/web/src/components/CloudTAK/util/Share.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,20 @@
>
<IconX
:size='20'
:stroke='1'
stroke='1'
/>
</TablerIconButton>
</div>
</div>

<div class='mx-2'>
<TablerInput
label=''
placeholder='Filter...'
v-model='filter'
/>
</div>

<TablerLoading v-if='loading' />
<TablerNone
v-else-if='!visibleContacts.length'
Expand All @@ -33,9 +41,9 @@
width: 100%;
`'
>
<Contact
<COTContact
v-for='a of visibleContacts'
:key='a.id'
:key='a.uid'
:compact='compact'
:contact='a'
:button-chat='false'
Expand All @@ -61,7 +69,7 @@
<IconShare2
v-if='compact'
:size='20'
:stroke='1'
stroke='1'
/>
<span v-else>Share to Selected</span>
</TablerButton>
Expand All @@ -81,7 +89,7 @@
<IconBroadcast
v-if='compact'
:size='20'
:stroke='1'
stroke='1'
/>
<span v-else>Broadcast to All</span>
</TablerButton>
Expand All @@ -104,10 +112,12 @@
</div>
</template>

<script>
import { std, stdurl } from '/src/std.ts';
<script setup lang='ts'>
import { ref, computed, onMounted } from 'vue';
import { std, stdurl } from '../../../../src/std.ts';
import {
TablerNone,
TablerInput,
TablerLoading,
TablerButton,
TablerIconButton
Expand All @@ -117,134 +127,123 @@ import {
IconBroadcast,
IconShare2
} from '@tabler/icons-vue';
import Contact from '../util/Contact.vue';
import { useConnectionStore } from '/src/stores/connection.ts';
import { useCOTStore } from '/src/stores/cots.ts';
import COT from '../../../../src/stores/base/cot.ts'
import type { Contact, ContactList, Feature } from '../../../../src/types.ts'
import COTContact from '../util/Contact.vue';
import { useConnectionStore } from '../../../../src/stores/connection.ts';
import { useCOTStore } from '../../../../src/stores/cots.ts';
const cotStore = useCOTStore();
const connectionStore = useConnectionStore();
export default {
name: 'COTShare',
components: {
Contact,
IconX,
IconBroadcast,
IconShare2,
TablerNone,
TablerLoading,
TablerButton,
TablerIconButton,
},
props: {
feats: { type: Array },
basemaps: { type: Array },
compact: {
type: Boolean,
default: false
},
maxheight: {
type: String,
default: '100%'
}
},
emits: [
'cancel',
'done'
],
data: function() {
return {
err: false,
loading: true,
selected: new Set(),
contacts: [],
const props = defineProps<{
feats?: Feature[] | COT[],
basemaps?: number[],
compact?: boolean
}>();
const emit = defineEmits([
'cancel',
'done'
]);
const loading = ref(true);
const filter = ref('');
const selected = ref<Set<Contact>>(new Set())
const contacts = ref<ContactList>([]);
const visibleContacts = computed<ContactList>(() => {
return contacts.value.filter((contact) => {
return contact.callsign;
}).filter((contact) => {
return contact.callsign.toLowerCase().includes(filter.value.toLowerCase());
})
});
onMounted(async () => {
await fetchList();
});
/** Feats often come from Vector Tiles which don't contain the full feature */
function currentFeats(): Feature[] {
return (props.feats || []).map((f) => {
if (f.properties.type === 'b-f-t-r') {
// FileShare is manually generated and won't exist in CoT Store
return f;
} else {
const cot = cotStore.get(f.id)
if (cot) {
return cot.as_feature();
} else {
return;
}
}
},
computed: {
visibleContacts: function() {
return this.contacts.filter((contact) => {
return contact.callsign;
})
}).filter((f) => {
return !!f;
}) as Feature[];
}
async function share() {
const feats = currentFeats();
// CoTs with Attachments must always be send via a DataPackage
if (
feats.length === 1
&& !props.basemaps
&& (!feats[0].properties.attachments || feats[0].properties.attachments.length === 0)
) {
for (const contact of selected.value) {
const feat = JSON.parse(JSON.stringify(feats[0]));
feat.properties.dest = [{ uid: contact.uid }];
connectionStore.sendCOT(feat);
}
},
mounted: async function() {
await this.fetchList();
},
methods: {
/** Feats often come from Vector Tiles which don't contain the full feature */
currentFeats: function() {
return (this.feats || []).map((f) => {
if (f.properties.type === 'b-f-t-r') {
// FileShare is manually generated and won't exist in CoT Store
return f;
} else {
return cotStore.get(f.id);
}
}).filter((f) => {
return !!f;
});
},
share: async function() {
const feats = this.currentFeats();
// CoTs with Attachments must always be send via a DataPackage
if (
feats.length === 1
&& !this.basemaps
&& (!feats[0].properties.attachments || feats[0].properties.attachments.length === 0)
) {
for (const contact of this.selected) {
const feat = JSON.parse(JSON.stringify(feats[0]));
feat.properties.dest = [{ uid: contact.uid }];
connectionStore.sendCOT(feat);
}
} else {
await std('/api/marti/package', {
method: 'PUT',
body: {
type: 'FeatureCollection',
uids: Array.from(this.selected).map((contact) => { return contact.uid }),
basemaps: this.basemaps || [],
features: feats.map((f) => {
f = JSON.parse(JSON.stringify(f));
return { id: f.id || f.properties.id, type: f.type, properties: f.properties, geometry: f.geometry }
})
}
});
} else {
await std('/api/marti/package', {
method: 'PUT',
body: {
type: 'FeatureCollection',
uids: Array.from(selected.value).map((contact) => { return contact.uid }),
basemaps: props.basemaps || [],
features: feats.map((f) => {
f = JSON.parse(JSON.stringify(f));
return { id: f.id || f.properties.id, type: f.type, properties: f.properties, geometry: f.geometry }
})
}
});
}
this.$emit('done');
},
broadcast: async function() {
const feats = this.currentFeats();
if (
feats.length === 1
&& !this.basemaps
&& (!feats[0].properties.attachments || feats[0].properties.attachments.length === 0)
) {
connectionStore.sendCOT(JSON.parse(JSON.stringify(feats[0])));
this.$emit('done');
} else {
await std('/api/marti/package', {
method: 'PUT',
body: {
type: 'FeatureCollection',
basemaps: this.basemaps || [],
features: feats.map((f) => {
f = JSON.parse(JSON.stringify(f));
return { id: f.id || f.properties.id, type: f.type, properties: f.properties, geometry: f.geometry }
})
}
});
emit('done');
}
async function broadcast() {
const feats = currentFeats();
if (
feats.length === 1
&& !props.basemaps
&& (!feats[0].properties.attachments || feats[0].properties.attachments.length === 0)
) {
connectionStore.sendCOT(JSON.parse(JSON.stringify(feats[0])));
emit('done');
} else {
await std('/api/marti/package', {
method: 'PUT',
body: {
type: 'FeatureCollection',
basemaps: props.basemaps || [],
features: feats.map((f) => {
f = JSON.parse(JSON.stringify(f));
return { id: f.id || f.properties.id, type: f.type, properties: f.properties, geometry: f.geometry }
})
}
},
fetchList: async function() {
this.loading = true;
const url = stdurl('/api/marti/api/contacts/all');
this.contacts = await std(url);
this.loading = false;
},
});
}
}
async function fetchList() {
loading.value = true;
const url = stdurl('/api/marti/api/contacts/all');
contacts.value = await std(url) as ContactList;
loading.value = false;
}
</script>
1 change: 1 addition & 0 deletions api/web/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export type Group = paths["/marti/group"]["get"]["responses"]["200"]["content"][
export type User = paths["/user/{:username}"]["get"]["responses"]["200"]["content"]["application/json"];
export type UserList = paths["/user"]["get"]["responses"]["200"]["content"]["application/json"];

export type Contact = paths["/marti/api/contacts/all"]["get"]["responses"]["200"]["content"]["application/json"][0];
export type ContactList = paths["/marti/api/contacts/all"]["get"]["responses"]["200"]["content"]["application/json"];

export type Content = paths["/marti/package"]["put"]["responses"]["200"]["content"]["application/json"];
Expand Down

0 comments on commit 1947b34

Please sign in to comment.