Merge branch 'main' into vue3

This commit is contained in:
Dominik Pschenitschni 2021-10-15 20:43:11 +02:00
commit 8e1ab8e09b
No known key found for this signature in database
GPG key ID: B257AC0149F43A77
16 changed files with 126 additions and 80 deletions

View file

@ -80,25 +80,13 @@ steps:
depends_on: depends_on:
- dependencies - 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 - name: build-prod
image: node:16 image: node:16
pull: true pull: true
environment: environment:
YARN_CACHE_FOLDER: .cache/yarn/ YARN_CACHE_FOLDER: .cache/yarn/
commands: commands:
- yarn build --dest dist-prod - yarn build
depends_on: depends_on:
- dependencies - dependencies
@ -120,12 +108,12 @@ steps:
CYPRESS_CACHE_FOLDER: .cache/cypress/ CYPRESS_CACHE_FOLDER: .cache/cypress/
CYPRESS_DEFAULT_COMMAND_TIMEOUT: 60000 CYPRESS_DEFAULT_COMMAND_TIMEOUT: 60000
commands: commands:
- sed -i 's/localhost/api/g' dist-dev/index.html - sed -i 's/localhost/api/g' dist/index.html
- yarn serve:dist-dev & npx wait-on http://localhost:5000 - yarn serve:dist & npx wait-on http://localhost:5000
- yarn test:frontend --browser chrome - yarn test:frontend --browser chrome
depends_on: depends_on:
- dependencies - dependencies
- build-dev - build-prod
- name: upload-test-results - name: upload-test-results
image: plugins/s3 image: plugins/s3

View file

@ -2,3 +2,10 @@
import './commands' import './commands'
import 'cypress-file-upload' import 'cypress-file-upload'
import '@4tw/cypress-drag-drop' 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
})

View file

@ -19,6 +19,7 @@
import {mapState} from 'vuex' import {mapState} from 'vuex'
import logoUrl from '@/assets/logo-full.svg' import logoUrl from '@/assets/logo-full.svg'
import { saveLastVisited } from '@/helpers/saveLastVisited'
export default { export default {
name: 'contentNoAuth', name: 'contentNoAuth',
@ -57,6 +58,7 @@ export default {
localStorage.getItem('passwordResetToken') === null && localStorage.getItem('passwordResetToken') === null &&
localStorage.getItem('emailConfirmToken') === null localStorage.getItem('emailConfirmToken') === null
) { ) {
saveLastVisited(this.$route.name, this.$route.params)
this.$router.push({name: 'user.login'}) this.$router.push({name: 'user.login'})
} }
}, },

View file

