Merge branch 'main' into feature/ganttastic

# Conflicts:
#	pnpm-lock.yaml
This commit is contained in:
Dominik Pschenitschni 2022-09-28 18:00:32 +02:00
commit a1e280e47b
No known key found for this signature in database
GPG key ID: B257AC0149F43A77
15 changed files with 364 additions and 312 deletions

View file

@ -46,7 +46,7 @@ steps:
PNPM_CACHE_FOLDER: .cache/pnpm PNPM_CACHE_FOLDER: .cache/pnpm
CYPRESS_CACHE_FOLDER: .cache/cypress CYPRESS_CACHE_FOLDER: .cache/cypress
commands: commands:
- corepack enable && corepack prepare pnpm@7.9.3 --activate && pnpm config set store-dir .cache/pnpm - corepack enable && pnpm config set store-dir .cache/pnpm
- pnpm install --fetch-timeout 100000 - pnpm install --fetch-timeout 100000
# depends_on: # depends_on:
# - restore-cache # - restore-cache
@ -57,7 +57,7 @@ steps:
environment: environment:
PNPM_CACHE_FOLDER: .cache/pnpm PNPM_CACHE_FOLDER: .cache/pnpm
commands: commands:
- corepack enable && corepack prepare pnpm@7.9.3 --activate && pnpm config set store-dir .cache/pnpm - corepack enable && pnpm config set store-dir .cache/pnpm
- pnpm run lint - pnpm run lint
depends_on: depends_on:
- dependencies - dependencies
@ -68,7 +68,7 @@ steps:
environment: environment:
PNPM_CACHE_FOLDER: .cache/pnpm PNPM_CACHE_FOLDER: .cache/pnpm
commands: commands:
- corepack enable && corepack prepare pnpm@7.9.3 --activate && pnpm config set store-dir .cache/pnpm - corepack enable && pnpm config set store-dir .cache/pnpm
- pnpm run build - pnpm run build
depends_on: depends_on:
- dependencies - dependencies
@ -77,7 +77,7 @@ steps:
image: node:18-alpine image: node:18-alpine
pull: true pull: true
commands: commands:
- corepack enable && corepack prepare pnpm@7.9.3 --activate && pnpm config set store-dir .cache/pnpm - corepack enable && pnpm config set store-dir .cache/pnpm
- pnpm run test:unit - pnpm run test:unit
depends_on: depends_on:
- dependencies - dependencies
@ -89,7 +89,7 @@ steps:
environment: environment:
PNPM_CACHE_FOLDER: .cache/pnpm PNPM_CACHE_FOLDER: .cache/pnpm
commands: commands:
- corepack enable && corepack prepare pnpm@7.9.3 --activate && pnpm config set store-dir .cache/pnpm - corepack enable && pnpm config set store-dir .cache/pnpm
- pnpm run typecheck - pnpm run typecheck
depends_on: depends_on:
- dependencies - dependencies
@ -107,7 +107,7 @@ steps:
from_secret: cypress_project_key from_secret: cypress_project_key
commands: commands:
- sed -i 's/localhost/api/g' dist/index.html - sed -i 's/localhost/api/g' dist/index.html
- corepack enable && corepack prepare pnpm@7.9.3 --activate && pnpm config set store-dir .cache/pnpm - corepack enable && pnpm config set store-dir .cache/pnpm
- pnpm cypress install - pnpm cypress install
- pnpm run serve:dist & npx wait-on http://localhost:4173 - pnpm run serve:dist & npx wait-on http://localhost:4173
- pnpm run test:frontend --browser chrome --record - pnpm run test:frontend --browser chrome --record
@ -202,7 +202,7 @@ steps:
environment: environment:
PNPM_CACHE_FOLDER: .cache/pnpm PNPM_CACHE_FOLDER: .cache/pnpm
commands: commands:
- corepack enable && corepack prepare pnpm@7.9.3 --activate && pnpm config set store-dir .cache/.pnp - corepack enable && pnpm config set store-dir .cache/.pnp
- pnpm install --fetch-timeout 100000 - pnpm install --fetch-timeout 100000
- pnpm run lint - pnpm run lint
- "echo '{\"VERSION\": \"'$(git describe --tags --always --abbrev=10 | sed 's/-/+/' | sed 's/^v//' | sed 's/-g/-/')'\"}' > src/version.json" - "echo '{\"VERSION\": \"'$(git describe --tags --always --abbrev=10 | sed 's/-/+/' | sed 's/^v//' | sed 's/-g/-/')'\"}' > src/version.json"
@ -278,7 +278,7 @@ steps:
environment: environment:
PNPM_CACHE_FOLDER: .cache/pnpm PNPM_CACHE_FOLDER: .cache/pnpm
commands: commands:
- corepack enable && corepack prepare pnpm@7.9.3 --activate && pnpm config set store-dir .cache/pnpm - corepack enable && pnpm config set store-dir .cache/pnpm
- pnpm install --fetch-timeout 100000 - pnpm install --fetch-timeout 100000
- pnpm run lint - pnpm run lint
- "echo '{\"VERSION\": \"'$(git describe --tags --always --abbrev=10 | sed 's/-/+/' | sed 's/^v//' | sed 's/-g/-/')'\"}' > src/version.json" - "echo '{\"VERSION\": \"'$(git describe --tags --always --abbrev=10 | sed 's/-/+/' | sed 's/^v//' | sed 's/-g/-/')'\"}' > src/version.json"
@ -659,6 +659,6 @@ steps:
from_secret: crowdin_key from_secret: crowdin_key
--- ---
kind: signature kind: signature
hmac: 9eccb4a9999236be0f772c4cbe7ccce2875faa57f32de179bd75cf0df4124d00 hmac: c885a0e50db729842402494aa645dd3ac662828b691108550f6bf302158295ba
... ...

