@@ -13,6 +13,7 @@ import { isAttachmentUploaded, isDataPostError } from 'src/features/attachments/
13
13
import { sortAttachmentsByName } from 'src/features/attachments/sortAttachments' ;
14
14
import { attachmentSelector } from 'src/features/attachments/tools' ;
15
15
import { FileScanResults } from 'src/features/attachments/types' ;
16
+ import { DataModels } from 'src/features/datamodel/DataModelsProvider' ;
16
17
import { FD } from 'src/features/formData/FormDataWrite' ;
17
18
import { dataModelPairsToObject } from 'src/features/formData/types' ;
18
19
import {
@@ -24,11 +25,18 @@ import {
24
25
import { useCurrentLanguage } from 'src/features/language/LanguageProvider' ;
25
26
import { useLanguage } from 'src/features/language/useLanguage' ;
26
27
import { backendValidationIssueGroupListToObject } from 'src/features/validation' ;
28
+ import {
29
+ mapBackendIssuesToTaskValidations ,
30
+ mapBackendValidationsToValidatorGroups ,
31
+ mapValidatorGroupsToDataModelValidations ,
32
+ } from 'src/features/validation/backendValidation/backendValidationUtils' ;
33
+ import { Validation } from 'src/features/validation/validationContext' ;
27
34
import { useWaitForState } from 'src/hooks/useWaitForState' ;
35
+ import { doUpdateAttachmentTags } from 'src/queries/queries' ;
28
36
import { nodesProduce } from 'src/utils/layout/NodesContext' ;
29
37
import { NodeDataPlugin } from 'src/utils/layout/plugins/NodeDataPlugin' ;
30
38
import { splitDashedKey } from 'src/utils/splitDashedKey' ;
31
- import { appSupportsNewAttachmentAPI } from 'src/utils/versioning/versions' ;
39
+ import { appSupportsNewAttachmentAPI , appSupportsSetTagsEndpoint } from 'src/utils/versioning/versions' ;
32
40
import type {
33
41
DataPostResponse ,
34
42
IAttachment ,
@@ -40,10 +48,12 @@ import type {
40
48
import type { AttachmentsSelector } from 'src/features/attachments/tools' ;
41
49
import type { AttachmentStateInfo } from 'src/features/attachments/types' ;
42
50
import type { FDActionResult } from 'src/features/formData/FormDataWriteStateMachine' ;
51
+ import type { BackendFieldValidatorGroups } from 'src/features/validation' ;
43
52
import type { DSPropsForSimpleSelector } from 'src/hooks/delayedSelectors' ;
44
53
import type { IDataModelBindingsList , IDataModelBindingsSimple } from 'src/layout/common.generated' ;
45
54
import type { RejectedFileError } from 'src/layout/FileUpload/RejectedFileError' ;
46
55
import type { CompWithBehavior } from 'src/layout/layout' ;
56
+ import type { SetTagsRequest } from 'src/queries/queries' ;
47
57
import type { IData } from 'src/types/shared' ;
48
58
import type { NodesContext , NodesStoreFull } from 'src/utils/layout/NodesContext' ;
49
59
import type { NodeDataPluginSetState } from 'src/utils/layout/plugins/NodeDataPlugin' ;
@@ -384,47 +394,66 @@ export class AttachmentsStorePlugin extends NodeDataPlugin<AttachmentsStorePlugi
384
394
useAttachmentsUpdate ( ) {
385
395
const { mutateAsync : removeTag } = useAttachmentsRemoveTagMutation ( ) ;
386
396
const { mutateAsync : addTag } = useAttachmentsAddTagMutation ( ) ;
387
- const mutateDataElement = useOptimisticallyUpdateDataElement ( ) ;
397
+ const { mutateAsync : updateTags } = useAttachmentUpdateTagsMutation ( ) ;
398
+ const optimisticallyUpdateDataElement = useOptimisticallyUpdateDataElement ( ) ;
388
399
const { lang } = useLanguage ( ) ;
389
400
const update = store . useSelector ( ( state ) => state . attachmentUpdate ) ;
390
401
const fulfill = store . useSelector ( ( state ) => state . attachmentUpdateFulfilled ) ;
391
402
const reject = store . useSelector ( ( state ) => state . attachmentUpdateRejected ) ;
403
+ const backendVersion = useApplicationMetadata ( ) . altinnNugetVersion ?? '' ;
392
404
393
405
return useCallback (
394
406
async ( action : AttachmentActionUpdate ) => {
395
407
const { tags, attachment } = action ;
396
- const tagToAdd = tags . filter ( ( t ) => ! attachment . data . tags ?. includes ( t ) ) ;
397
- const tagToRemove = attachment . data . tags ?. filter ( ( t ) => ! tags . includes ( t ) ) || [ ] ;
398
- const areEqual = tagToAdd . length && tagToRemove . length && tagToAdd [ 0 ] === tagToRemove [ 0 ] ;
408
+ const tagsToAdd = tags . filter ( ( t ) => ! attachment . data . tags ?. includes ( t ) ) ;
409
+ const tagsToRemove = attachment . data . tags ?. filter ( ( t ) => ! tags . includes ( t ) ) ?? [ ] ;
410
+ const areEqual = tagsToAdd . length && tagsToRemove . length && tagsToAdd [ 0 ] === tagsToRemove [ 0 ] ;
411
+ const dataGuid = attachment . data . id ;
399
412
400
413
// If there are no tags to add or remove, or if the tags are the same, do nothing.
401
- if ( ( ! tagToAdd . length && ! tagToRemove . length ) || areEqual ) {
414
+ if ( ( ! tagsToAdd . length && ! tagsToRemove . length ) || areEqual ) {
402
415
return ;
403
416
}
404
417
405
418
update ( action ) ;
406
419
try {
407
- if ( tagToAdd . length ) {
408
- await Promise . all ( tagToAdd . map ( ( tag ) => addTag ( { dataGuid : attachment . data . id , tagToAdd : tag } ) ) ) ;
409
- }
410
- if ( tagToRemove . length ) {
420
+ if ( appSupportsSetTagsEndpoint ( backendVersion ) ) {
421
+ await updateTags ( {
422
+ dataGuid,
423
+ setTagsRequest : {
424
+ tags,
425
+ } ,
426
+ } ) ;
427
+ } else {
428
+ await Promise . all ( tagsToAdd . map ( ( tag ) => addTag ( { dataGuid : attachment . data . id , tagToAdd : tag } ) ) ) ;
411
429
await Promise . all (
412
- tagToRemove . map ( ( tag ) => removeTag ( { dataGuid : attachment . data . id , tagToRemove : tag } ) ) ,
430
+ tagsToRemove . map ( ( tag ) => removeTag ( { dataGuid : attachment . data . id , tagToRemove : tag } ) ) ,
413
431
) ;
414
432
}
415
433
fulfill ( action ) ;
416
- mutateDataElement ( attachment . data . id , ( dataElement ) => ( { ...dataElement , tags } ) ) ;
434
+ optimisticallyUpdateDataElement ( dataGuid , ( dataElement ) => ( { ...dataElement , tags } ) ) ;
435
+
436
+ return ;
417
437
} catch ( error ) {
418
438
reject ( action , error ) ;
419
439
toast ( lang ( 'form_filler.file_uploader_validation_error_update' ) , { type : 'error' } ) ;
420
440
}
421
441
} ,
422
- [ addTag , mutateDataElement , fulfill , lang , reject , removeTag , update ] ,
442
+ [
443
+ update ,
444
+ backendVersion ,
445
+ fulfill ,
446
+ optimisticallyUpdateDataElement ,
447
+ updateTags ,
448
+ addTag ,
449
+ removeTag ,
450
+ reject ,
451
+ lang ,
452
+ ] ,
423
453
) ;
424
454
} ,
425
455
useAttachmentsRemove ( ) {
426
456
const { mutateAsync : removeAttachment } = useAttachmentsRemoveMutation ( ) ;
427
- const removeDataElement = useOptimisticallyRemoveDataElement ( ) ;
428
457
const { lang } = useLanguage ( ) ;
429
458
const remove = store . useSelector ( ( state ) => state . attachmentRemove ) ;
430
459
const fulfill = store . useSelector ( ( state ) => state . attachmentRemoveFulfilled ) ;
@@ -450,7 +479,6 @@ export class AttachmentsStorePlugin extends NodeDataPlugin<AttachmentsStorePlugi
450
479
}
451
480
452
481
fulfill ( action ) ;
453
- removeDataElement ( action . attachment . data . id ) ;
454
482
455
483
return true ;
456
484
} catch ( error ) {
@@ -459,7 +487,7 @@ export class AttachmentsStorePlugin extends NodeDataPlugin<AttachmentsStorePlugi
459
487
return false ;
460
488
}
461
489
} ,
462
- [ removeDataElement , fulfill , lang , reject , remove , removeAttachment , removeValueFromList , setLeafValue ] ,
490
+ [ remove , removeAttachment , fulfill , removeValueFromList , setLeafValue , reject , lang ] ,
463
491
) ;
464
492
} ,
465
493
useAttachments ( nodeId ) {
@@ -682,7 +710,7 @@ function useAttachmentsUploadMutationOld() {
682
710
return useMutation ( options ) ;
683
711
}
684
712
685
- export function useAttachmentsUploadMutation ( ) {
713
+ function useAttachmentsUploadMutation ( ) {
686
714
const { doAttachmentUpload } = useAppMutations ( ) ;
687
715
const instanceId = useLaxInstanceId ( ) ;
688
716
const language = useCurrentLanguage ( ) ;
@@ -705,6 +733,30 @@ export function useAttachmentsUploadMutation() {
705
733
return useMutation ( options ) ;
706
734
}
707
735
736
+ function useAttachmentsRemoveMutation ( ) {
737
+ const { doAttachmentRemove } = useAppMutations ( ) ;
738
+ const instanceId = useLaxInstanceId ( ) ;
739
+ const language = useCurrentLanguage ( ) ;
740
+ const optimisticallyRemoveDataElement = useOptimisticallyRemoveDataElement ( ) ;
741
+
742
+ return useMutation ( {
743
+ mutationFn : ( dataGuid : string ) => {
744
+ if ( ! instanceId ) {
745
+ throw new Error ( 'Missing instanceId, cannot remove attachment' ) ;
746
+ }
747
+
748
+ return doAttachmentRemove ( instanceId , dataGuid , language ) ;
749
+ } ,
750
+ onError : ( error : AxiosError ) => {
751
+ window . logError ( 'Failed to delete attachment:\n' , error ) ;
752
+ } ,
753
+ onSuccess : ( _data , dataGuid ) => {
754
+ optimisticallyRemoveDataElement ( dataGuid ) ;
755
+ } ,
756
+ } ) ;
757
+ }
758
+
759
+ // FIXME: remove this in future release, when all backends support updateTags endpoint
708
760
function useAttachmentsAddTagMutation ( ) {
709
761
const { doAttachmentAddTag } = useAppMutations ( ) ;
710
762
const instanceId = useLaxInstanceId ( ) ;
@@ -723,6 +775,7 @@ function useAttachmentsAddTagMutation() {
723
775
} ) ;
724
776
}
725
777
778
+ // FIXME: remove this in future release, when all backends support updateTags endpoint
726
779
function useAttachmentsRemoveTagMutation ( ) {
727
780
const { doAttachmentRemoveTag } = useAppMutations ( ) ;
728
781
const instanceId = useLaxInstanceId ( ) ;
@@ -741,21 +794,36 @@ function useAttachmentsRemoveTagMutation() {
741
794
} ) ;
742
795
}
743
796
744
- function useAttachmentsRemoveMutation ( ) {
745
- const { doAttachmentRemove } = useAppMutations ( ) ;
797
+ function useAttachmentUpdateTagsMutation ( ) {
746
798
const instanceId = useLaxInstanceId ( ) ;
747
- const language = useCurrentLanguage ( ) ;
799
+ const defaultDataElementId = DataModels . useDefaultDataElementId ( ) ;
800
+ const updateBackendValidations = Validation . useUpdateBackendValidations ( ) ;
748
801
749
802
return useMutation ( {
750
- mutationFn : ( dataGuid : string ) => {
803
+ mutationFn : ( { dataGuid, setTagsRequest } : { dataGuid : string ; setTagsRequest : SetTagsRequest } ) => {
751
804
if ( ! instanceId ) {
752
- throw new Error ( 'Missing instanceId, cannot remove attachment' ) ;
805
+ throw new Error ( 'Missing instanceId, cannot add attachment' ) ;
753
806
}
754
807
755
- return doAttachmentRemove ( instanceId , dataGuid , language ) ;
808
+ return doUpdateAttachmentTags ( { instanceId, dataGuid, setTagsRequest } ) ;
756
809
} ,
757
810
onError : ( error : AxiosError ) => {
758
- window . logError ( 'Failed to delete attachment:\n' , error ) ;
811
+ window . logError ( 'Failed to add tag to attachment:\n' , error ) ;
812
+ } ,
813
+ onSuccess : ( data , variables ) => {
814
+ const backendValidations = data . validationIssues . reduce ( ( prev , curr ) => [ ...prev , ...curr . issues ] , [ ] ) ;
815
+ const initialTaskValidations = mapBackendIssuesToTaskValidations ( backendValidations ) ;
816
+
817
+ const initialValidatorGroups : BackendFieldValidatorGroups = mapBackendValidationsToValidatorGroups (
818
+ backendValidations ,
819
+ defaultDataElementId ,
820
+ ) ;
821
+
822
+ const dataModelValidations = mapValidatorGroupsToDataModelValidations ( initialValidatorGroups , [
823
+ variables . dataGuid ,
824
+ ] ) ;
825
+
826
+ updateBackendValidations ( dataModelValidations , { initial : backendValidations } , initialTaskValidations ) ;
759
827
} ,
760
828
} ) ;
761
829
}
0 commit comments