fix: saving default list #1143

Merged
konrad merged 17 commits from fix/task-create-list into main 2021-12-13 22:20:46 +00:00
Owner
Relevant forum discussion: https://community.vikunja.io/t/quick-add-magic-for-list-not-working/493/3
konrad added 1 commit 2021-12-05 14:32:55 +00:00
fix: saving default list
All checks were successful
continuous-integration/drone/pr Build is passing
0aafbd34a8
konrad reviewed 2021-12-05 14:33:27 +00:00
@ -183,0 +184,4 @@
get() {
return this.$store.getters['lists/getListById'](this.settings.defaultListId)
},
set(l) {
Author
Owner

It looks like this does not even generate an error message.

@dpschen Any idea why?

It looks like this does not even generate an error message. @dpschen Any idea why?

Why should it generate one? I think I need more context.

Regardless:
This seems like the wrong location to set a default value. 0 isn't recognizable as such.
I think at least it should be a const (e.g. DEFAULT_LIST_ID).

Maybe better: creating a defaultList getter in the store and use that everywhere where we need the defaultList. Because else we will repeat the returning of the defaultListId as 0 over and over =)

Why should it generate one? I think I need more context. Regardless: This seems like the wrong location to set a default value. `0` isn't recognizable as such. I think at least it should be a const (e.g. `DEFAULT_LIST_ID`). Maybe better: creating a defaultList getter in the store and use that everywhere where we need the defaultList. Because else we will repeat the returning of the defaultListId as `0` over and over =)
Author
Owner

Why should it generate one? I think I need more context.

Because I'm trying to assign to a computed value. It works with a setter, but doesn't this should throw an error?

Maybe better: creating a defaultList getter in the store and use that everywhere where we need the defaultList. Because else we will repeat the returning of the defaultListId as 0 over and over =)

But here in this case I want to query the set default list from the component instead of the one currently saved in the store. Having a getter in the store with a function property that's only used once seems a bit pointless.

Not really sure how to improve this. Added a DEFAULT_LIST_ID const for now, maybe that already improves things a bit.

> Why should it generate one? I think I need more context. Because I'm trying to assign to a computed value. It works with a setter, but doesn't this should throw an error? > Maybe better: creating a defaultList getter in the store and use that everywhere where we need the defaultList. Because else we will repeat the returning of the defaultListId as 0 over and over =) But here in this case I want to query the set default list from the component instead of the one currently saved in the store. Having a getter in the store with a function property that's only used once seems a bit pointless. Not really sure how to improve this. Added a `DEFAULT_LIST_ID` const for now, maybe that already improves things a bit.

So if you set the defaultListId to 0 that means there is none, right?

EDIT: in the sense of: we reset the defaultListId

If that's the reason for the 0 then maybe we should just use that value when sending to the service.
Or even better using this as a formatter inside the service.

So if userSettingsService.update is called with an settings object where defaultListId is undefined just replace that with defaultListId: 0 before sending to the server (?)

So if you set the defaultListId to `0` that means there is none, right? EDIT: in the sense of: we reset the defaultListId If that's the reason for the `0` then maybe we should just use that value when sending to the service. Or even better using this as a formatter inside the service. So if `userSettingsService.update` is called with an settings object where `defaultListId` is undefined just replace that with `defaultListId: 0` before sending to the server (?)
konrad requested review from dpschen 2021-12-05 14:33:33 +00:00
Member

Hi konrad!

Thank you for creating a PR!

I've deployed the changes of this PR on a preview environment under this URL: https://1143-fixtask-create-list--vikunja-frontend-preview.netlify.app

You can use this url to view the changes live and test them out.
You will need to manually connect this to an api running somehwere. The easiest to use is https://try.vikunja.io/.

Have a nice day!

Beep boop, I'm a bot.