View file

@ -21,16 +21,17 @@ COPY pnpm-lock.yaml ./
RUN \ RUN \
# https://pnpm.io/installation#using-corepack # https://pnpm.io/installation#using-corepack
corepack enable && \ corepack enable && \
corepack prepare pnpm@7.9.3 --activate && \ # we don't use corepack prepare here by intend since
# we have renovate to keep our dependencies up to date
# Build the frontend # Build the frontend
pnpm fetch pnpm fetch --prod
ADD . ./ ADD . ./
RUN apk add --no-cache git RUN apk add --no-cache git
RUN \ RUN \
pnpm install --offline && \ pnpm install -r --offline --prod && \
echo '{"VERSION": "'$(git describe --tags --always --abbrev=10 | sed 's/-/+/' | sed 's/^v//' | sed 's/-g/-/')'"}' > src/version.json && \ echo '{"VERSION": "'$(git describe --tags --always --abbrev=10 | sed 's/-/+/' | sed 's/^v//' | sed 's/-g/-/')'"}' > src/version.json && \
pnpm run build pnpm run build

View file

@ -1,10 +1,10 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<title>Vikunja</title>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0"> <meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Vikunja</title>
<meta name="description" content="Vikunja (/vɪˈkuːnjə/) - The to-do app to organize your life."> <meta name="description" content="Vikunja (/vɪˈkuːnjə/) - The to-do app to organize your life.">
<meta name="theme-color" content="#1973ff"/> <meta name="theme-color" content="#1973ff"/>

View file

