Merge pull request #6991 from ivanscinote/SCI-10010-ik

Add wrong-version modal for SciNote Edit [SCI-10010]
This commit is contained in:
Martin Artnik 2024-02-01 10:49:30 +01:00 committed by GitHub
commit 1458fd20a8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 159 additions and 54 deletions

View file

@ -57,11 +57,15 @@
:fileName="attachment.attributes.file_name"
@confirm="showNoPredefinedAppModal = false"
/>
<UpdateVersionModal
v-if="showUpdateVersionModal"
@cancel="showUpdateVersionModal = false"
/>
<editLaunchingApplicationModal
v-if="editAppModal"
:fileName="attachment.attributes.file_name"
:application="this.localAppName"
@cancel="editAppModal = false"
v-if="editAppModal"
:fileName="attachment.attributes.file_name"
:application="this.localAppName"
@cancel="editAppModal = false"
/>
</Teleport>
</div>

View file

@ -1,43 +1,52 @@
import axios from '../../../../../packs/custom_axios.js';
import { satisfies } from 'compare-versions';
import editLaunchingApplicationModal from '../../modal/edit_launching_application_modal.vue';
import NoPredefinedAppModal from '../../modal/no_predefined_app_modal.vue';
import UpdateVersionModal from '../../modal/update_version_modal.vue';
export default {
data() {
return {
localAppName: null,
scinoteEditRunning: false,
scinoteEditVersion: null,
showNoPredefinedAppModal: false,
showUpdateVersionModal: false,
editAppModal: false
};
},
components: {
editLaunchingApplicationModal,
NoPredefinedAppModal
NoPredefinedAppModal,
UpdateVersionModal
},
computed: {
attributes() {
return this.attachment.attributes;
},
canOpenLocally() {
return this.scinoteEditRunning &&
!!this.attachment.attributes.urls.open_locally &&
this.attachment.attributes.asset_type !== 'gene_sequence' &&
this.attachment.attributes.asset_type !== 'marvinjs'
return this.scinoteEditRunning
&& !!this.attributes.urls.open_locally
&& this.attributes.asset_type !== 'gene_sequence'
&& this.attributes.asset_type !== 'marvinjs';
}
},
methods: {
async fetchLocalAppInfo() {
try {
const statusResponse = await axios.get(
`${this.attachment.attributes.urls.open_locally_api}/status`
`${this.attributes.urls.open_locally_api}/status`
);
if (statusResponse.status === 200) {
this.scinoteEditRunning = true;
this.scinoteEditVersion = statusResponse.data.version;
} else {
return;
}
const response = await axios.get(
`${this.attachment.attributes.urls.open_locally_api}/default-application/${this.attachment.attributes.file_extension}`
`${this.attributes.urls.open_locally_api}/default-application/${this.attributes.file_extension}`
);
if (response.data.application.toLowerCase() !== 'pick an app') {
@ -48,18 +57,25 @@ export default {
}
},
async openLocally() {
if (this.localAppName === null) {
if (this.isWrongVersion(this.scinoteEditVersion)) {
this.showUpdateVersionModal = true;
return;
} else if (this.localAppName === null) {
this.showNoPredefinedAppModal = true;
return;
}
this.editAppModal = true;
try {
const { data } = await axios.get(this.attachment.attributes.urls.open_locally);
await axios.post(this.attachment.attributes.urls.open_locally_api + '/download', data);
const { data } = await axios.get(this.attributes.urls.open_locally);
await axios.post(`${this.attributes.urls.open_locally_api}/download`, data);
} catch (error) {
console.error('Error in request:', error);
}
},
isWrongVersion(version) {
const { min, max } = this.attributes.edit_version_range;
return !satisfies(version, `${min} - ${max}`);
}
}
}

View file

@ -1,11 +1,11 @@
<template>
<div class="sn-open-locally-menu">
<div v-if="!this.canOpenLocally && (this.attachment.attributes.wopi && this.attachment.attributes.urls.edit_asset)">
<a :href="`${this.attachment.attributes.urls.edit_asset}`" target="_blank"
class="block whitespace-nowrap rounded px-3 py-2.5
hover:!text-sn-blue hover:no-underline cursor-pointer hover:bg-sn-super-light-grey">
{{ this.attachment.attributes.wopi_context.button_text }}
</a>
<div class="sn-open-locally-menu" @mouseenter="fetchLocalAppInfo">
<div v-if="!canOpenLocally && (attachment.attributes.wopi && attachment.attributes.urls.edit_asset)">
<a :href="`${attachment.attributes.urls.edit_asset}`" target="_blank"
class="block whitespace-nowrap rounded px-3 py-2.5
hover:!text-sn-blue hover:no-underline cursor-pointer hover:bg-sn-super-light-grey">
{{ attachment.attributes.wopi_context.button_text }}
</a>
</div>
<div v-else>
<MenuDropdown
@ -16,11 +16,11 @@
:position="'right'"
:btnText="i18n.t('attachments.open_in')"
:caret="true"
@openLocally="openLocally"
@openImageEditor="openImageEditor"
@open-locally="openLocally"
@open-image-editor="openImageEditor"
></MenuDropdown>
<a v-else-if="this.menu.length === 1" class="btn btn-light" :href="this.menu[0].url" :target="this.menu[0].target" @click="this[this.menu[0].emit]()">
{{ this.menu[0].text }}
<a v-else-if="menu.length === 1" class="btn btn-light !bg-sn-white" :href="menu[0].url" :target="menu[0].target" @click="this[this.menu[0].emit]()">
{{ menu[0].text }}
</a>
</div>
@ -31,10 +31,14 @@
@confirm="showNoPredefinedAppModal = false"
/>
<editLaunchingApplicationModal
v-if="editAppModal"
:fileName="attachment.attributes.file_name"
:application="this.localAppName"
@cancel="editAppModal = false"
v-if="editAppModal"
:fileName="attachment.attributes.file_name"
:application="this.localAppName"
@cancel="editAppModal = false"
/>
<UpdateVersionModal
v-if="showUpdateVersionModal"
@cancel="showUpdateVersionModal = false"
/>
</Teleport>
</div>
@ -43,17 +47,21 @@
<script>
import OpenLocallyMixin from './mixins/open_locally.js';
import MenuDropdown from '../../menu_dropdown.vue';
import UpdateVersionModal from '../modal/update_version_modal.vue';
export default {
name: 'OpenLocallyMenu',
mixins: [OpenLocallyMixin],
components: { MenuDropdown },
components: { MenuDropdown, UpdateVersionModal },
props: {
data: { type: String, required: true },
attachment: { type: Object, required: true }
},
created() {
this.attachment = { attributes: JSON.parse(this.data) };
this.fetchLocalAppInfo();
window.openLocallyMenu = this;
},
beforeUnmount() {
delete window.openLocallyMenuComponent;
},
computed: {
menu() {

View file

@ -145,6 +145,10 @@
:fileName="attachment.attributes.file_name"
@confirm="showNoPredefinedAppModal = false"
/>
<UpdateVersionModal
v-if="showUpdateVersionModal"
@cancel="showUpdateVersionModal = false"
/>
<a class="image-edit-button hidden"
v-if="attachment.attributes.asset_type != 'marvinjs'
&& attachment.attributes.image_editable
@ -167,7 +171,6 @@ import ContextMenu from './context_menu.vue';
import deleteAttachmentModal from './delete_modal.vue';
import MenuDropdown from '../../../shared/menu_dropdown.vue';
import MoveAssetModal from '../modal/move.vue';
import NoPredefinedAppModal from '../modal/no_predefined_app_modal.vue';
import MoveMixin from './mixins/move.js';
import OpenLocallyMixin from './mixins/open_locally.js';
import { vOnClickOutside } from '@vueuse/components';
@ -179,7 +182,6 @@ export default {
ContextMenu,
deleteAttachmentModal,
MoveAssetModal,
NoPredefinedAppModal,
MenuDropdown
},
props: {
@ -196,8 +198,7 @@ export default {
return {
showOptions: false,
deleteModal: false,
isMenuOpen: false,
showNoPredefinedAppModal: false
isMenuOpen: false
};
},
directives: {

View file

@ -5,14 +5,14 @@
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><i class="sn-icon sn-icon-close"></i></button>
<h4 class="modal-title" id="modal-delete-result-element">
{{ i18n.t('assets.asset_sync.set_up_app') }}
{{ i18n.t('assets.no_predefined_app_modal.set_up_app') }}
</h4>
</div>
<div class="modal-body">
<p v-html="i18n.t('assets.asset_sync.body_text_html', { file_name: fileName })"></p>
<p v-html="i18n.t('assets.no_predefined_app_modal.body_text_html', { file_name: fileName })"></p>
</div>
<div class="modal-footer">
<button class="btn btn-primary" @click="confirm">{{ this.i18n.t('assets.asset_sync.no_predefined_app') }}</button>
<button class="btn btn-primary" @click="confirm">{{ this.i18n.t('assets.no_predefined_app_modal.understand_button') }}</button>
</div>
</div>
</div>

View file

@ -0,0 +1,54 @@
<template>
<div ref="modal" @keydown.esc="cancel" class="modal" id="modalUpdateVersion" tabindex="-1" role="dialog">
<div class="modal-dialog modal-md" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><i class="sn-icon sn-icon-close"></i></button>
<h4 class="modal-title" id="modal-delete-result-element">
{{ i18n.t('assets.update_version_modal.title') }}
</h4>
</div>
<div class="modal-body">
<p v-html="i18n.t('assets.update_version_modal.body_text_html')"></p>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" @click="cancel">{{ i18n.t('general.cancel') }}</button>
<ScinoteEditDownload
:data="userAgent"
:isUpdateVersionModal="true"
/>
</div>
</div>
</div>
</div>
</template>
<script>
import ScinoteEditDownload from '../../../../vue/shared/scinote_edit_download.vue';
export default {
name: 'UpdateVersionModal',
components: {
ScinoteEditDownload
},
props: {
fileName: String
},
computed: {
userAgent() {
return window.navigator.userAgent;
}
},
mounted() {
$(this.$refs.modal).modal('show');
$(this.$refs.modal).on('hidden.bs.modal', () => {
this.$emit('cancel');
});
},
methods: {
cancel() {
$(this.$refs.modal).modal('hide');
}
}
};
</script>

View file

@ -1,7 +1,7 @@
<template>
<div class="buttons">
<template v-if="isWindows">
<a :href="responseData[0]['url']"
<a :href="getWindowsHref"
class="btn btn-primary new-project-btn"
:title="i18n.t('users.settings.account.addons.desktop_app.windows_button')"
role="button"
@ -9,13 +9,13 @@
target="_blank">
<span class="hidden-xs">{{ i18n.t('users.settings.account.addons.desktop_app.windows_button') }}</span>
</a>
<div class="text-xs pt-2 pb-6" style="color: var(--sn-sleepy-grey);">
<div v-if="showButtonLabel" class="text-xs pt-2 pb-6" style="color: var(--sn-sleepy-grey);">
{{ i18n.t('users.settings.account.addons.desktop_app.version', { version: this.responseData[0]['version']}) }}
</div>
</template>
<template v-else-if="isMac">
<a :href="responseData[1]['url']"
<a :href="getMacHref"
class="btn btn-primary new-project-btn"
:title="i18n.t('users.settings.account.addons.desktop_app.macos_button')"
role="button"
@ -23,7 +23,7 @@
target="_blank">
<span class="hidden-xs">{{ i18n.t('users.settings.account.addons.desktop_app.macos_button') }}</span>
</a>
<div class="text-xs pt-2 pb-6" style="color: var(--sn-sleepy-grey);">
<div v-if="showButtonLabel" class="text-xs pt-2 pb-6" style="color: var(--sn-sleepy-grey);">
{{ i18n.t('users.settings.account.addons.desktop_app.version', { version: this.responseData[1]['version']}) }}
</div>
</template>
@ -31,7 +31,7 @@
<template v-else>
<div class="flex">
<div>
<a :href="responseData[0]['url']"
<a :href="getWindowsHref"
class="btn btn-primary new-project-btn"
:title="i18n.t('users.settings.account.addons.desktop_app.windows_button')"
role="button"
@ -39,7 +39,7 @@
target="_blank">
<span class="hidden-xs">{{ i18n.t('users.settings.account.addons.desktop_app.windows_button') }}</span>
</a>
<div class="text-xs pt-2 pb-6" style="color: var(--sn-sleepy-grey);">
<div v-if="showButtonLabel" class="text-xs pt-2 pb-6" style="color: var(--sn-sleepy-grey);">
{{ i18n.t('users.settings.account.addons.desktop_app.version',
{ version: this.responseData[0]['version']})
}}
@ -47,7 +47,7 @@
</div>
<div class="ml-2">
<a :href="responseData[1]['url']"
<a :href="getMacHref"
class="btn btn-primary new-project-btn"
:title="i18n.t('users.settings.account.addons.desktop_app.macos_button')"
role="button"
@ -55,7 +55,7 @@
target="_blank">
<span class="hidden-xs">{{ i18n.t('users.settings.account.addons.desktop_app.macos_button') }}</span>
</a>
<p class="text-xs pt-2 pb-6" style="color: var(--sn-sleepy-grey);">
<p v-if="showButtonLabel" class="text-xs pt-2 pb-6" style="color: var(--sn-sleepy-grey);">
{{ i18n.t('users.settings.account.addons.desktop_app.version',
{ version: this.responseData[1]['version']})
}}
@ -78,12 +78,13 @@
export default {
name: 'ScinoteEditDownload',
props: {
data: { type: String, required: true }
data: { type: String, required: true },
isUpdateVersionModal: { type: Boolean, required: false }
},
data() {
return {
userAgent: this.data,
responseData: {}
responseData: []
};
},
computed: {
@ -92,6 +93,15 @@ export default {
},
isMac() {
return /Mac OS/.test(this.userAgent);
},
showButtonLabel() {
return this.responseData && this.responseData.length > 0 && !this.isUpdateVersionModal;
},
getWindowsHref() {
return this.responseData && this.responseData.length > 0 ? this.responseData[0].url : '#';
},
getMacHref() {
return this.responseData && this.responseData.length > 0 ? this.responseData[1].url : '#';
}
},
created() {

View file

@ -11,7 +11,7 @@ class AssetSerializer < ActiveModel::Serializer
attributes :file_name, :file_extension, :view_mode, :icon, :urls, :updated_at_formatted,
:file_size, :medium_preview, :large_preview, :asset_type, :wopi,
:wopi_context, :pdf_previewable, :file_size_formatted, :asset_order,
:updated_at, :metadata, :image_editable, :image_context, :pdf, :attached, :parent_type
:updated_at, :metadata, :image_editable, :image_context, :pdf, :attached, :parent_type, :edit_version_range
def icon
file_fa_icon_class(object)
@ -117,6 +117,10 @@ class AssetSerializer < ActiveModel::Serializer
end
end
def edit_version_range
{ min: Constants::MIN_SCINOTE_EDIT_VERSION, max: Constants::MAX_SCINOTE_EDIT_VERSION }
end
def urls
urls = {
preview: asset_file_preview_path(object),

View file

@ -4,7 +4,7 @@
<% if can_edit && !preview %>
<% if wopi_enabled? && wopi_file?(asset) %>
<div id="openLocallyMenu" data-behaviour="vue">
<open-locally-menu data="<%= AssetSerializer.new(asset, scope: { user: current_user }).to_json %>"/>
<open-locally-menu :attachment="<%= { attributes: AssetSerializer.new(asset, scope: { user: current_user }).as_json }.to_json %>" />
</div>
<% elsif asset.file.metadata[:asset_type] == 'marvinjs' %>
<button class="btn btn-light marvinjs-edit-button"
@ -40,7 +40,7 @@
</button>
<% end %>
<div id="openLocallyMenu" data-behaviour="vue">
<open-locally-menu data="<%= AssetSerializer.new(asset, scope: { user: current_user }).to_json %>"/>
<open-locally-menu :attachment="<%= { attributes: AssetSerializer.new(asset, scope: { user: current_user }).as_json }.to_json %>" />
</div>
<% end %>
<a class="btn btn-light file-download-link" href="<%= rails_blob_path(asset.file, disposition: 'attachment') %>" data-turbolinks="false">

View file

@ -434,6 +434,10 @@ class Constants
# Grover timeout in ms
GROVER_TIMEOUT_MS = 300000
# SciNote Edit supported versions
MIN_SCINOTE_EDIT_VERSION = ENV['MIN_SCINOTE_EDIT_VERSION'].freeze
MAX_SCINOTE_EDIT_VERSION = ENV['MAX_SCINOTE_EDIT_VERSION'].freeze
# ) \ / (
# /|\ )\_/( /|\
# * / | \ (/\|/\) / | \ *

View file

@ -401,6 +401,7 @@ en:
open_locally_in: "Open in %{application}"
open_in: "Open in"
open_locally: "Open locally"
open_in: "Open in"
new:
description: 'New'
uploading: 'Uploading'
@ -3614,10 +3615,13 @@ en:
empty_office_file:
description: "The file is empty. Please add some data before saving the file."
reload: "Reload file"
asset_sync:
no_predefined_app_modal:
body_text_html: "The specified application for accessing the <b>%{file_name}</b> is not preconfigured. To successfully open this file, please set up the appropriate application in your local environment beforehand.</p>"
no_predefined_app: "I understand"
understand_button: "I understand"
set_up_app: "Set up an application to open this file"
update_version_modal:
title: "Update required"
body_text_html: "The current version of the SciNote Edit application is no longer supported. To ensure a seamless and secure user experience, we recommend installing to the latest version."
edit_launching_application_modal:
title: "Launching application"
description: "%{file_name} will now open in %{application}. Saved changes in %{application} will automatically be synced in SciNote."