diff --git a/.drone.yml b/.drone.yml index 347ca88b..6b7e89aa 100644 --- a/.drone.yml +++ b/.drone.yml @@ -80,25 +80,13 @@ steps: depends_on: - dependencies - # Building in dev mode to avoid the service worker for testing - - name: build-dev - image: node:16 - pull: true - environment: - YARN_CACHE_FOLDER: .cache/yarn/ - CYPRESS_CACHE_FOLDER: .cache/cypress/ - commands: - - yarn build:dev - depends_on: - - dependencies - - name: build-prod image: node:16 pull: true environment: YARN_CACHE_FOLDER: .cache/yarn/ commands: - - yarn build --dest dist-prod + - yarn build depends_on: - dependencies @@ -120,12 +108,12 @@ steps: CYPRESS_CACHE_FOLDER: .cache/cypress/ CYPRESS_DEFAULT_COMMAND_TIMEOUT: 60000 commands: - - sed -i 's/localhost/api/g' dist-dev/index.html - - yarn serve:dist-dev & npx wait-on http://localhost:5000 + - sed -i 's/localhost/api/g' dist/index.html + - yarn serve:dist & npx wait-on http://localhost:5000 - yarn test:frontend --browser chrome depends_on: - dependencies - - build-dev + - build-prod - name: upload-test-results image: plugins/s3 diff --git a/cypress/support/index.js b/cypress/support/index.js index 22ec961f..0c885c65 100644 --- a/cypress/support/index.js +++ b/cypress/support/index.js @@ -2,3 +2,10 @@ import './commands' import 'cypress-file-upload' import '@4tw/cypress-drag-drop' + +// see https://github.com/cypress-io/cypress/issues/702#issuecomment-587127275 +Cypress.on('window:before:load', (win) => { + // disable service workers + // @ts-ignore + delete win.navigator.__proto__.ServiceWorker +}) \ No newline at end of file diff --git a/src/components/home/contentNoAuth.vue b/src/components/home/contentNoAuth.vue index 8229c799..c60a070b 100644 --- a/src/components/home/contentNoAuth.vue +++ b/src/components/home/contentNoAuth.vue @@ -19,6 +19,7 @@ import {mapState} from 'vuex' import logoUrl from '@/assets/logo-full.svg' +import { saveLastVisited } from '@/helpers/saveLastVisited' export default { name: 'contentNoAuth', @@ -57,6 +58,7 @@ export default { localStorage.getItem('passwordResetToken') === null && localStorage.getItem('emailConfirmToken') === null ) { + saveLastVisited(this.$route.name, this.$route.params) this.$router.push({name: 'user.login'}) } }, diff --git a/src/components/tasks/add-task.vue b/src/components/tasks/add-task.vue index 7802f05c..ade0ea76 100644 --- a/src/components/tasks/add-task.vue +++ b/src/components/tasks/add-task.vue @@ -10,7 +10,7 @@ v-focus v-model="newTaskTitle" ref="newTaskInput" - :style="{'height': `${textAreaHeight}px`}" + :style="{'height': `calc(${textAreaHeight}px - 2px + 1rem)`}" @keyup="errorMessage = ''" @keydown.enter="handleEnter" /> @@ -40,7 +40,8 @@ import TaskService from '../../services/task' import QuickAddMagic from '@/components/tasks/partials/quick-add-magic.vue' -const INITIAL_SCROLL_HEIGHT = 40 +const INPUT_BORDER_PX = 2 +const LINE_HEIGHT = 1.5 // using getComputedStyles().lineHeight returns an (wrong) absolute pixel value, we need the factor to do calculations with it. const cleanupTitle = title => { return title.replace(/^((\* |\+ |- )(\[ \] )?)/g, '') @@ -54,7 +55,8 @@ export default { newTaskTitle: '', taskService: new TaskService(), errorMessage: '', - textAreaHeight: INITIAL_SCROLL_HEIGHT, + textAreaHeight: null, + initialTextAreaHeight: null, } }, components: { @@ -68,14 +70,17 @@ export default { }, watch: { newTaskTitle(newVal) { - let scrollHeight = this.$refs.newTaskInput.scrollHeight - if (scrollHeight < INITIAL_SCROLL_HEIGHT || newVal === '') { - scrollHeight = INITIAL_SCROLL_HEIGHT - } + // Calculating the textarea height based on lines of input in it. That is more reliable when removing a + // line from the input. + const numberOfLines = newVal.split(/\r\n|\r|\n/).length + const fontSize = parseInt(window.getComputedStyle(this.$refs.newTaskInput, null).getPropertyValue('font-size')) - this.textAreaHeight = scrollHeight + this.textAreaHeight = numberOfLines * fontSize * LINE_HEIGHT + INPUT_BORDER_PX }, }, + mounted() { + this.initialTextAreaHeight = this.$refs.newTaskInput.scrollHeight + INPUT_BORDER_PX + }, methods: { addTask() { if (this.newTaskTitle === '') { diff --git a/src/helpers/saveLastVisited.ts b/src/helpers/saveLastVisited.ts new file mode 100644 index 00000000..fc731d26 --- /dev/null +++ b/src/helpers/saveLastVisited.ts @@ -0,0 +1,18 @@ +const LAST_VISITED_KEY = 'lastVisited' + +export const saveLastVisited = (name: string, params: object) => { + localStorage.setItem(LAST_VISITED_KEY, JSON.stringify({name, params})) +} + +export const getLastVisited = () => { + const lastVisited = localStorage.getItem(LAST_VISITED_KEY) + if (lastVisited === null) { + return null + } + + return JSON.parse(lastVisited) +} + +export const clearLastVisited = () => { + return localStorage.removeItem(LAST_VISITED_KEY) +} diff --git a/src/i18n/lang/de-DE.json b/src/i18n/lang/de-DE.json index e0671444..4482730c 100644 --- a/src/i18n/lang/de-DE.json +++ b/src/i18n/lang/de-DE.json @@ -128,7 +128,7 @@ } }, "list": { - "archived": "This list is archived. It is not possible to create new or edit tasks for it.", + "archived": "Diese Liste ist archiviert. Es ist nicht möglich, neue Aufgaben zu erstellen oder sie zu bearbeiten.", "title": "Listentitel", "color": "Farbe", "lists": "Listen", @@ -626,7 +626,7 @@ "addSuccess": "Das Label wurde erfolgreich hinzugefügt.", "createSuccess": "Das Label wurde erfolgreich erstellt.", "removeSuccess": "Das Label wurde erfolgreich entfernt.", - "addCreateSuccess": "The label has been created and added successfully." + "addCreateSuccess": "Das Label wurde erfolgreich erstellt und hinzugefügt." }, "priority": { "unset": "Nicht eingestellt", @@ -646,19 +646,19 @@ "delete": "Aufgabenbeziehung entfernen", "deleteText1": "Willst du diese Aufgabenbeziehung wirklich entfernen?", "deleteText2": "Dies kann nicht rückgängig gemacht werden!", - "select": "Select a relation kind", + "select": "Beziehungsart auswählen", "kinds": { - "subtask": "Subtask | Subtasks", - "parenttask": "Parent Task | Parent Tasks", - "related": "Related Task | Related Tasks", - "duplicateof": "Duplicate Of | Duplicates Of", - "duplicates": "Duplicates | Duplicates", - "blocking": "Blocking | Blocking", - "blocked": "Blocked By | Blocked By", - "precedes": "Precedes | Precedes", - "follows": "Follows | Follows", - "copiedfrom": "Copied From | Copied From", - "copiedto": "Copied To | Copied To" + "subtask": "Unteraufgabe | Unteraufgaben", + "parenttask": "Übergeordnete Aufgabe | Übergeordnete Aufgaben", + "related": "Zugehörige Aufgabe | Zugehörige Aufgaben", + "duplicateof": "Duplikat von | Duplikate von", + "duplicates": "Dupliziert | Duplizierte", + "blocking": "Blockiert | Blockiert", + "blocked": "Blockiert von | Blockiert von", + "precedes": "Vorgänger | Vorgänger", + "follows": "Folgt | Folgen", + "copiedfrom": "Kopiert von | Kopiert von", + "copiedto": "Kopiert nach | Kopiert nach" } }, "repeat": { diff --git a/src/i18n/lang/de-swiss.json b/src/i18n/lang/de-swiss.json index 78108316..e2d38e32 100644 --- a/src/i18n/lang/de-swiss.json +++ b/src/i18n/lang/de-swiss.json @@ -128,7 +128,7 @@ } }, "list": { - "archived": "This list is archived. It is not possible to create new or edit tasks for it.", + "archived": "Diese Liste ist archiviert. Es ist nicht möglich, neue Aufgaben zu erstellen oder sie zu bearbeiten.", "title": "Liste Titl", "color": "Farb", "lists": "Listene", @@ -626,7 +626,7 @@ "addSuccess": "Das Label isch erfolgriich hinzuegfüegt worde.", "createSuccess": "Das Label isch erfolgriich erstellt worde.", "removeSuccess": "Das Label isch erfolgriich glöscht.", - "addCreateSuccess": "The label has been created and added successfully." + "addCreateSuccess": "Das Label wurde erfolgreich erstellt und hinzugefügt." }, "priority": { "unset": "Nid iihgstellt", @@ -646,19 +646,19 @@ "delete": "Uufgabe Beziehig chüble", "deleteText1": "Bisch du dir sicher, dass du die Zueghörigkeit chüblä wetsch?", "deleteText2": "Das chan nid rückgängig gmacht werde!", - "select": "Select a relation kind", + "select": "Beziehungsart auswählen", "kinds": { - "subtask": "Subtask | Subtasks", - "parenttask": "Parent Task | Parent Tasks", - "related": "Related Task | Related Tasks", - "duplicateof": "Duplicate Of | Duplicates Of", - "duplicates": "Duplicates | Duplicates", - "blocking": "Blocking | Blocking", - "blocked": "Blocked By | Blocked By", - "precedes": "Precedes | Precedes", - "follows": "Follows | Follows", - "copiedfrom": "Copied From | Copied From", - "copiedto": "Copied To | Copied To" + "subtask": "Unteraufgabe | Unteraufgaben", + "parenttask": "Übergeordnete Aufgabe | Übergeordnete Aufgaben", + "related": "Zugehörige Aufgabe | Zugehörige Aufgaben", + "duplicateof": "Duplikat von | Duplikate von", + "duplicates": "Dupliziert | Duplizierte", + "blocking": "Blockiert | Blockiert", + "blocked": "Blockiert von | Blockiert von", + "precedes": "Vorgänger | Vorgänger", + "follows": "Folgt | Folgen", + "copiedfrom": "Kopiert von | Kopiert von", + "copiedto": "Kopiert nach | Kopiert nach" } }, "repeat": { @@ -720,13 +720,13 @@ "delete": { "header": "Das Team chüble", "text1": "Bischder sicher, dasst wetsch da Team mit allne Mitglieder lösche?", - "text2": "Alli Teammitglider werded ihren Zuegang zu de Liste und Namensrüüm wo mit dem Team verbunde sind verlüüre. Das CHAN NID RÜCKGÄNIG gmacht werde!", + "text2": "All team members will lose access to lists and namespaces shared with this team. This CANNOT BE UNDONE!", "success": "Da Team isch erfolgriich g'chüblet wore." }, "deleteUser": { "header": "Benutzer usem Team entferne", "text1": "Bisch du dir sicher, dass du de Benutzer usm Team werfe wetsch?", - "text2": "Er wird Zuegriff uf alli Liste und Namensrüüm verlüüre, wo da Team zuegriff druff het. Das CHAN NID RÜCKGÄNIG GMACHT WERDE!", + "text2": "They will lose access to all lists and namespaces this team has access to. This CANNOT BE UNDONE!", "success": "Benutzer erfolgriich usegworfe." } }, diff --git a/src/i18n/lang/en.json b/src/i18n/lang/en.json index 40910b86..1e14b333 100644 --- a/src/i18n/lang/en.json +++ b/src/i18n/lang/en.json @@ -720,13 +720,13 @@ "delete": { "header": "Delete the team", "text1": "Are you sure you want to delete this team and all of its members?", - "text2": "All team members will loose access to lists and namespaces shared with this team. This CANNOT BE UNDONE!", + "text2": "All team members will lose access to lists and namespaces shared with this team. This CANNOT BE UNDONE!", "success": "The team was successfully deleted." }, "deleteUser": { "header": "Remove a user from the team", "text1": "Are you sure you want to remove this user from the team?", - "text2": "They will loose access to all lists and namespaces this team has access to. This CANNOT BE UNDONE!", + "text2": "They will lose access to all lists and namespaces this team has access to. This CANNOT BE UNDONE!", "success": "The user was successfully deleted from the team." } }, diff --git a/src/i18n/lang/fr-FR.json b/src/i18n/lang/fr-FR.json index c800080a..c40ecfca 100644 --- a/src/i18n/lang/fr-FR.json +++ b/src/i18n/lang/fr-FR.json @@ -626,7 +626,7 @@ "addSuccess": "Étiquette ajoutée.", "createSuccess": "Étiquette créée.", "removeSuccess": "Étiquette retirée.", - "addCreateSuccess": "The label has been created and added successfully." + "addCreateSuccess": "L'étiquette a bien été créée et ajoutée." }, "priority": { "unset": "Non définie", @@ -646,19 +646,19 @@ "delete": "Supprimer la relation de tâche", "deleteText1": "Supprimer cette relation de tâche ?", "deleteText2": "Ceci ne peut pas être annulé !", - "select": "Select a relation kind", + "select": "Sélectionnez un genre de relation", "kinds": { - "subtask": "Subtask | Subtasks", - "parenttask": "Parent Task | Parent Tasks", - "related": "Related Task | Related Tasks", - "duplicateof": "Duplicate Of | Duplicates Of", - "duplicates": "Duplicates | Duplicates", - "blocking": "Blocking | Blocking", - "blocked": "Blocked By | Blocked By", - "precedes": "Precedes | Precedes", - "follows": "Follows | Follows", - "copiedfrom": "Copied From | Copied From", - "copiedto": "Copied To | Copied To" + "subtask": "Sous-tâche | Sous-tâches", + "parenttask": "Tâche parente | Tâches parentes", + "related": "Tâche connexe | Tâches connexes", + "duplicateof": "Doublon de | Doublons de", + "duplicates": "Doublon | Doublons", + "blocking": "Blocage | Blocages", + "blocked": "Bloqué par | Bloqués par", + "precedes": "Précède | Précède", + "follows": "Suit | Suit", + "copiedfrom": "Copié depuis | Copiés depuis", + "copiedto": "Copié vers | Copiés vers" } }, "repeat": { diff --git a/src/styles/components/list.scss b/src/styles/components/list.scss index 825a4cac..a9e83ce9 100644 --- a/src/styles/components/list.scss +++ b/src/styles/components/list.scss @@ -291,7 +291,11 @@ $list-spacing: 1rem; } .list-cards-wrapper-2-rows { - flex-wrap: wrap; - max-height: calc(#{$list-height * 2} + #{$list-spacing * 2}); - overflow: hidden; + flex-wrap: wrap; + max-height: calc(#{$list-height * 2} + #{$list-spacing * 2} - 4px); + overflow: hidden; + + @media screen and (max-width: $mobile) { + max-height: calc(#{$list-height * 4} + #{$list-spacing * 4} - 4px); + } } diff --git a/src/styles/theme/navigation.scss b/src/styles/theme/navigation.scss index 1e06b22a..67f588e3 100644 --- a/src/styles/theme/navigation.scss +++ b/src/styles/theme/navigation.scss @@ -54,7 +54,7 @@ line-height: 1; .button { - padding: 0 0.25rem; + padding: 0 .5rem; height: 1rem; .icon { diff --git a/src/styles/variables/variables.scss b/src/styles/variables/variables.scss index dbc7532b..33f34407 100644 --- a/src/styles/variables/variables.scss +++ b/src/styles/variables/variables.scss @@ -38,9 +38,9 @@ $scrollbar-hover-color: $grey-500; $button-height: 34px; -$switch-view-height: 43px; +$switch-view-height: 2.69rem; -$user-dropdown-width-mobile: 4rem; +$user-dropdown-width-mobile: 5rem; $hamburger-menu-icon-spacing: 1rem; $hamburger-menu-icon-width: 28px; $navbar-height: 4rem; diff --git a/src/views/labels/ListLabels.vue b/src/views/labels/ListLabels.vue index b1bd5993..a7a2b9d4 100644 --- a/src/views/labels/ListLabels.vue +++ b/src/views/labels/ListLabels.vue @@ -163,6 +163,9 @@ export default { // object passed to this function here still has a reference to the store. this.labelEditLabel = new LabelModel({ ...label, + // The model does not support passing dates into it directly so we need to convert them first + created: +label.created, + updated: +label.updated, }) this.isLabelEdit = true diff --git a/src/views/user/Login.vue b/src/views/user/Login.vue index fcb79cad..59464aad 100644 --- a/src/views/user/Login.vue +++ b/src/views/user/Login.vue @@ -104,13 +104,13 @@