@ -25,15 +25,15 @@
"@github/hotkey": "2.0.1", "@github/hotkey": "2.0.1",
"@infectoone/vue-ganttastic": "^2.0.4", "@infectoone/vue-ganttastic": "^2.0.4",
"@kyvg/vue3-notification": "2.4.1", "@kyvg/vue3-notification": "2.4.1",
"@sentry/tracing": "7.13.0", "@sentry/tracing": "7.14.0",
"@sentry/vue": "7.13.0", "@sentry/vue": "7.14.0",
"@types/is-touch-device": "1.0.0", "@types/is-touch-device": "1.0.0",
"@types/lodash.clonedeep": "4.5.7", "@types/lodash.clonedeep": "4.5.7",
"@types/sortablejs": "1.15.0", "@types/sortablejs": "1.15.0",
"@vueuse/core": "9.3.0", "@vueuse/core": "9.3.0",
"@vueuse/router": "9.3.0", "@vueuse/router": "9.3.0",
"axios": "0.27.2", "axios": "0.27.2",
"blurhash": "2.0.1", "blurhash": "2.0.2",
"bulma-css-variables": "0.9.33", "bulma-css-variables": "0.9.33",
"camel-case": "4.1.2", "camel-case": "4.1.2",
"codemirror": "5.65.9", "codemirror": "5.65.9",
@ -54,7 +54,7 @@
"sortablejs": "1.15.0", "sortablejs": "1.15.0",
"ufo": "0.8.5", "ufo": "0.8.5",
"v-tooltip": "4.0.0-beta.17", "v-tooltip": "4.0.0-beta.17",
"vue": "3.2.39", "vue": "3.2.40",
"vue-advanced-cropper": "2.8.3", "vue-advanced-cropper": "2.8.3",
"vue-flatpickr-component": "9.0.6", "vue-flatpickr-component": "9.0.6",
"vue-i18n": "9.2.2", "vue-i18n": "9.2.2",
@ -70,6 +70,7 @@
"@faker-js/faker": "7.5.0", "@faker-js/faker": "7.5.0",
"@types/dompurify": "2.3.4", "@types/dompurify": "2.3.4",
"@types/flexsearch": "0.7.3", "@types/flexsearch": "0.7.3",
"@types/node": "16.11.62",
"@typescript-eslint/eslint-plugin": "5.38.1", "@typescript-eslint/eslint-plugin": "5.38.1",
"@typescript-eslint/parser": "5.38.1", "@typescript-eslint/parser": "5.38.1",
"@vitejs/plugin-legacy": "2.2.0", "@vitejs/plugin-legacy": "2.2.0",
@ -80,7 +81,7 @@
"autoprefixer": "10.4.12", "autoprefixer": "10.4.12",
"browserslist": "4.21.4", "browserslist": "4.21.4",
"caniuse-lite": "1.0.30001412", "caniuse-lite": "1.0.30001412",
"cypress": "10.8.0", "cypress": "10.9.0",
"esbuild": "0.15.9", "esbuild": "0.15.9",
"eslint": "8.24.0", "eslint": "8.24.0",
"eslint-plugin-vue": "9.5.1", "eslint-plugin-vue": "9.5.1",
@ -92,8 +93,8 @@
"rollup": "2.79.1", "rollup": "2.79.1",
"rollup-plugin-visualizer": "5.8.2", "rollup-plugin-visualizer": "5.8.2",
"sass": "1.55.0", "sass": "1.55.0",
"typescript": "4.8.3", "typescript": "4.8.4",
"vite": "3.1.3", "vite": "3.1.4",
"vite-plugin-pwa": "0.13.1", "vite-plugin-pwa": "0.13.1",
"vite-svg-loader": "3.6.0", "vite-svg-loader": "3.6.0",
"vitest": "0.23.4", "vitest": "0.23.4",

File diff suppressed because it is too large Load diff

View file

