feat: allow sorting of tags by creation date and size (#125)

closes #125
This commit is contained in:
Joxit 2021-04-13 05:34:28 +02:00
parent 3bfe107e3c
commit e990c39a18
No known key found for this signature in database
GPG key ID: F526592B8E012263
5 changed files with 107 additions and 43 deletions

View file

@ -16,7 +16,7 @@
--> -->
<copy-to-clipboard> <copy-to-clipboard>
<div class="copy-to-clipboard"> <div class="copy-to-clipboard">
<input style="display: none; width: 1px; height: 1px;" value="{ state.dockerCmd }"> <input style="display: none; width: 1px; height: 1px;" value="{ getDockerCmd(props) }">
<material-button waves-center="true" rounded="true" waves-color="#ddd" onClick="{ copy }" <material-button waves-center="true" rounded="true" waves-color="#ddd" onClick="{ copy }"
title="Copy pull command."> title="Copy pull command.">
<i class="material-icons">content_copy</i> <i class="material-icons">content_copy</i>
@ -27,33 +27,40 @@
ERROR_CAN_NOT_READ_CONTENT_DIGEST ERROR_CAN_NOT_READ_CONTENT_DIGEST
} from '../../scripts/utils'; } from '../../scripts/utils';
export default { export default {
onBeforeMount(props, state) { onMounted(props, state) {
this.load(props, state);
},
onUpdated(props, state) {
this.load(props, state);
},
getDockerCmd(props) {
if (props.target === 'tag') { if (props.target === 'tag') {
state.dockerCmd = `docker pull ${props.pullUrl}/${props.image.name}:${props.image.tag}`; return `docker pull ${props.pullUrl}/${props.image.name}:${props.image.tag}`;
} else {
return `docker pull ${props.pullUrl}/${props.image.name}@${props.image.digest}`
} }
}, },
onMounted(props, state) { load(props, state) {
if (props.target !== 'tag') { if (props.target !== 'tag' && !props.image.digest) {
props.image.one('content-digest', (digest) => { props.image.one('content-digest', (digest) => {
this.update({ this.update()
dockerCmd: `docker pull ${props.pullUrl}/${props.image.name}@${digest}`
})
}); });
props.image.trigger('get-content-digest'); props.image.trigger('get-content-digest');
} }
}, },
copy() { copy() {
if (!this.state.dockerCmd) { const copyText = this.$('input');
if (!copyText.value) {
this.props.onNotify(ERROR_CAN_NOT_READ_CONTENT_DIGEST); this.props.onNotify(ERROR_CAN_NOT_READ_CONTENT_DIGEST);
return; return;
} }
const copyText = this.$('input');
copyText.style.display = 'block'; copyText.style.display = 'block';
copyText.select(); copyText.select();
document.execCommand('copy'); document.execCommand('copy');
copyText.style.display = 'none'; copyText.style.display = 'none';
this.props.onNotify('`' + this.state.dockerCmd + '` has been copied to clipboard.') this.props.onNotify('`' + copyText.value + '` has been copied to clipboard.')
} }
} }
</script> </script>

View file