Hi konrad! Thank you for creating a PR! I've deployed the changes of this PR on a preview environment under this URL: https://1143-fixtask-create-list--vikunja-frontend-preview.netlify.app You can use this url to view the changes live and test them out. You will need to manually connect this to an api running somehwere. The easiest to use is https://try.vikunja.io/. Have a nice day! > Beep boop, I'm a bot.
konrad added 1 commit 2021-12-05 14:47:19 +00:00
fix: list not set initially
All checks were successful
continuous-integration/drone/pr Build is passing
86e827f93c
konrad reviewed 2021-12-05 14:48:12 +00:00
@ -38,2 +38,4 @@
Multiselect,
},
mounted() {
this.list = this.modelValue
Author
Owner

It looks like the watcher does not get called, despite immediate set to true.
@dpschen Do you have an idea why that is the case?

It looks like the watcher does not get called, despite `immediate` set to `true`. @dpschen Do you have an idea why that is the case?

See the other comment

See the other comment
dpschen marked this conversation as resolved
konrad added 1 commit 2021-12-05 15:12:06 +00:00
feat: convert list search component to script setup api
Some checks reported errors
continuous-integration/drone/pr Build was killed
057e847e39
konrad added 1 commit 2021-12-05 15:13:44 +00:00
fix: multiselect search result padding
Some checks failed
continuous-integration/drone/pr Build is failing
42ecf4e10a
Author
Owner

Note to self: imrove the error message when the list was not found (no default list set or none provided via quick add magic) to tell the users they need to provide a list

Note to self: imrove the error message when the list was not found (no default list set or none provided via quick add magic) to tell the users they need to provide a list
dpschen reviewed 2021-12-06 12:57:55 +00:00
dpschen requested changes 2021-12-06 13:14:16 +00:00
@ -458,3 +458,3 @@
font-family: $family-sans-serif;
font-weight: normal;
padding: .5rem 0;
padding: .5rem .5rem;

padding: .5rem;

`padding: .5rem;`
Author
Owner

Done.

Done.
konrad marked this conversation as resolved
@ -2,3 +2,3 @@
<multiselect
class="control is-expanded"
:placeholder="$t('list.search')"
:placeholder="t('list.search')"

$t is undefined?

`$t` is undefined?
Author
Owner

It actually isn't! I just thought I needed to use t since I'm importing it already. Changed it.

It actually isn't! I just thought I needed to use `t` since I'm importing it already. Changed it.

I'm not sure what way is actually the better one because later we might need to change everything in t or write our own injection as far as I can remeber the vue-i18n 9 documentation.

I'm not sure what way is actually the better one because later we might need to change everything in `t` or write our own injection as far as I can remeber the vue-i18n 9 documentation.
konrad marked this conversation as resolved
@ -39,0 +38,4 @@
watch(
() => props.modelValue,
value => {
list = value

I think you need to use Object.assign for a reactive here. Because else you will replace the reactive with the static data coming from the prop modelValue.

Or use a ref. But then you need to assign to list.value

I think you need to use `Object.assign` for a reactive here. Because else you will replace the reactive with the static data coming from the prop `modelValue`. Or use a `ref`. But then you need to assign to `list.value`

If you use watchEffect here you can save the definition of what to watch (vue does that automatically). Also it runs initially by default, so no need for immediate: true.

Also there is a spelling error (it is immeditate: true, right now), so that might also be a problem.

Sidenote: vueuse useVModel might also be of help for the value syncing here).

If you use `watchEffect` here you can save the definition of what to watch (vue does that automatically). Also it runs initially by default, so no need for `immediate: true`. Also there is a spelling error (it is `immeditate: true,` right now), so that might also be a problem. Sidenote: [vueuse useVModel](https://vueuse.org/core/usevmodel/) might also be of help for the value syncing here).
Author
Owner

Or use a ref. But then you need to assign to list.value

I thought ref should be used preferrably for primitive types, not objects?

Also there is a spelling error (it is immeditate: true, right now), so that might also be a problem.

wow now I feel a bit dumb 😄 It seems to work if I correct it.

> Or use a ref. But then you need to assign to list.value I thought `ref` should be used preferrably for primitive types, not objects? > Also there is a spelling error (it is immeditate: true, right now), so that might also be a problem. wow now I feel a bit dumb 😄 It seems to work if I correct it.
Author
Owner

watchEffect seems to work great, switched it to this and Object.assign.

`watchEffect` seems to work great, switched it to this and `Object.assign`.
konrad marked this conversation as resolved
@ -66,2 +57,2 @@
},
},
function select(l) {
list = l

Use Object.assign

Use `Object.assign`
konrad marked this conversation as resolved
@ -68,0 +62,4 @@
function namespace(namespaceId) {
const namespace = store.getters['namespaces/getNamespaceById'](namespaceId)
if (namespace !== null) {

return namespace !== null
    ? namespace.title
    : t('list.shared')
```js return namespace !== null ? namespace.title : t('list.shared') ```
Author
Owner

Done.

Done.
konrad marked this conversation as resolved
Member

Maybe related:

I also realized that there is no default list initially.

So even if it returns 0 will this work?

Maybe related: I also realized that there is no default list initially. So even if it returns `0` will this work?
konrad added 3 commits 2021-12-07 19:42:43 +00:00
konrad added 1 commit 2021-12-07 19:45:02 +00:00
fix: usage of immediate
All checks were successful
continuous-integration/drone/pr Build is passing
c441283909
konrad added 1 commit 2021-12-07 19:54:30 +00:00
fix: use watchEffect and prevent loosing reactivity
All checks were successful
continuous-integration/drone/pr Build is passing
8c4aeec53f
konrad requested review from dpschen 2021-12-07 19:55:13 +00:00
Author
Owner

I also realized that there is no default list initially.

So even if it returns 0 will this work?

It wont work in that case. The default list is 0 because that's the nil value of an int in Go. Setting it to 0 matches that behaviour but is not ideal.

> I also realized that there is no default list initially. > > So even if it returns `0` will this work? It wont work in that case. The default list is 0 because that's the nil value of an int in Go. Setting it to 0 matches that behaviour but is not ideal.
konrad added 1 commit 2021-12-07 20:04:07 +00:00
chore: add const for default list id
Some checks failed
continuous-integration/drone/pr Build is failing
49e44901b2
dpschen requested changes 2021-12-07 21:39:42 +00:00
@ -35,0 +26,4 @@
const store = useStore()
const {t} = useI18n()
let list = reactive(new ListModel())

A reactive has to be a const. If you reassign it, it wouldn't be a reactive anymore. Same goes for refs.

If you think of refs as reactives with just one exposed prop that is named value it's easier to understand.
Also good to know is that vues reactivity is now based on Proxies. When you know this it makes sense that reactivity works only on the objects props (.value / the props of a reactive) and you cannot simply replace the proxy.

(if you knew all of that, tell me and I'll stop explaing)

A reactive has to be a `const`. If you reassign it, it wouldn't be a reactive anymore. Same goes for refs. If you think of refs as reactives with just one exposed prop that is named value it's easier to understand. Also good to know is that vues reactivity is now based on [Proxies](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy). When you know this it makes sense that reactivity works only on the objects props (`.value` / the props of a reactive) and you cannot simply replace the proxy. (if you knew all of that, tell me and I'll stop explaing)
Author
Owner

Thanks for the explanation, that makes a lot of sense.

(if you knew all of that, tell me and I'll stop explaing)

No worries! I kind of know bits and pieces of it, not the whole thing :)

Thanks for the explanation, that makes a lot of sense. > (if you knew all of that, tell me and I'll stop explaing) No worries! I kind of know bits and pieces of it, not the whole thing :)
konrad marked this conversation as resolved
@ -51,2 +47,2 @@
this.foundLists = this.$store.getters['lists/searchList'](query)
},
)
list = props.modelValue

I overlooked this when I first checked this.
With the watcher / watchEffect this shouldn't be necessary.

I overlooked this when I first checked this. With the watcher / watchEffect this shouldn't be necessary.
Author
Owner

Yes, this works.

Yes, this works.
konrad marked this conversation as resolved
@ -67,1 +57,3 @@
},
function select(l) {
list = l
emit('selected', list)

I think we should remove the selected and only use update:modelValue to reduce complexity, since they are doing the same.

I think we should remove the `selected` and only use `update:modelValue` to reduce complexity, since they are doing the same.
Author
Owner

I didn't know you could listen to update:modelValue explicitly. Removed selected.

I didn't know you could listen to `update:modelValue` explicitly. Removed `selected`.

AFAIK it really is just a convention, other than that a regular event.

AFAIK it really is just a convention, other than that a regular event.
konrad marked this conversation as resolved
dpschen reviewed 2021-12-07 21:51:08 +00:00
@ -205,3 +214,3 @@
saveLanguage(this.language)
setQuickAddMagicMode(this.quickAddMagicMode)
this.settings.defaultListId = this.defaultList ? this.defaultList.id : 0
this.settings.defaultListId = this.defaultList ? this.defaultList.id : DEFAULT_LIST_ID

Since this is before sending to the server we should just create a new object here (and not modify the settings directly). Something like (super untested):

const settings = {
	...this.settings, // maybe needs copy
    defaultListId ? this.defaultList.id : DEFAULT_LIST_ID,
}

const newSettings = await this.userSettingsService.update(settings)


this.$store.commit('auth/setUserSettings', newSettings)
Since this is before sending to the server we should just create a new object here (and not modify the settings directly). Something like (super untested): ```js const settings = { ...this.settings, // maybe needs copy defaultListId ? this.defaultList.id : DEFAULT_LIST_ID, } const newSettings = await this.userSettingsService.update(settings) this.$store.commit('auth/setUserSettings', newSettings) ```
Author
Owner

Changed it to do just that. The api does not return the new settings though, only a success message - I'm committing the settings object built previously.

Changed it to do just that. The api does not return the new settings though, only a success message - I'm committing the settings object built previously.
konrad marked this conversation as resolved
konrad added 1 commit 2021-12-11 15:12:05 +00:00
chore: make list const
Some checks reported errors
continuous-integration/drone/pr Build was killed
85dfed5bac
konrad added 1 commit 2021-12-11 15:14:38 +00:00
chore: remove selected event
Some checks reported errors
continuous-integration/drone/pr Build was killed
51d8405353
konrad added 1 commit 2021-12-11 15:19:16 +00:00
chore: create new settings object before saving
All checks were successful
continuous-integration/drone/pr Build is passing
0080ad2077
dpschen requested changes 2021-12-12 13:29:00 +00:00
@ -478,3 +478,3 @@
color: transparent;
transition: color $transition;
padding: 0 .5rem;
padding-left: .5rem;

Not sure if by intend: you removed the padding-right.

Not sure if by intend: you removed the `padding-right`.
Author
Owner

Yes that was intended because the text on the right "press enter or click to select" already has padding.

Yes that was intended because the text on the right "press enter or click to select" already has padding.
konrad marked this conversation as resolved
@ -18,3 +18,2 @@
<script>
import ListModel from '../../../models/list'
<script setup>

Use ts

Use ts
Author
Owner

I don't know why but I can't seem to use the component anymore when I add it:

 SyntaxError: import not found: default client.ts:33:12
    warnFailedFetch client.ts:33
    fetchUpdate client.ts:334
    fetchUpdate client.ts:321
    handleMessage client.ts:72
    handleMessage client.ts:70
    <anonymous> client.ts:44
    (Async: EventListener.handleEvent)
    <anonymous> client.ts:43
15:06:39.577
[hmr] Failed to reload /src/components/tasks/partials/listSearch.vue. This could be due to syntax errors or importing non-existent modules. (see errors above)
I don't know why but I can't seem to use the component anymore when I add it: ``` SyntaxError: import not found: default client.ts:33:12 warnFailedFetch client.ts:33 fetchUpdate client.ts:334 fetchUpdate client.ts:321 handleMessage client.ts:72 handleMessage client.ts:70 <anonymous> client.ts:44 (Async: EventListener.handleEvent) <anonymous> client.ts:43 15:06:39.577 [hmr] Failed to reload /src/components/tasks/partials/listSearch.vue. This could be due to syntax errors or importing non-existent modules. (see errors above) ```
Author
Owner

I don't know why it did not work previously, seems to work now.

I don't know why it did not work previously, seems to work now.
konrad marked this conversation as resolved
@ -48,0 +27,4 @@
const {t} = useI18n()
const list = reactive(new ListModel())
let foundLists = ref([])

Ref foundLists should be a const.

Ref `foundLists` should be a const.

Move foundLists definition next to where it is used (the findLists function).
=> Group by function; More vue composition way of doing things.

Move `foundLists` definition next to where it is used (the findLists function). => Group by function; More vue composition way of doing things.
Author
Owner

Done.

Done.
konrad marked this conversation as resolved
@ -48,0 +30,4 @@
let foundLists = ref([])
const props = defineProps({
modelValue: {
required: false,

Add type

Add type
Author
Owner

I had to use a custom validation because the ListModel is not really a type.

I had to use a custom validation because the `ListModel` is not really a type.
konrad marked this conversation as resolved
@ -68,0 +47,4 @@
}
function select(l) {
Object.assign(list, l)

Is the Object.assign here even needed?

Asking because:
Since you emit the new value in the next line it should already be changed via the update of the modelValue in the watchEffect. In the sense of events up / props down.

Is the `Object.assign` here even needed? Asking because: Since you emit the new value in the next line it should already be changed via the update of the modelValue in the watchEffect. In the sense of events up / props down.

Okay I just saw that modelValue is not required and also not always set (e.g. in the TaskDetailView).
Then of course it's still needed.

Okay I just saw that modelValue is not required and also not always set (e.g. in the TaskDetailView). Then of course it's still needed.
konrad marked this conversation as resolved
@ -211,2 +217,3 @@
const settings = {
...this.settings,
})
defaultListId: this.defaultList ? this.defaultList.id : DEFAULT_LIST_ID,

I think I know now what seemed of for me:
defaultListId should be a computed.
This way the logic this.defaultList ? this.defaultList.id : DEFAULT_LIST_ID does not have to be repeated here and in the defaultList setter.

I think I know now what seemed of for me: defaultListId should be a computed. This way the logic `this.defaultList ? this.defaultList.id : DEFAULT_LIST_ID` does not have to be repeated here and in the defaultList setter.
Author
Owner

We actually don't need this at all due to the setter. I've removed it.

We actually don't need this at all due to the setter. I've removed it.

Didn't realize this aswell. Nice!

Didn't realize this aswell. Nice!
konrad marked this conversation as resolved
konrad added 1 commit 2021-12-12 14:08:38 +00:00
chore: add validation
All checks were successful
continuous-integration/drone/pr Build is passing
cfa643e9b5
konrad added 1 commit 2021-12-12 14:11:27 +00:00
fix: cleanup unnecessary default list id spread
All checks were successful
continuous-integration/drone/pr Build is passing
48fe0f5bba
konrad added 2 commits 2021-12-12 19:55:39 +00:00
chore: use ts for listSearch component
All checks were successful
continuous-integration/drone/pr Build is passing
cb0b7f6e6b
dpschen approved these changes 2021-12-13 13:45:32 +00:00
konrad merged commit 543dae2f30 into main 2021-12-13 22:20:46 +00:00
konrad deleted branch fix/task-create-list 2021-12-13 22:20:46 +00:00
This repo is archived. You cannot comment on pull requests.
No description provided.