diff --git a/app/src/interfaces/file-image/file-image.vue b/app/src/interfaces/file-image/file-image.vue
index 4730518136..4a0557eb4a 100644
--- a/app/src/interfaces/file-image/file-image.vue
+++ b/app/src/interfaces/file-image/file-image.vue
@@ -35,8 +35,14 @@
-
-
+
+
@@ -61,7 +67,13 @@
:primary-key="image.id"
:edits="edits"
@input="update"
- />
+ >
+
+
+
+
+
+
@@ -76,7 +88,7 @@ import api, { addTokenToURL } from '@/api';
import { useRelationM2O } from '@/composables/use-relation-m2o';
import { RelationQuerySingle, useRelationSingle } from '@/composables/use-relation-single';
import { formatFilesize } from '@/utils/format-filesize';
-import { getRootPath } from '@/utils/get-root-path';
+import { getAssetUrl } from '@/utils/get-asset-url';
import { readableMimeType } from '@/utils/readable-mime-type';
import DrawerItem from '@/views/private/components/drawer-item.vue';
import FileLightbox from '@/views/private/components/file-lightbox.vue';
@@ -142,11 +154,6 @@ const src = computed(() => {
const ext = computed(() => (image.value ? readableMimeType(image.value.type, true) : 'unknown'));
-const downloadSrc = computed(() => {
- if (!image.value) return null;
- return addTokenToURL(getRootPath() + 'assets/' + image.value.id);
-});
-
const meta = computed(() => {
if (!image.value) return null;
const { filesize, width, height, type } = image.value;
@@ -238,74 +245,76 @@ img {
}
}
-.shadow {
- position: absolute;
- bottom: 0;
- left: 0;
- z-index: 2;
- width: 100%;
- height: 40px;
- overflow: hidden;
- line-height: 1;
- white-space: nowrap;
- text-overflow: ellipsis;
- background: linear-gradient(180deg, rgb(38 50 56 / 0) 0%, rgb(38 50 56 / 0.25) 100%);
- transition: height var(--fast) var(--transition);
-}
+.image-preview {
+ .shadow {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ z-index: 2;
+ width: 100%;
+ height: 40px;
+ overflow: hidden;
+ line-height: 1;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ background: linear-gradient(180deg, rgb(38 50 56 / 0) 0%, rgb(38 50 56 / 0.25) 100%);
+ transition: height var(--fast) var(--transition);
+ }
-.actions {
- --v-button-color: var(--foreground-subdued);
- --v-button-background-color: var(--white);
- --v-button-color-hover: var(--foreground-normal);
- --v-button-background-color-hover: var(--white);
+ .actions {
+ --v-button-color: var(--foreground-subdued);
+ --v-button-background-color: var(--white);
+ --v-button-color-hover: var(--foreground-normal);
+ --v-button-background-color-hover: var(--white);
- position: absolute;
- top: calc(50% - 32px);
- left: 0;
- z-index: 3;
- display: flex;
- justify-content: center;
- width: 100%;
+ position: absolute;
+ top: calc(50% - 32px);
+ left: 0;
+ z-index: 3;
+ display: flex;
+ justify-content: center;
+ width: 100%;
- .v-button {
- margin-right: 12px;
- transform: translateY(10px);
- opacity: 0;
- transition: var(--medium) var(--transition);
- transition-property: opacity transform;
+ .v-button {
+ margin-right: 12px;
+ transform: translateY(10px);
+ opacity: 0;
+ transition: var(--medium) var(--transition);
+ transition-property: opacity transform;
- @for $i from 0 through 4 {
- &:nth-of-type(#{$i + 1}) {
- transition-delay: $i * 25ms;
+ @for $i from 0 through 4 {
+ &:nth-of-type(#{$i + 1}) {
+ transition-delay: $i * 25ms;
+ }
}
}
+
+ .v-button:last-child {
+ margin-right: 0px;
+ }
}
- .v-button:last-child {
- margin-right: 0px;
+ .info {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ z-index: 3;
+ width: 100%;
+ padding: 8px 12px;
+ line-height: 1.2;
}
-}
-.info {
- position: absolute;
- bottom: 0;
- left: 0;
- z-index: 3;
- width: 100%;
- padding: 8px 12px;
- line-height: 1.2;
-}
+ .title {
+ color: var(--white);
+ }
-.title {
- color: var(--white);
-}
-
-.meta {
- height: 17px;
- max-height: 0;
- overflow: hidden;
- color: rgb(255 255 255 / 0.75);
- transition: max-height var(--fast) var(--transition);
+ .meta {
+ height: 17px;
+ max-height: 0;
+ overflow: hidden;
+ color: rgb(255 255 255 / 0.75);
+ transition: max-height var(--fast) var(--transition);
+ }
}
.image-preview:focus-within,
diff --git a/app/src/interfaces/file/file.vue b/app/src/interfaces/file/file.vue
index ad9b096311..fea17a7c4c 100644
--- a/app/src/interfaces/file/file.vue
+++ b/app/src/interfaces/file/file.vue
@@ -46,7 +46,7 @@
-
+
{{ t('download_file') }}
@@ -86,7 +86,13 @@
:edits="edits"
:disabled="disabled"
@input="update"
- />
+ >
+
+
+
+
+
+
{
return '/assets/' + id;
});
-const downloadURL = computed(() => {
- return addTokenToURL(getRootPath() + assetURL.value.slice(1));
-});
-
const imageThumbnail = computed(() => {
if (file.value === null || props.value === null) return null;
if (file.value.type.includes('svg')) return assetURL.value;
diff --git a/app/src/interfaces/files/files.vue b/app/src/interfaces/files/files.vue
index 0875ca2848..920ca0c7ff 100644
--- a/app/src/interfaces/files/files.vue
+++ b/app/src/interfaces/files/files.vue
@@ -47,11 +47,15 @@
-
+
{{ t('open_file_in_tab') }}
-
+
{{ t('download_file') }}
@@ -89,7 +93,7 @@
secondary
rounded
icon
- download
+ :download="downloadName"
:href="downloadUrl"
>
@@ -129,12 +133,11 @@ import { useI18n } from 'vue-i18n';
import DrawerItem from '@/views/private/components/drawer-item.vue';
import DrawerCollection from '@/views/private/components/drawer-collection.vue';
import Draggable from 'vuedraggable';
+import { getAssetUrl } from '@/utils/get-asset-url';
import { adjustFieldsForDisplays } from '@/utils/adjust-fields-for-displays';
import { get, clamp, isEmpty } from 'lodash';
import { usePermissionsStore } from '@/stores/permissions';
import { useUserStore } from '@/stores/user';
-import { addTokenToURL } from '@/api';
-import { getRootPath } from '@/utils/get-root-path';
import { getFieldsFromTemplate } from '@directus/shared/utils';
import { Filter } from '@directus/shared/types';
@@ -202,7 +205,7 @@ const templateWithDefaults = computed(() => {
const fields = computed(() =>
adjustFieldsForDisplays(
- getFieldsFromTemplate(templateWithDefaults.value),
+ [...getFieldsFromTemplate(templateWithDefaults.value), `${relationInfo.value?.relation.field}.filename_download`],
relationInfo.value?.junctionCollection.collection ?? ''
)
);
@@ -317,23 +320,28 @@ function onSelect(selected: string[]) {
select(selected.filter((id) => selectedPrimaryKeys.value.includes(id) === false));
}
-const downloadUrl = computed(() => {
+const downloadName = computed(() => {
if (relatedPrimaryKey.value === null || relationInfo.value?.relatedCollection.collection !== 'directus_files') return;
- return addTokenToURL(getRootPath() + `assets/${relatedPrimaryKey.value}?download`);
+ const junctionField = relationInfo.value.junctionField.field;
+ const relationPkField = relationInfo.value.relatedPrimaryKeyField.field;
+
+ return displayItems.value.find((item) => get(item, [junctionField, relationPkField]))?.directus_files_id
+ ?.filename_download;
});
-function getUrl(junctionRow: Record, addDownload?: boolean) {
+const downloadUrl = computed(() => {
+ if (relatedPrimaryKey.value === null || relationInfo.value?.relatedCollection.collection !== 'directus_files') return;
+ return getAssetUrl(String(relatedPrimaryKey.value), true);
+});
+
+function getFilename(junctionRow: Record) {
const junctionField = relationInfo.value?.junctionField.field;
if (!junctionField) return;
const key = junctionRow[junctionField]?.id ?? junctionRow[junctionField] ?? null;
if (!key) return null;
- if (addDownload) {
- return addTokenToURL(getRootPath() + `assets/${key}?download`);
- }
-
- return addTokenToURL(getRootPath() + `assets/${key}`);
+ return key;
}
const customFilter = computed(() => {
diff --git a/app/src/modules/files/routes/item.vue b/app/src/modules/files/routes/item.vue
index 9e4a20773c..ed88307685 100644
--- a/app/src/modules/files/routes/item.vue
+++ b/app/src/modules/files/routes/item.vue
@@ -65,9 +65,15 @@
-
-
-
+
+