@ -6,13 +6,13 @@
{{ $t('input.datemathHelp.intro') }} {{ $t('input.datemathHelp.intro') }}
</p> </p>
<p> <p>
<i18n-t keypath="input.datemathHelp.expression"> <i18n-t keypath="input.datemathHelp.expression" scope="global">
<code>now</code> <code>now</code>
<code>||</code> <code>||</code>
</i18n-t> </i18n-t>
</p> </p>
<p> <p>
<i18n-t keypath="input.datemathHelp.similar"> <i18n-t keypath="input.datemathHelp.similar" scope="global">
<BaseButton <BaseButton
href="https://grafana.com/docs/grafana/latest/dashboards/time-range-controls/" href="https://grafana.com/docs/grafana/latest/dashboards/time-range-controls/"
target="_blank"> target="_blank">
@ -99,7 +99,7 @@
<tr> <tr>
<td><code>{{ exampleDate }}||+1M/d</code></td> <td><code>{{ exampleDate }}||+1M/d</code></td>
<td> <td>
<i18n-t keypath="input.datemathHelp.examples.datePlusMonth"> <i18n-t keypath="input.datemathHelp.examples.datePlusMonth" scope="global">
<code>{{ exampleDate }}</code> <code>{{ exampleDate }}</code>
</i18n-t> </i18n-t>
</td> </td>

View file

@ -23,7 +23,7 @@
</div> </div>
</div> </div>
<div class="api-url-info" v-else> <div class="api-url-info" v-else>
<i18n-t keypath="apiConfig.use"> <i18n-t keypath="apiConfig.use" scope="global">
<span class="url" v-tooltip="apiUrl"> {{ apiDomain }} </span> <span class="url" v-tooltip="apiUrl"> {{ apiDomain }} </span>
</i18n-t> </i18n-t>
<br/> <br/>

View file

@ -1,6 +1,6 @@
<template> <template>
<message variant="danger"> <message variant="danger">
<i18n-t keypath="loadingError.failed"> <i18n-t keypath="loadingError.failed" scope="global">
<ButtonLink @click="reload">{{ $t('loadingError.tryAgain') }}</ButtonLink> <ButtonLink @click="reload">{{ $t('loadingError.tryAgain') }}</ButtonLink>
<ButtonLink href="https://vikunja.io/contact/">{{ $t('loadingError.contact') }}</ButtonLink> <ButtonLink href="https://vikunja.io/contact/">{{ $t('loadingError.contact') }}</ButtonLink>
</i18n-t> </i18n-t>

View file

@ -92,7 +92,7 @@
</p> </p>
<p class="mb-2"> <p class="mb-2">
<i18n-t keypath="list.share.links.sharedBy"> <i18n-t keypath="list.share.links.sharedBy" scope="global">
<strong>{{ s.sharedBy.getDisplayName() }}</strong> <strong>{{ s.sharedBy.getDisplayName() }}</strong>
</i18n-t> </i18n-t>
</p> </p>

View file

@ -38,7 +38,7 @@
<div class="filename">{{ a.file.name }}</div> <div class="filename">{{ a.file.name }}</div>
<div class="info"> <div class="info">
<p class="attachment-info-meta"> <p class="attachment-info-meta">
<i18n-t keypath="task.attachment.createdBy"> <i18n-t keypath="task.attachment.createdBy" scope="global">
<span v-tooltip="formatDateLong(a.created)"> <span v-tooltip="formatDateLong(a.created)">
{{ formatDateSince(a.created) }} {{ formatDateSince(a.created) }}
</span> </span>

View file