@ -15,11 +15,20 @@ Copyright (C) 2016-2021 Jones Magloire @Joxit
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
--> -->
<image-content-digest> <image-content-digest>
<div title="{ state.title }">{ state.displayId }</div> <div title="{ getTitle(props.image, state.chars) }">{ getDigest(props.image, state.chars) }</div>
<script> <script>
export default { export default {
onMounted(props) { onMounted(props, state) {
this.chars = -1; this.load(props, state);
},
onUpdated(props, state) {
this.load(props, state);
},
load(props, state) {
if (props.image.digest) {
return;
}
state.chars = -1;
props.image.one('content-digest', (digest) => { props.image.one('content-digest', (digest) => {
this.digest = digest; this.digest = digest;
props.image.on('content-digest-chars', this.onResize); props.image.on('content-digest-chars', this.onResize);
@ -28,22 +37,23 @@ Copyright (C) 2016-2021 Jones Magloire @Joxit
props.image.trigger('get-content-digest'); props.image.trigger('get-content-digest');
}, },
onResize(chars) { onResize(chars) {
if (chars === this.chars) { if (chars !== this.state.chars) {
return; this.update({
} chars
let displayId = this.digest; });
let title = ''; }
this.chars = chars; },
if (chars >= 70) { getTitle(image, chars) {
displayId = this.digest; return chars >= 70 ? '' : (image.digest || '');
} else if (chars <= 0) { },
displayId = ''; getDigest(image, chars) {
title = this.digest; if (chars >= 70) {
} else { return image.digest || '';
displayId = this.digest.slice(0, chars) + '...'; } else if (chars <= 0) {
title = this.digest; return '';
} } else {
this.update({title, displayId}); return image.digest && image.digest.slice(0, chars) + '...';
}
} }
} }
</script> </script>

View file

@ -15,17 +15,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
--> -->
<image-date> <image-date>
<div title="Creation date { state.localDate }">{ dateFormat(state.date) } ago</div> <div title="Creation date { getLocalDate(props.image) }">{ getDate(props.image) } ago</div>
<script> <script>
import { import {
dateFormat, dateFormat,
} from '../../scripts/utils'; } from '../../scripts/utils';
export default { export default {
state: {
localDate: 'unknown'
},
onMounted(props) { onMounted(props) {
props.image.on('creation-date', (date) => { props.image.one('creation-date', (date) => {
this.update({ this.update({
date: date, date: date,
localDate: date.toLocaleString() localDate: date.toLocaleString()
@ -33,7 +30,12 @@
}); });
props.image.trigger('get-date'); props.image.trigger('get-date');
}, },
dateFormat getDate(image) {
return dateFormat(image.creationDate)
},
getLocalDate(image) {
return (image.creationDate && image.creationDate.toLocaleString()) || 'unknown'
}
} }
</script> </script>
</image-date> </image-date>

View file

@ -15,21 +15,33 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
--> -->
<image-size> <image-size>
<div title="Compressed size of your image.">{ bytesToSize(state.size) }</div> <div title="Compressed size of your image.">{ getImageSize(props.image) }</div>
<script> <script>
import { import {
bytesToSize, bytesToSize,
} from '../../scripts/utils'; } from '../../scripts/utils';
export default { export default {
onMounted(props) { onMounted(props, state) {
this.load(props, state);
},
onUpdated(props, state) {
this.load(props, state);
},
load(props, state) {
if (typeof props.image.size === 'number') {
return;
}
props.image.on('size', (size) => { props.image.on('size', (size) => {
this.update({ this.update({
size size
}); });
}); });
props.image.trigger('get-size'); props.image.trigger('get-size');
}, },
bytesToSize getImageSize(image) {
return bytesToSize(image.size)
}
} }
</script> </script>
</image-size> </image-size>

View file

@ -19,13 +19,21 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<table style="border: none;"> <table style="border: none;">
<thead> <thead>
<tr> <tr>
<th class="creation-date">Creation date</th> <th
<th class="image-size">Size</th> class="creation-date { (state.desc && state.orderType === 'date') ? 'material-card-th-sorted-descending' : 'material-card-th-sorted-ascending' }"
onclick="{() => onPageReorder('date') }">
Creation date
</th>
<th
class="image-size { (state.desc && state.orderType === 'size') ? 'material-card-th-sorted-descending' : 'material-card-th-sorted-ascending' }"
onclick="{() => onPageReorder('size') }">
Size
</th>
<th id="image-content-digest-header" if="{ props.showContentDigest }">Content Digest</th> <th id="image-content-digest-header" if="{ props.showContentDigest }">Content Digest</th>
<th id="image-tag-header" <th id="image-tag-header"
class="{ props.asc ? 'material-card-th-sorted-ascending' : 'material-card-th-sorted-descending' }" class="{ props.asc ? 'material-card-th-sorted-ascending' : 'material-card-th-sorted-descending' }"
onclick="{() => props.onReverseOrder() }">Tag onclick="{ onReverseOrder }">Tag
</th> </th>
<th class="show-tag-history">History</th> <th class="show-tag-history">History</th>
<th class="remove-tag { state.toDelete.size > 0 ? 'delete' : '' }" if="{ props.isImageRemoveActivated }"> <th class="remove-tag { state.toDelete.size > 0 ? 'delete' : '' }" if="{ props.isImageRemoveActivated }">
@ -138,7 +146,32 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
toDelete: this.state.toDelete toDelete: this.state.toDelete
}) })
}, },
getPage, onReverseOrder() {
this.state.orderType = null;
this.state.desc = false;
this.props.onReverseOrder();
},
onPageReorder(type) {
this.update({
orderType: type,
desc: (this.state.orderType && this.state.orderType !== type) || !this.state.desc
})
},
getPage(tags, page) {
const sortedTags = getPage(tags, page);
if (this.state.orderType === 'date') {
sortedTags.sort((e1, e2) =>
!this.state.desc ?
e2.creationDate.getTime() - e1.creationDate.getTime() :
e1.creationDate.getTime() - e2.creationDate.getTime());
} else if (this.state.orderType === 'size') {
sortedTags.sort((e1, e2) =>
!this.state.desc ?
e2.size - e1.size :
e1.size - e2.size);
}
return sortedTags;
},
matchSearch matchSearch
} }
</script> </script>