@ -10,7 +10,7 @@
v-focus v-focus
v-model="newTaskTitle" v-model="newTaskTitle"
ref="newTaskInput" ref="newTaskInput"
:style="{'height': `${textAreaHeight}px`}" :style="{'height': `calc(${textAreaHeight}px - 2px + 1rem)`}"
@keyup="errorMessage = ''" @keyup="errorMessage = ''"
@keydown.enter="handleEnter" @keydown.enter="handleEnter"
/> />
@ -40,7 +40,8 @@
import TaskService from '../../services/task' import TaskService from '../../services/task'
import QuickAddMagic from '@/components/tasks/partials/quick-add-magic.vue' 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 => { const cleanupTitle = title => {
return title.replace(/^((\* |\+ |- )(\[ \] )?)/g, '') return title.replace(/^((\* |\+ |- )(\[ \] )?)/g, '')
@ -54,7 +55,8 @@ export default {
newTaskTitle: '', newTaskTitle: '',
taskService: new TaskService(), taskService: new TaskService(),
errorMessage: '', errorMessage: '',
textAreaHeight: INITIAL_SCROLL_HEIGHT, textAreaHeight: null,
initialTextAreaHeight: null,
} }
}, },
components: { components: {
@ -68,14 +70,17 @@ export default {
}, },
watch: { watch: {
newTaskTitle(newVal) { newTaskTitle(newVal) {
let scrollHeight = this.$refs.newTaskInput.scrollHeight // Calculating the textarea height based on lines of input in it. That is more reliable when removing a
if (scrollHeight < INITIAL_SCROLL_HEIGHT || newVal === '') { // line from the input.
scrollHeight = INITIAL_SCROLL_HEIGHT 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: { methods: {
addTask() { addTask() {
if (this.newTaskTitle === '') { if (this.newTaskTitle === '') {

View file

@ -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)
}

View file

@ -128,7 +128,7 @@
} }
}, },
"list": { "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", "title": "Listentitel",
"color": "Farbe", "color": "Farbe",
"lists": "Listen", "lists": "Listen",
@ -626,7 +626,7 @@
"addSuccess": "Das Label wurde erfolgreich hinzugefügt.", "addSuccess": "Das Label wurde erfolgreich hinzugefügt.",
"createSuccess": "Das Label wurde erfolgreich erstellt.", "createSuccess": "Das Label wurde erfolgreich erstellt.",
"removeSuccess": "Das Label wurde erfolgreich entfernt.", "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": { "priority": {
"unset": "Nicht eingestellt", "unset": "Nicht eingestellt",
@ -646,19 +646,19 @@
"delete": "Aufgabenbeziehung entfernen", "delete": "Aufgabenbeziehung entfernen",
"deleteText1": "Willst du diese Aufgabenbeziehung wirklich entfernen?", "deleteText1": "Willst du diese Aufgabenbeziehung wirklich entfernen?",
"deleteText2": "Dies kann nicht rückgängig gemacht werden!", "deleteText2": "Dies kann nicht rückgängig gemacht werden!",
"select": "Select a relation kind", "select": "Beziehungsart auswählen",
"kinds": { "kinds": {
"subtask": "Subtask | Subtasks", "subtask": "Unteraufgabe | Unteraufgaben",
"parenttask": "Parent Task | Parent Tasks", "parenttask": "Übergeordnete Aufgabe | Übergeordnete Aufgaben",
"related": "Related Task | Related Tasks", "related": "Zugehörige Aufgabe | Zugehörige Aufgaben",
"duplicateof": "Duplicate Of | Duplicates Of", "duplicateof": "Duplikat von | Duplikate von",
"duplicates": "Duplicates | Duplicates", "duplicates": "Dupliziert | Duplizierte",
"blocking": "Blocking | Blocking", "blocking": "Blockiert | Blockiert",
"blocked": "Blocked By | Blocked By", "blocked": "Blockiert von | Blockiert von",
"precedes": "Precedes | Precedes", "precedes": "Vorgänger | Vorgänger",
"follows": "Follows | Follows", "follows": "Folgt | Folgen",
"copiedfrom": "Copied From | Copied From", "copiedfrom": "Kopiert von | Kopiert von",
"copiedto": "Copied To | Copied To" "copiedto": "Kopiert nach | Kopiert nach"
} }
}, },
"repeat": { "repeat": {

View file

@ -128,7 +128,7 @@
} }
}, },
"list": { "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", "title": "Liste Titl",
"color": "Farb", "color": "Farb",
"lists": "Listene", "lists": "Listene",
@ -626,7 +626,7 @@
"addSuccess": "Das Label isch erfolgriich hinzuegfüegt worde.", "addSuccess": "Das Label isch erfolgriich hinzuegfüegt worde.",
"createSuccess": "Das Label isch erfolgriich erstellt worde.", "createSuccess": "Das Label isch erfolgriich erstellt worde.",
"removeSuccess": "Das Label isch erfolgriich glöscht.", "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": { "priority": {
"unset": "Nid iihgstellt", "unset": "Nid iihgstellt",
@ -646,19 +646,19 @@
"delete": "Uufgabe Beziehig chüble", "delete": "Uufgabe Beziehig chüble",
"deleteText1": "Bisch du dir sicher, dass du die Zueghörigkeit chüblä wetsch?", "deleteText1": "Bisch du dir sicher, dass du die Zueghörigkeit chüblä wetsch?",
"deleteText2": "Das chan nid rückgängig gmacht werde!", "deleteText2": "Das chan nid rückgängig gmacht werde!",
"select": "Select a relation kind", "select": "Beziehungsart auswählen",
"kinds": { "kinds": {
"subtask": "Subtask | Subtasks", "subtask": "Unteraufgabe | Unteraufgaben",
"parenttask": "Parent Task | Parent Tasks", "parenttask": "Übergeordnete Aufgabe | Übergeordnete Aufgaben",
"related": "Related Task | Related Tasks", "related": "Zugehörige Aufgabe | Zugehörige Aufgaben",
"duplicateof": "Duplicate Of | Duplicates Of", "duplicateof": "Duplikat von | Duplikate von",
"duplicates": "Duplicates | Duplicates", "duplicates": "Dupliziert | Duplizierte",
"blocking": "Blocking | Blocking", "blocking": "Blockiert | Blockiert",
"blocked": "Blocked By | Blocked By", "blocked": "Blockiert von | Blockiert von",
"precedes": "Precedes | Precedes", "precedes": "Vorgänger | Vorgänger",
"follows": "Follows | Follows", "follows": "Folgt | Folgen",
"copiedfrom": "Copied From | Copied From", "copiedfrom": "Kopiert von | Kopiert von",
"copiedto": "Copied To | Copied To" "copiedto": "Kopiert nach | Kopiert nach"
} }
}, },
"repeat": { "repeat": {
@ -720,13 +720,13 @@
"delete": { "delete": {
"header": "Das Team chüble", "header": "Das Team chüble",
"text1": "Bischder sicher, dasst wetsch da Team mit allne Mitglieder lösche?", "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." "success": "Da Team isch erfolgriich g'chüblet wore."
}, },
"deleteUser": { "deleteUser": {
"header": "Benutzer usem Team entferne", "header": "Benutzer usem Team entferne",
"text1": "Bisch du dir sicher, dass du de Benutzer usm Team werfe wetsch?", "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." "success": "Benutzer erfolgriich usegworfe."
} }
}, },

View file

@ -720,13 +720,13 @@
"delete": { "delete": {
"header": "Delete the team", "header": "Delete the team",
"text1": "Are you sure you want to delete this team and all of its members?", "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." "success": "The team was successfully deleted."
}, },
"deleteUser": { "deleteUser": {
"header": "Remove a user from the team", "header": "Remove a user from the team",
"text1": "Are you sure you want to remove this 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." "success": "The user was successfully deleted from the team."
} }
}, },

View file

@ -626,7 +626,7 @@
"addSuccess": "Étiquette ajoutée.", "addSuccess": "Étiquette ajoutée.",
"createSuccess": "Étiquette créée.", "createSuccess": "Étiquette créée.",
"removeSuccess": "Étiquette retiré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": { "priority": {
"unset": "Non définie", "unset": "Non définie",
@ -646,19 +646,19 @@
"delete": "Supprimer la relation de tâche", "delete": "Supprimer la relation de tâche",
"deleteText1": "Supprimer cette relation de tâche ?", "deleteText1": "Supprimer cette relation de tâche ?",
"deleteText2": "Ceci ne peut pas être annulé !", "deleteText2": "Ceci ne peut pas être annulé !",
"select": "Select a relation kind", "select": "Sélectionnez un genre de relation",
"kinds": { "kinds": {
"subtask": "Subtask | Subtasks", "subtask": "Sous-tâche | Sous-tâches",
"parenttask": "Parent Task | Parent Tasks", "parenttask": "Tâche parente | Tâches parentes",
"related": "Related Task | Related Tasks", "related": "Tâche connexe | Tâches connexes",
"duplicateof": "Duplicate Of | Duplicates Of", "duplicateof": "Doublon de | Doublons de",
"duplicates": "Duplicates | Duplicates", "duplicates": "Doublon | Doublons",
"blocking": "Blocking | Blocking", "blocking": "Blocage | Blocages",
"blocked": "Blocked By | Blocked By", "blocked": "Bloqué par | Bloqués par",
"precedes": "Precedes | Precedes", "precedes": "Précède | Précède",
"follows": "Follows | Follows", "follows": "Suit | Suit",
"copiedfrom": "Copied From | Copied From", "copiedfrom": "Copié depuis | Copiés depuis",
"copiedto": "Copied To | Copied To" "copiedto": "Copié vers | Copiés vers"
} }
}, },
"repeat": { "repeat": {

View file

@ -292,6 +292,10 @@ $list-spacing: 1rem;
.list-cards-wrapper-2-rows { .list-cards-wrapper-2-rows {
flex-wrap: wrap; flex-wrap: wrap;
max-height: calc(#{$list-height * 2} + #{$list-spacing * 2}); max-height: calc(#{$list-height * 2} + #{$list-spacing * 2} - 4px);
overflow: hidden; overflow: hidden;
@media screen and (max-width: $mobile) {
max-height: calc(#{$list-height * 4} + #{$list-spacing * 4} - 4px);
}
} }

View file

@ -54,7 +54,7 @@
line-height: 1; line-height: 1;
.button { .button {
padding: 0 0.25rem; padding: 0 .5rem;
height: 1rem; height: 1rem;
.icon { .icon {

View file

@ -38,9 +38,9 @@ $scrollbar-hover-color: $grey-500;
$button-height: 34px; $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-spacing: 1rem;
$hamburger-menu-icon-width: 28px; $hamburger-menu-icon-width: 28px;
$navbar-height: 4rem; $navbar-height: 4rem;

View file

@ -163,6 +163,9 @@ export default {
// object passed to this function here still has a reference to the store. // object passed to this function here still has a reference to the store.
this.labelEditLabel = new LabelModel({ this.labelEditLabel = new LabelModel({
...label, ...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 this.isLabelEdit = true

View file

@ -104,13 +104,13 @@
<script> <script>
import {mapState} from 'vuex' import {mapState} from 'vuex'
import router from '../../router'
import {HTTPFactory} from '@/http-common' import {HTTPFactory} from '@/http-common'
import {ERROR_MESSAGE, LOADING} from '@/store/mutation-types' import {ERROR_MESSAGE, LOADING} from '@/store/mutation-types'
import legal from '../../components/misc/legal' import legal from '../../components/misc/legal'
import ApiConfig from '@/components/misc/api-config.vue' import ApiConfig from '@/components/misc/api-config.vue'
import {getErrorText} from '@/message' import {getErrorText} from '@/message'
import {redirectToProvider} from '../../helpers/redirectToProvider' import {redirectToProvider} from '../../helpers/redirectToProvider'
import {getLastVisited, clearLastVisited} from '../../helpers/saveLastVisited'
export default { export default {
components: { components: {
@ -142,9 +142,18 @@ export default {
}) })
} }
// Check if the user is already logged in, if so, redirect him to the homepage // Check if the user is already logged in, if so, redirect them to the homepage
if (this.authenticated) { if (this.authenticated) {
router.push({name: 'home'}) const last = getLastVisited()
if (last !== null) {
this.$router.push({
name: last.name,
params: last.params,
})
clearLastVisited()
} else {
this.$router.push({name: 'home'})
}
} }
}, },
created() { created() {

View file

@ -14,6 +14,7 @@ import {mapState} from 'vuex'
import {ERROR_MESSAGE, LOADING} from '@/store/mutation-types' import {ERROR_MESSAGE, LOADING} from '@/store/mutation-types'
import {getErrorText} from '@/message' import {getErrorText} from '@/message'
import {clearLastVisited, getLastVisited} from '../../helpers/saveLastVisited'
export default { export default {
name: 'Auth', name: 'Auth',
@ -63,7 +64,16 @@ export default {
code: this.$route.query.code, code: this.$route.query.code,
}) })
.then(() => { .then(() => {
const last = getLastVisited()
if (last !== null) {
this.$router.push({
name: last.name,
params: last.params,
})
clearLastVisited()
} else {
this.$router.push({name: 'home'}) this.$router.push({name: 'home'})
}
}) })
.catch(e => { .catch(e => {
const err = getErrorText(e) const err = getErrorText(e)

View file

@ -116,7 +116,7 @@ export default {
} }
}, },
beforeMount() { beforeMount() {
// Check if the user is already logged in, if so, redirect him to the homepage // Check if the user is already logged in, if so, redirect them to the homepage
if (this.authenticated) { if (this.authenticated) {
router.push({name: 'home'}) router.push({name: 'home'})
} }