@ -1,7 +1,7 @@
<template> <template>
<p class="created"> <p class="created">
<time :datetime="formatISO(task.created)" v-tooltip="formatDateLong(task.created)"> <time :datetime="formatISO(task.created)" v-tooltip="formatDateLong(task.created)">
<i18n-t keypath="task.detail.created"> <i18n-t keypath="task.detail.created" scope="global">
<span>{{ formatDateSince(task.created) }}</span> <span>{{ formatDateSince(task.created) }}</span>
{{ task.createdBy.getDisplayName() }} {{ task.createdBy.getDisplayName() }}
</i18n-t> </i18n-t>
@ -10,7 +10,7 @@
<br/> <br/>
<!-- Computed properties to show the actual date every time it gets updated --> <!-- Computed properties to show the actual date every time it gets updated -->
<time :datetime="formatISO(task.updated)" v-tooltip="updatedFormatted"> <time :datetime="formatISO(task.updated)" v-tooltip="updatedFormatted">
<i18n-t keypath="task.detail.updated"> <i18n-t keypath="task.detail.updated" scope="global">
<span>{{ updatedSince }}</span> <span>{{ updatedSince }}</span>
</i18n-t> </i18n-t>
</time> </time>
@ -18,7 +18,7 @@
<template v-if="task.done"> <template v-if="task.done">
<br/> <br/>
<time :datetime="formatISO(task.doneAt)" v-tooltip="doneFormatted"> <time :datetime="formatISO(task.doneAt)" v-tooltip="doneFormatted">
<i18n-t keypath="task.detail.doneAt"> <i18n-t keypath="task.detail.doneAt" scope="global">
<span>{{ doneSince }}</span> <span>{{ doneSince }}</span>
</i18n-t> </i18n-t>
</time> </time>

View file

@ -1,6 +1,8 @@
import AbstractService from './abstractService' import AbstractService from './abstractService'
import {downloadBlob} from '../helpers/downloadBlob' import {downloadBlob} from '../helpers/downloadBlob'
const DOWNLOAD_NAME = 'vikunja-export.zip'
export default class DataExportService extends AbstractService { export default class DataExportService extends AbstractService {
request(password: string) { request(password: string) {
return this.post('/user/export/request', {password}) return this.post('/user/export/request', {password})
@ -10,7 +12,7 @@ export default class DataExportService extends AbstractService {
const clear = this.setLoading() const clear = this.setLoading()
try { try {
const url = await this.getBlobUrl('/user/export/download', 'POST', {password}) const url = await this.getBlobUrl('/user/export/download', 'POST', {password})
downloadBlob(url, 'vikunja-export.zip') downloadBlob(url, DOWNLOAD_NAME)
} finally { } finally {
clear() clear()
} }

View file

@ -34,52 +34,43 @@
</create-edit> </create-edit>
</template> </template>
<script lang="ts"> <script setup lang="ts">
import {defineComponent} from 'vue' import {computed, ref} from 'vue'
import {mapState} from 'pinia' import {useI18n} from 'vue-i18n'
import {useRouter} from 'vue-router'
import LabelModel from '../../models/label'
import CreateEdit from '@/components/misc/create-edit.vue' import CreateEdit from '@/components/misc/create-edit.vue'
import ColorPicker from '../../components/input/colorPicker.vue' import ColorPicker from '@/components/input/colorPicker.vue'
import { setTitle } from '@/helpers/setTitle'
import { useLabelStore } from '@/stores/labels'
export default defineComponent({ import LabelModel from '@/models/label'
name: 'NewLabel', import {useLabelStore} from '@/stores/labels'
data() { import {useTitle} from '@/composables/useTitle'
return { import {success} from '@/message'
label: new LabelModel(),
showError: false,
}
},
components: {
CreateEdit,
ColorPicker,
},
mounted() {
setTitle(this.$t('label.create.title'))
},
computed: {
...mapState(useLabelStore, {
loading: state => state.isLoading,
}),
},
methods: {
async newLabel() {
if (this.label.title === '') {
this.showError = true
return
}
this.showError = false
const labelStore = useLabelStore() const router = useRouter()
const label = labelStore.createLabel(this.label)
this.$router.push({ const {t} = useI18n({useScope: 'global'})
name: 'labels.index', useTitle(() => t('label.create.title'))
params: {id: label.id},
}) const labelStore = useLabelStore()
this.$message.success({message: this.$t('label.create.success')}) const label = ref(new LabelModel())
},
}, const showError = ref(false)
}) const loading = computed(() => labelStore.isLoading)
async function newLabel() {
if (label.value.title === '') {
showError.value = true
return
}
showError.value = false
const labelStore = useLabelStore()
const newLabel = labelStore.createLabel(label.value)
router.push({
name: 'labels.index',
params: {id: newLabel.id},
})
success({message: t('label.create.success')})
}
</script> </script>

View file

@ -10,9 +10,12 @@
class="control is-expanded" class="control is-expanded"
:class="{ 'is-loading': namespaceService.loading }" :class="{ 'is-loading': namespaceService.loading }"
> >
<!-- The user should be able to close the modal by pressing escape - that already works with the default modal.
But with the input modal here since it autofocuses the input that input field catches the focus instead.
Hence we place the listener on the input field directly. -->
<input <input
@keyup.enter="newNamespace()" @keyup.enter="newNamespace()"
@keyup.esc="back()" @keyup.esc="$router.back()"
class="input" class="input"
:placeholder="$t('namespace.attributes.titlePlaceholder')" :placeholder="$t('namespace.attributes.titlePlaceholder')"
type="text" type="text"
@ -40,48 +43,42 @@
</create-edit> </create-edit>
</template> </template>
<script lang="ts"> <script setup lang="ts">
import {defineComponent} from 'vue' import {ref, shallowReactive} from 'vue'
import {useI18n} from 'vue-i18n'
import {useRouter} from 'vue-router'
import Message from '@/components/misc/message.vue' import Message from '@/components/misc/message.vue'
import NamespaceModel from '../../models/namespace'
import NamespaceService from '../../services/namespace'
import CreateEdit from '@/components/misc/create-edit.vue' import CreateEdit from '@/components/misc/create-edit.vue'
import ColorPicker from '../../components/input/colorPicker.vue' import ColorPicker from '@/components/input/colorPicker.vue'
import { setTitle } from '@/helpers/setTitle'
import NamespaceModel from '@/models/namespace'
import NamespaceService from '@/services/namespace'
import {useNamespaceStore} from '@/stores/namespaces' import {useNamespaceStore} from '@/stores/namespaces'
import type {INamespace} from '@/modelTypes/INamespace'
export default defineComponent({ import {useTitle} from '@/composables/useTitle'
name: 'NewNamespace', import {success} from '@/message'
data() {
return {
showError: false,
namespace: new NamespaceModel(),
namespaceService: new NamespaceService(),
}
},
components: {
Message,
ColorPicker,
CreateEdit,
},
mounted() {
setTitle(this.$t('namespace.create.title'))
},
methods: {
async newNamespace() {
if (this.namespace.title === '') {
this.showError = true
return
}
this.showError = false
const namespace = await this.namespaceService.create(this.namespace) const showError = ref(false)
const namespaceStore = useNamespaceStore() const namespace = ref<INamespace>(new NamespaceModel())
namespaceStore.addNamespace(namespace) const namespaceService = shallowReactive(new NamespaceService())
this.$message.success({message: this.$t('namespace.create.success')})
this.$router.back() const {t} = useI18n({useScope: 'global'})
}, const router = useRouter()
},
}) useTitle(() => t('namespace.create.title'))
async function newNamespace() {
if (namespace.value.title === '') {
showError.value = true
return
}
showError.value = false
const newNamespace = await namespaceService.create(namespace.value)
useNamespaceStore().addNamespace(newNamespace)
success({message: t('namespace.create.success')})
router.back()
}
</script> </script>

View file

@ -25,7 +25,7 @@
{{ isLocalUser ? $t('user.settings.caldav.tokensHowTo') : $t('user.settings.caldav.mustUseToken') }} {{ isLocalUser ? $t('user.settings.caldav.tokensHowTo') : $t('user.settings.caldav.mustUseToken') }}
<template v-if="!isLocalUser"> <template v-if="!isLocalUser">
<br/> <br/>
<i18n-t keypath="user.settings.caldav.usernameIs"> <i18n-t keypath="user.settings.caldav.usernameIs" scope="global">
<strong>{{ username }}</strong> <strong>{{ username }}</strong>
</i18n-t> </i18n-t>
</template> </template>