From f17372bd76297f41b47cc075dee4634cce0aa6cc Mon Sep 17 00:00:00 2001 From: Thiago Carvalho Date: Thu, 22 Feb 2024 15:34:40 -0300 Subject: [PATCH 01/16] feat(ANS-105) - add new cc license types - add the LicenseCategory enum to differentiate cc and udl licenses --- .../fs_entry_license_bloc.dart | 110 +++++---- lib/components/fs_entry_license_form.dart | 212 ++++++++++++++---- lib/services/license/license_service.dart | 5 + lib/services/license/license_state.dart | 20 ++ lib/services/license/licenses/cc_by.dart | 52 ++++- 5 files changed, 307 insertions(+), 92 deletions(-) diff --git a/lib/blocs/fs_entry_license/fs_entry_license_bloc.dart b/lib/blocs/fs_entry_license/fs_entry_license_bloc.dart index 6a74e310ac..bf9ecd1456 100644 --- a/lib/blocs/fs_entry_license/fs_entry_license_bloc.dart +++ b/lib/blocs/fs_entry_license/fs_entry_license_bloc.dart @@ -24,40 +24,9 @@ class FsEntryLicenseBloc final String driveId; final List selectedItems; - final selectForm = FormGroup({ - 'licenseType': FormControl( - validators: [Validators.required], - value: udlLicenseMeta, - ), - }); - LicenseMeta get selectFormLicenseMeta => - selectForm.control('licenseType').value; - - final udlForm = FormGroup({ - 'licenseFeeAmount': FormControl( - validators: [ - Validators.composeOR([ - Validators.pattern( - r'^\d+\.?\d*$', - validationMessage: 'Invalid amount', - ), - Validators.equals(''), - ]), - ], - ), - 'licenseFeeCurrency': FormControl( - validators: [Validators.required], - value: UdlCurrency.u, - ), - 'commercialUse': FormControl( - validators: [Validators.required], - value: UdlCommercialUse.unspecified, - ), - 'derivations': FormControl( - validators: [Validators.required], - value: UdlDerivation.unspecified, - ), - }); + // We initialize with UDL license by default + LicenseMeta _selectedLicenseMeta = udlLicenseMeta; + LicenseMeta get selectedLicenseMeta => _selectedLicenseMeta; List? filesToLicense; LicenseParams? licenseParams; @@ -112,12 +81,18 @@ class FsEntryLicenseBloc } if (event is FsEntryLicenseSelect) { - if (selectFormLicenseMeta.hasParams) { - emit(const FsEntryLicenseConfiguring()); - } else { - licenseParams = null; - emit(const FsEntryLicenseReviewing()); + final licenseType = selectForm.control('licenseType').value; + + switch (licenseType) { + case LicenseCategory.udl: + _selectedLicenseMeta = udlLicenseMeta; + break; + case LicenseCategory.cc: + _selectedLicenseMeta = ccByLicenseMeta; + break; } + + emit(const FsEntryLicenseConfiguring()); } if (event is FsEntryLicenseConfigurationBack) { @@ -125,17 +100,24 @@ class FsEntryLicenseBloc } if (event is FsEntryLicenseConfigurationSubmit) { - if (selectFormLicenseMeta.licenseType == LicenseType.udl) { + final licenseCategory = selectForm.control('licenseType').value; + + if (licenseCategory == LicenseCategory.cc) { + _selectedLicenseMeta = ccForm.control('ccAttributionField').value; + } + + if (_selectedLicenseMeta.licenseType == LicenseType.udl) { licenseParams = await udlFormToLicenseParams(udlForm); } else { addError( - 'Unsupported license configuration: ${selectFormLicenseMeta.licenseType}'); + 'Unsupported license configuration: ${_selectedLicenseMeta.licenseType}'); } + emit(const FsEntryLicenseReviewing()); } if (event is FsEntryLicenseReviewBack) { - if (selectFormLicenseMeta.hasParams) { + if (_selectedLicenseMeta.hasParams) { licenseParams = null; emit(const FsEntryLicenseConfiguring()); } else { @@ -148,7 +130,7 @@ class FsEntryLicenseBloc try { await licenseEntities( profile: profile, - licenseMeta: selectFormLicenseMeta, + licenseMeta: _selectedLicenseMeta, licenseParams: licenseParams, ); emit(const FsEntryLicenseSuccess()); @@ -328,4 +310,46 @@ class FsEntryLicenseBloc errorLog.add(error.toString()); super.onError(error, stackTrace); } + + // Forms + + final selectForm = FormGroup({ + 'licenseType': FormControl( + validators: [Validators.required], + value: LicenseCategory.udl, + ), + }); + + final udlForm = FormGroup({ + 'licenseFeeAmount': FormControl( + validators: [ + Validators.composeOR([ + Validators.pattern( + r'^\d+\.?\d*$', + validationMessage: 'Invalid amount', + ), + Validators.equals(''), + ]), + ], + ), + 'licenseFeeCurrency': FormControl( + validators: [Validators.required], + value: UdlCurrency.u, + ), + 'commercialUse': FormControl( + validators: [Validators.required], + value: UdlCommercialUse.unspecified, + ), + 'derivations': FormControl( + validators: [Validators.required], + value: UdlDerivation.unspecified, + ), + }); + + final ccForm = FormGroup({ + 'ccAttributionField': FormControl( + validators: [Validators.required], + value: ccByLicenseMeta, + ), + }); } diff --git a/lib/components/fs_entry_license_form.dart b/lib/components/fs_entry_license_form.dart index a94d1414bf..446cc23854 100644 --- a/lib/components/fs_entry_license_form.dart +++ b/lib/components/fs_entry_license_form.dart @@ -2,11 +2,13 @@ import 'package:ardrive/blocs/blocs.dart'; import 'package:ardrive/components/license_summary.dart'; import 'package:ardrive/core/crypto/crypto.dart'; import 'package:ardrive/l11n/validation_messages.dart'; +import 'package:ardrive/misc/resources.dart'; import 'package:ardrive/models/models.dart'; import 'package:ardrive/services/services.dart'; import 'package:ardrive/theme/theme.dart'; import 'package:ardrive/turbo/services/upload_service.dart'; import 'package:ardrive/utils/app_localizations_wrapper.dart'; +import 'package:ardrive/utils/open_url.dart'; import 'package:ardrive/utils/show_general_dialog.dart'; import 'package:ardrive_ui/ardrive_ui.dart'; import 'package:flutter/material.dart'; @@ -89,7 +91,7 @@ class _FsEntryLicenseFormState extends State { builder: (context, state) { return Builder(builder: (context) { final licenseMeta = - context.read().selectFormLicenseMeta; + context.read().selectedLicenseMeta; if (state is FsEntryLicenseNoFiles) { return ArDriveCard( height: 350, @@ -196,20 +198,23 @@ class _FsEntryLicenseFormState extends State { width: kMediumDialogWidth, // TODO: Localize // title: appLocalizationsOf(context).renameFolderEmphasized, - content: SizedBox( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - LicenseFileList(fileList: filesToLicense), - const SizedBox(height: 16), - const Divider(height: 24), - ReactiveForm( + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + LicenseFileList(fileList: filesToLicense), + const SizedBox(height: 16), + const Divider(height: 24), + SizedBox( + child: ReactiveForm( formGroup: context.watch().selectForm, child: ReactiveDropdownField( + alignment: AlignmentDirectional.centerStart, + isExpanded: true, formControlName: 'licenseType', decoration: InputDecoration( + border: InputBorder.none, label: Text( 'License', // TODO: Localize @@ -232,40 +237,56 @@ class _FsEntryLicenseFormState extends State { control.dirty && control.invalid, validationMessages: kValidationMessages(appLocalizationsOf(context)), - items: licenseMetaMap.values - .map( - (value) => DropdownMenuItem( - value: value, - child: - Text('${value.name} (${value.shortName})'), + items: LicenseCategory.values.map( + (value) { + return DropdownMenuItem( + value: value, + child: Text( + '${licenseCategoryNames[value]}', ), - ) - .toList(), + ); + }, + ).toList(), ), ), - const Divider(height: 32), - Text( - // TODO: Localize - 'Cost: 0 AR', - style: ArDriveTypography.body.buttonLargeRegular( - color: ArDriveTheme.of(context) - .themeData - .colors - .themeFgDefault, + ), + ArDriveClickArea( + child: GestureDetector( + onTap: () { + openUrl( + url: Resources.howDoesKeyFileLoginWork, + ); + }, + child: Text( + 'Learn More about Licensing', + style: ArDriveTypography.body + .buttonNormalRegular() + .copyWith(decoration: TextDecoration.underline), ), ), - Text( - // TODO: Localize - 'Free for now, maybe paid later.', - style: ArDriveTypography.body.captionRegular( - color: ArDriveTheme.of(context) - .themeData - .colors - .themeFgSubtle, - ), + ), + const Divider(height: 32), + Text( + // TODO: Localize + 'Cost: 0 AR', + style: ArDriveTypography.body.buttonLargeRegular( + color: ArDriveTheme.of(context) + .themeData + .colors + .themeFgDefault, ), - ], - ), + ), + Text( + // TODO: Localize + 'Free for now, maybe paid later.', + style: ArDriveTypography.body.captionRegular( + color: ArDriveTheme.of(context) + .themeData + .colors + .themeFgSubtle, + ), + ), + ], ), actions: [ ModalAction( @@ -281,12 +302,15 @@ class _FsEntryLicenseFormState extends State { ], ); } else if (state is FsEntryLicenseConfiguring) { + final licenseType = context + .read() + .selectedLicenseMeta + .licenseType; return ArDriveScrollBar( child: SingleChildScrollView( child: ArDriveStandardModal( - title: - 'Configure ${licenseMeta.name} (${licenseMeta.shortName})', - width: kMediumDialogWidth, + title: 'Configure ${licenseMeta.name}', + width: kLargeDialogWidth, content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, @@ -298,11 +322,7 @@ class _FsEntryLicenseFormState extends State { .filesToLicense!), const SizedBox(height: 16), const Divider(height: 24), - context - .read() - .selectFormLicenseMeta - .licenseType == - LicenseType.udl + licenseType == LicenseType.udl ? UdlParamsForm( onChangeLicenseFee: () { setState(() {}); @@ -310,7 +330,13 @@ class _FsEntryLicenseFormState extends State { formGroup: context.watch().udlForm, ) - : const Text('Unsupported license type'), + : licenseType == LicenseType.ccBy + ? CcParamsForm( + formGroup: context + .watch() + .ccForm, + ) + : const Text('Unsupported license type'), ], ), actions: [ @@ -828,3 +854,93 @@ class _UdlParamsFormState extends State { )); } } + +class CcParamsForm extends StatefulWidget { + const CcParamsForm({super.key, required this.formGroup}); + + final FormGroup formGroup; + + @override + State createState() => _CcParamsFormState(); +} + +class _CcParamsFormState extends State { + LicenseMeta get licenseMeta => + context.read().selectedLicenseMeta; + + @override + Widget build(BuildContext context) { + final inputBorder = OutlineInputBorder( + borderSide: BorderSide( + color: ArDriveTheme.of(context) + .themeData + .colors + .themeFgDisabled + .withOpacity(0.3), + width: 2, + ), + borderRadius: BorderRadius.circular(4), + ); + + return ReactiveForm( + formGroup: widget.formGroup, + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Flexible( + child: LabeledInput( + labelText: 'Type', + child: ReactiveDropdownField( + formControlName: 'ccAttributionField', + decoration: InputDecoration( + enabledBorder: inputBorder, + focusedBorder: inputBorder, + ), + onChanged: (e) { + setState(() {}); + }, + showErrors: (control) => control.dirty && control.invalid, + validationMessages: + kValidationMessages(appLocalizationsOf(context)), + items: ccLicenses + .map( + (e) => DropdownMenuItem( + value: e, + child: Text(e.name), + ), + ) + .toList(), + ), + ), + ), + ], + ), + Builder( + builder: (context) { + final selectedLicenseMeta = context + .watch() + .ccForm + .value['ccAttributionField'] as LicenseMeta; + + return Text( + selectedLicenseMeta.shortName, + style: ArDriveTypography.body.buttonNormalBold( + color: ArDriveTheme.of(context) + .themeData + .colors + .themeFgDisabled, + ), + ); + }, + ), + ]), + ); + } +} diff --git a/lib/services/license/license_service.dart b/lib/services/license/license_service.dart index c42041f370..e1a5dfd3b7 100644 --- a/lib/services/license/license_service.dart +++ b/lib/services/license/license_service.dart @@ -33,6 +33,11 @@ class LicenseService { case LicenseType.udl: return UdlLicenseParams.fromAdditionalTags(additionalTags ?? {}); case LicenseType.ccBy: + case LicenseType.ccByNC: + case LicenseType.ccByNCND: + case LicenseType.ccByNCSA: + case LicenseType.ccByND: + case LicenseType.ccBySA: return EmptyParams(); default: throw ArgumentError('Unknown license type: $licenseType'); diff --git a/lib/services/license/license_state.dart b/lib/services/license/license_state.dart index b80edb1bb3..f2c6a0e182 100644 --- a/lib/services/license/license_state.dart +++ b/lib/services/license/license_state.dart @@ -4,9 +4,19 @@ import 'package:equatable/equatable.dart'; enum LicenseType { udl, ccBy, + ccByNC, + ccByNCND, + ccByNCSA, + ccByND, + ccBySA, unknown, } +enum LicenseCategory { + cc, + udl, +} + class LicenseMeta extends Equatable { final LicenseType licenseType; final String licenseDefinitionTxId; @@ -47,6 +57,16 @@ class EmptyParams extends LicenseParams {} final licenseMetaMap = { LicenseType.udl: udlLicenseMeta, LicenseType.ccBy: ccByLicenseMeta, + LicenseType.ccByNC: ccByNCLicenseMeta, + LicenseType.ccByNCND: ccByNCNDLicenseMeta, + LicenseType.ccByNCSA: ccByNCSA, + LicenseType.ccByND: ccByNDLicenseMeta, + LicenseType.ccBySA: ccBySA, +}; + +final licenseCategoryNames = { + LicenseCategory.udl: 'Universal Data License - UDL', + LicenseCategory.cc: 'Creative Commons - CC', }; class LicenseState extends Equatable { diff --git a/lib/services/license/licenses/cc_by.dart b/lib/services/license/licenses/cc_by.dart index b28dc8e801..728a28b822 100644 --- a/lib/services/license/licenses/cc_by.dart +++ b/lib/services/license/licenses/cc_by.dart @@ -1,9 +1,59 @@ import '../license_state.dart'; +List ccLicenses = [ + ccByLicenseMeta, + ccByNCLicenseMeta, + ccByNCNDLicenseMeta, + ccByNCSA, + ccByNDLicenseMeta, + ccBySA, +]; + const ccByLicenseMeta = LicenseMeta( licenseType: LicenseType.ccBy, licenseDefinitionTxId: 'rz2DNzn9pnYOU6049Wm6V7kr0BhyfWE6ZD_mqrXMv5A', - name: 'Creative Commons Attribution', + name: 'Attribution', shortName: 'CC-BY', version: '4.0', + hasParams: true, +); + +const ccByNCLicenseMeta = LicenseMeta( + licenseType: LicenseType.ccByNC, + licenseDefinitionTxId: '9jG6a1fWgQ_wE4R6OGA2Xg9vGRAwpkrQIMC83nC3kvI', + name: 'Attribution Non-Commercial', + shortName: 'CC-BY-NC', + version: '4.0', +); + +const ccByNCNDLicenseMeta = LicenseMeta( + licenseType: LicenseType.ccByNCND, + licenseDefinitionTxId: 'OlTlW1xEw75UC0cdmNqvxc3j6iAmFXrS4usWIBfu_3E', + name: 'Attribution Non-Commercial No-Derivatives', + shortName: 'CC-BY-NC-ND', + version: '4.0', +); + +const ccByNCSA = LicenseMeta( + licenseType: LicenseType.ccByNCSA, + licenseDefinitionTxId: '2PO2MDRNZLJjgA_0hNGUAD7yXg9nneq-3fxTTLP-uo8', + name: 'Attribution Non-Commercial Share-A-Like', + shortName: 'CC-BY-NC-SA', + version: '4.0', +); + +const ccByNDLicenseMeta = LicenseMeta( + licenseType: LicenseType.ccByND, + licenseDefinitionTxId: 'XaIMRBMNqTUlHa_hzypkopfRFyAKqit-AWo-OxwIxoo', + name: 'Attribution No-Derivatives', + shortName: 'CC-BY-ND', + version: '4.0', +); + +const ccBySA = LicenseMeta( + licenseType: LicenseType.ccBySA, + licenseDefinitionTxId: 'sKz-PZ96ApDoy5RTBspxhs1GP-cHommw4_9hEiZ6K3c', + name: 'Attribution Share-A-Like', + shortName: 'CC-BY-SA', + version: '4.0', ); From c4f3bea19ccb3f8a9f516e619d861b8d9bd8ce00 Mon Sep 17 00:00:00 2001 From: Thiago Carvalho Date: Thu, 22 Feb 2024 15:35:51 -0300 Subject: [PATCH 02/16] refactor: rename variables --- lib/services/license/license_state.dart | 4 ++-- lib/services/license/licenses/cc_by.dart | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/services/license/license_state.dart b/lib/services/license/license_state.dart index f2c6a0e182..d85fe563cc 100644 --- a/lib/services/license/license_state.dart +++ b/lib/services/license/license_state.dart @@ -59,9 +59,9 @@ final licenseMetaMap = { LicenseType.ccBy: ccByLicenseMeta, LicenseType.ccByNC: ccByNCLicenseMeta, LicenseType.ccByNCND: ccByNCNDLicenseMeta, - LicenseType.ccByNCSA: ccByNCSA, + LicenseType.ccByNCSA: ccByNCSAMeta, LicenseType.ccByND: ccByNDLicenseMeta, - LicenseType.ccBySA: ccBySA, + LicenseType.ccBySA: ccBySAMeta, }; final licenseCategoryNames = { diff --git a/lib/services/license/licenses/cc_by.dart b/lib/services/license/licenses/cc_by.dart index 728a28b822..5b8fb1acc8 100644 --- a/lib/services/license/licenses/cc_by.dart +++ b/lib/services/license/licenses/cc_by.dart @@ -4,9 +4,9 @@ List ccLicenses = [ ccByLicenseMeta, ccByNCLicenseMeta, ccByNCNDLicenseMeta, - ccByNCSA, + ccByNCSAMeta, ccByNDLicenseMeta, - ccBySA, + ccBySAMeta, ]; const ccByLicenseMeta = LicenseMeta( @@ -34,7 +34,7 @@ const ccByNCNDLicenseMeta = LicenseMeta( version: '4.0', ); -const ccByNCSA = LicenseMeta( +const ccByNCSAMeta = LicenseMeta( licenseType: LicenseType.ccByNCSA, licenseDefinitionTxId: '2PO2MDRNZLJjgA_0hNGUAD7yXg9nneq-3fxTTLP-uo8', name: 'Attribution Non-Commercial Share-A-Like', @@ -50,7 +50,7 @@ const ccByNDLicenseMeta = LicenseMeta( version: '4.0', ); -const ccBySA = LicenseMeta( +const ccBySAMeta = LicenseMeta( licenseType: LicenseType.ccBySA, licenseDefinitionTxId: 'sKz-PZ96ApDoy5RTBspxhs1GP-cHommw4_9hEiZ6K3c', name: 'Attribution Share-A-Like', From 29cf89a8b901a5e77efcc384b4272ac891de20b5 Mon Sep 17 00:00:00 2001 From: Thiago Carvalho Date: Thu, 22 Feb 2024 15:57:15 -0300 Subject: [PATCH 03/16] Update cc_by.dart --- lib/services/license/licenses/cc_by.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/services/license/licenses/cc_by.dart b/lib/services/license/licenses/cc_by.dart index 5b8fb1acc8..84916596de 100644 --- a/lib/services/license/licenses/cc_by.dart +++ b/lib/services/license/licenses/cc_by.dart @@ -11,7 +11,7 @@ List ccLicenses = [ const ccByLicenseMeta = LicenseMeta( licenseType: LicenseType.ccBy, - licenseDefinitionTxId: 'rz2DNzn9pnYOU6049Wm6V7kr0BhyfWE6ZD_mqrXMv5A', + licenseDefinitionTxId: 'mSOFUrl5mUQvG7VBP36DD39kzJASv9FDe3GxHpcCvRA', name: 'Attribution', shortName: 'CC-BY', version: '4.0', From 2601219b97059a61cd6ea88f00e36c2b48022764 Mon Sep 17 00:00:00 2001 From: Thiago Carvalho Date: Thu, 22 Feb 2024 16:00:23 -0300 Subject: [PATCH 04/16] Update udl.dart --- lib/services/license/licenses/udl.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/services/license/licenses/udl.dart b/lib/services/license/licenses/udl.dart index 6c2c2d4684..f292d4422e 100644 --- a/lib/services/license/licenses/udl.dart +++ b/lib/services/license/licenses/udl.dart @@ -2,10 +2,10 @@ import '../license_state.dart'; const udlLicenseMeta = LicenseMeta( licenseType: LicenseType.udl, - licenseDefinitionTxId: 'yRj4a5KMctX_uOmKWCFJIjmY8DeJcusVk6-HzLiM_t8', + licenseDefinitionTxId: 'IVjAM1C3x3GFdc3t9EqMnbtGnpgTuJbaiYZa1lk09_8', name: 'Universal Data License', shortName: 'UDL', - version: '1.0', + version: '2.0', hasParams: true, ); From 3e52aae69303582d7092f2a51537ed5c4276a1ce Mon Sep 17 00:00:00 2001 From: Thiago Carvalho Date: Fri, 23 Feb 2024 09:36:02 -0300 Subject: [PATCH 05/16] feat(ANS-105) - remove version field - adds old license meta for ccBy and udl v1 --- .../fs_entry_info/fs_entry_info_cubit.dart | 1 - lib/entities/license_assertion.dart | 4 +++- lib/services/license/license_state.dart | 7 +++--- lib/services/license/licenses/cc_by.dart | 22 +++++++++++++------ lib/services/license/licenses/udl.dart | 11 +++++++++- 5 files changed, 32 insertions(+), 13 deletions(-) diff --git a/lib/blocs/fs_entry_info/fs_entry_info_cubit.dart b/lib/blocs/fs_entry_info/fs_entry_info_cubit.dart index 117c27c12f..ffd2130eb0 100644 --- a/lib/blocs/fs_entry_info/fs_entry_info_cubit.dart +++ b/lib/blocs/fs_entry_info/fs_entry_info_cubit.dart @@ -89,7 +89,6 @@ class FsEntryInfoCubit extends Cubit { licenseDefinitionTxId: '', name: 'Pending', shortName: 'Pending', - version: '', ), ); } diff --git a/lib/entities/license_assertion.dart b/lib/entities/license_assertion.dart index eb2d547e86..601b956620 100644 --- a/lib/entities/license_assertion.dart +++ b/lib/entities/license_assertion.dart @@ -1,3 +1,4 @@ +import 'package:ardrive/utils/logger.dart'; import 'package:ardrive_utils/ardrive_utils.dart'; import 'package:arweave/arweave.dart'; import 'package:drift/drift.dart'; @@ -53,7 +54,8 @@ class LicenseAssertionEntity with TransactionPropertiesMixin { DateTime.fromMillisecondsSinceEpoch(transaction.block!.timestamp); } return licenseAssertionEntity; - } catch (_) { + } catch (e, stacktrace) { + logger.e('Failed to parse license assertion transaction', e, stacktrace); throw LicenseAssertionTransactionParseException( transactionId: transaction.id, ); diff --git a/lib/services/license/license_state.dart b/lib/services/license/license_state.dart index d85fe563cc..54df9cb3e2 100644 --- a/lib/services/license/license_state.dart +++ b/lib/services/license/license_state.dart @@ -3,7 +3,9 @@ import 'package:equatable/equatable.dart'; enum LicenseType { udl, + udlV2, ccBy, + ccByV2, ccByNC, ccByNCND, ccByNCSA, @@ -22,7 +24,6 @@ class LicenseMeta extends Equatable { final String licenseDefinitionTxId; final String name; final String shortName; - final String version; final bool hasParams; const LicenseMeta({ @@ -30,7 +31,6 @@ class LicenseMeta extends Equatable { required this.licenseDefinitionTxId, required this.name, required this.shortName, - required this.version, this.hasParams = false, }); @@ -40,7 +40,6 @@ class LicenseMeta extends Equatable { licenseDefinitionTxId, name, shortName, - version, hasParams, ]; } @@ -56,7 +55,9 @@ class EmptyParams extends LicenseParams {} final licenseMetaMap = { LicenseType.udl: udlLicenseMeta, + LicenseType.udlV2: udlLicenseMetaV2, LicenseType.ccBy: ccByLicenseMeta, + LicenseType.ccByV2: ccByLicenseMetaV2, LicenseType.ccByNC: ccByNCLicenseMeta, LicenseType.ccByNCND: ccByNCNDLicenseMeta, LicenseType.ccByNCSA: ccByNCSAMeta, diff --git a/lib/services/license/licenses/cc_by.dart b/lib/services/license/licenses/cc_by.dart index 84916596de..750ac722d8 100644 --- a/lib/services/license/licenses/cc_by.dart +++ b/lib/services/license/licenses/cc_by.dart @@ -9,51 +9,59 @@ List ccLicenses = [ ccBySAMeta, ]; +// Version 4.0 const ccByLicenseMeta = LicenseMeta( + licenseType: LicenseType.ccBy, + licenseDefinitionTxId: 'rz2DNzn9pnYOU6049Wm6V7kr0BhyfWE6ZD_mqrXMv5A', + name: 'Attribution', + shortName: 'CC-BY', + hasParams: true, +); + +const ccByLicenseMetaV2 = LicenseMeta( licenseType: LicenseType.ccBy, licenseDefinitionTxId: 'mSOFUrl5mUQvG7VBP36DD39kzJASv9FDe3GxHpcCvRA', name: 'Attribution', shortName: 'CC-BY', - version: '4.0', hasParams: true, ); +// Version 4.0 const ccByNCLicenseMeta = LicenseMeta( licenseType: LicenseType.ccByNC, licenseDefinitionTxId: '9jG6a1fWgQ_wE4R6OGA2Xg9vGRAwpkrQIMC83nC3kvI', name: 'Attribution Non-Commercial', shortName: 'CC-BY-NC', - version: '4.0', ); +// Version 4.0 const ccByNCNDLicenseMeta = LicenseMeta( licenseType: LicenseType.ccByNCND, licenseDefinitionTxId: 'OlTlW1xEw75UC0cdmNqvxc3j6iAmFXrS4usWIBfu_3E', name: 'Attribution Non-Commercial No-Derivatives', shortName: 'CC-BY-NC-ND', - version: '4.0', ); +// Version 4.0 const ccByNCSAMeta = LicenseMeta( licenseType: LicenseType.ccByNCSA, - licenseDefinitionTxId: '2PO2MDRNZLJjgA_0hNGUAD7yXg9nneq-3fxTTLP-uo8', + licenseDefinitionTxId: 'c1TDjOvM0PiwLKOGMYNUOCfe2iO_yPiN1LTit78DZUM', name: 'Attribution Non-Commercial Share-A-Like', shortName: 'CC-BY-NC-SA', - version: '4.0', ); +// Version 4.0 const ccByNDLicenseMeta = LicenseMeta( licenseType: LicenseType.ccByND, licenseDefinitionTxId: 'XaIMRBMNqTUlHa_hzypkopfRFyAKqit-AWo-OxwIxoo', name: 'Attribution No-Derivatives', shortName: 'CC-BY-ND', - version: '4.0', ); +// Version 4.0 const ccBySAMeta = LicenseMeta( licenseType: LicenseType.ccBySA, licenseDefinitionTxId: 'sKz-PZ96ApDoy5RTBspxhs1GP-cHommw4_9hEiZ6K3c', name: 'Attribution Share-A-Like', shortName: 'CC-BY-SA', - version: '4.0', ); diff --git a/lib/services/license/licenses/udl.dart b/lib/services/license/licenses/udl.dart index f292d4422e..6d21301b59 100644 --- a/lib/services/license/licenses/udl.dart +++ b/lib/services/license/licenses/udl.dart @@ -1,11 +1,20 @@ import '../license_state.dart'; +// Version 0.2 +const udlLicenseMetaV2 = LicenseMeta( + licenseType: LicenseType.udl, + licenseDefinitionTxId: 'yRj4a5KMctX_uOmKWCFJIjmY8DeJcusVk6-HzLiM_t8', + name: 'Universal Data License', + shortName: 'UDL', + hasParams: true, +); + +// Version 0.1 const udlLicenseMeta = LicenseMeta( licenseType: LicenseType.udl, licenseDefinitionTxId: 'IVjAM1C3x3GFdc3t9EqMnbtGnpgTuJbaiYZa1lk09_8', name: 'Universal Data License', shortName: 'UDL', - version: '2.0', hasParams: true, ); From be8806d1044437158cf7bfd7802cfb4343d25a48 Mon Sep 17 00:00:00 2001 From: Thiago Carvalho Date: Fri, 23 Feb 2024 09:38:06 -0300 Subject: [PATCH 06/16] Update license_service.dart --- lib/services/license/license_service.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/services/license/license_service.dart b/lib/services/license/license_service.dart index e1a5dfd3b7..09cce79974 100644 --- a/lib/services/license/license_service.dart +++ b/lib/services/license/license_service.dart @@ -33,6 +33,7 @@ class LicenseService { case LicenseType.udl: return UdlLicenseParams.fromAdditionalTags(additionalTags ?? {}); case LicenseType.ccBy: + case LicenseType.ccByV2: case LicenseType.ccByNC: case LicenseType.ccByNCND: case LicenseType.ccByNCSA: From 7184d4ccd87952da827ae1d4f14c613331dacd93 Mon Sep 17 00:00:00 2001 From: Thiago Carvalho Date: Fri, 23 Feb 2024 09:48:48 -0300 Subject: [PATCH 07/16] test(ANS105) - implement unit test ensuring new license type will be included on the licenses map --- lib/services/license/license_state.dart | 30 +++++++++++-------- test/services/license/license_state_test.dart | 22 ++++++++++++++ 2 files changed, 39 insertions(+), 13 deletions(-) create mode 100644 test/services/license/license_state_test.dart diff --git a/lib/services/license/license_state.dart b/lib/services/license/license_state.dart index 54df9cb3e2..41d258c42d 100644 --- a/lib/services/license/license_state.dart +++ b/lib/services/license/license_state.dart @@ -1,6 +1,10 @@ -import 'package:ardrive/services/license/licenses/licenses.dart'; +import 'package:ardrive/services/license/license.dart'; import 'package:equatable/equatable.dart'; +/// Updating a license type version will require to update the corresponding [licenseMetaMap]. +/// The [licenseMetaMap] is used to map the license type to the corresponding [LicenseMeta]. +/// +/// Please ensure to update the [licenseMetaMap] when adding a new license type. enum LicenseType { udl, udlV2, @@ -19,6 +23,18 @@ enum LicenseCategory { udl, } +final licenseMetaMap = { + LicenseType.udl: udlLicenseMeta, + LicenseType.udlV2: udlLicenseMetaV2, + LicenseType.ccBy: ccByLicenseMeta, + LicenseType.ccByV2: ccByLicenseMetaV2, + LicenseType.ccByNC: ccByNCLicenseMeta, + LicenseType.ccByNCND: ccByNCNDLicenseMeta, + LicenseType.ccByNCSA: ccByNCSAMeta, + LicenseType.ccByND: ccByNDLicenseMeta, + LicenseType.ccBySA: ccBySAMeta, +}; + class LicenseMeta extends Equatable { final LicenseType licenseType; final String licenseDefinitionTxId; @@ -53,18 +69,6 @@ abstract class LicenseParams extends Equatable { class EmptyParams extends LicenseParams {} -final licenseMetaMap = { - LicenseType.udl: udlLicenseMeta, - LicenseType.udlV2: udlLicenseMetaV2, - LicenseType.ccBy: ccByLicenseMeta, - LicenseType.ccByV2: ccByLicenseMetaV2, - LicenseType.ccByNC: ccByNCLicenseMeta, - LicenseType.ccByNCND: ccByNCNDLicenseMeta, - LicenseType.ccByNCSA: ccByNCSAMeta, - LicenseType.ccByND: ccByNDLicenseMeta, - LicenseType.ccBySA: ccBySAMeta, -}; - final licenseCategoryNames = { LicenseCategory.udl: 'Universal Data License - UDL', LicenseCategory.cc: 'Creative Commons - CC', diff --git a/test/services/license/license_state_test.dart b/test/services/license/license_state_test.dart new file mode 100644 index 0000000000..2b3330ae18 --- /dev/null +++ b/test/services/license/license_state_test.dart @@ -0,0 +1,22 @@ +import 'package:ardrive/services/license/license_state.dart'; +import 'package:test/test.dart'; + +void main() { + group('License Meta Map', () { + test('should contain all LicenseType enum values', () { + // Iterate over all LicenseType values + for (var type in LicenseType.values) { + if (type == LicenseType.unknown) continue; + // Check if the licenseMetaMap contains the current enum value + expect(licenseMetaMap.containsKey(type), isTrue, + reason: 'Missing meta for $type'); + } + }); + + test('should not contain unknown LicenseType in map', () { + // Verify the 'unknown' LicenseType is not in the map + expect(licenseMetaMap.containsKey(LicenseType.unknown), isFalse, + reason: 'Map should not contain meta for unknown license type'); + }); + }); +} From 77e6d474b0c1b848ee5dc0ef2c9984edb227a6fc Mon Sep 17 00:00:00 2001 From: Thiago Carvalho Date: Fri, 23 Feb 2024 10:19:27 -0300 Subject: [PATCH 08/16] feat(ANS 105) - adds CC0 - implement tests to ensure we wont change old license meta's licenseTxId - implemented right license type - set as default the ccbyv2 --- .../fs_entry_license_bloc.dart | 2 +- lib/services/license/license_state.dart | 2 + lib/services/license/licenses/cc_by.dart | 14 ++++- lib/services/license/licenses/udl.dart | 10 ++-- test/services/license/license_state_test.dart | 56 +++++++++++++++++++ 5 files changed, 75 insertions(+), 9 deletions(-) diff --git a/lib/blocs/fs_entry_license/fs_entry_license_bloc.dart b/lib/blocs/fs_entry_license/fs_entry_license_bloc.dart index bf9ecd1456..d9907b93f6 100644 --- a/lib/blocs/fs_entry_license/fs_entry_license_bloc.dart +++ b/lib/blocs/fs_entry_license/fs_entry_license_bloc.dart @@ -349,7 +349,7 @@ class FsEntryLicenseBloc final ccForm = FormGroup({ 'ccAttributionField': FormControl( validators: [Validators.required], - value: ccByLicenseMeta, + value: ccByLicenseMetaV2, ), }); } diff --git a/lib/services/license/license_state.dart b/lib/services/license/license_state.dart index 41d258c42d..5f48dd9bfa 100644 --- a/lib/services/license/license_state.dart +++ b/lib/services/license/license_state.dart @@ -8,6 +8,7 @@ import 'package:equatable/equatable.dart'; enum LicenseType { udl, udlV2, + cc0, ccBy, ccByV2, ccByNC, @@ -26,6 +27,7 @@ enum LicenseCategory { final licenseMetaMap = { LicenseType.udl: udlLicenseMeta, LicenseType.udlV2: udlLicenseMetaV2, + LicenseType.cc0: cc0LicenseMeta, LicenseType.ccBy: ccByLicenseMeta, LicenseType.ccByV2: ccByLicenseMetaV2, LicenseType.ccByNC: ccByNCLicenseMeta, diff --git a/lib/services/license/licenses/cc_by.dart b/lib/services/license/licenses/cc_by.dart index 750ac722d8..18715cd831 100644 --- a/lib/services/license/licenses/cc_by.dart +++ b/lib/services/license/licenses/cc_by.dart @@ -1,7 +1,8 @@ import '../license_state.dart'; List ccLicenses = [ - ccByLicenseMeta, + cc0LicenseMeta, + ccByLicenseMetaV2, ccByNCLicenseMeta, ccByNCNDLicenseMeta, ccByNCSAMeta, @@ -9,6 +10,13 @@ List ccLicenses = [ ccBySAMeta, ]; +const cc0LicenseMeta = LicenseMeta( + licenseType: LicenseType.cc0, + licenseDefinitionTxId: 'nF6Mjy_Yy_Gv-DYLq7QPxz5PdXUQ4rtOpbJZdcaFEKw', + name: 'Public Domain', + shortName: 'CC0', +); + // Version 4.0 const ccByLicenseMeta = LicenseMeta( licenseType: LicenseType.ccBy, @@ -19,7 +27,7 @@ const ccByLicenseMeta = LicenseMeta( ); const ccByLicenseMetaV2 = LicenseMeta( - licenseType: LicenseType.ccBy, + licenseType: LicenseType.ccByV2, licenseDefinitionTxId: 'mSOFUrl5mUQvG7VBP36DD39kzJASv9FDe3GxHpcCvRA', name: 'Attribution', shortName: 'CC-BY', @@ -45,7 +53,7 @@ const ccByNCNDLicenseMeta = LicenseMeta( // Version 4.0 const ccByNCSAMeta = LicenseMeta( licenseType: LicenseType.ccByNCSA, - licenseDefinitionTxId: 'c1TDjOvM0PiwLKOGMYNUOCfe2iO_yPiN1LTit78DZUM', + licenseDefinitionTxId: '2PO2MDRNZLJjgA_0hNGUAD7yXg9nneq-3fxTTLP-uo8', name: 'Attribution Non-Commercial Share-A-Like', shortName: 'CC-BY-NC-SA', ); diff --git a/lib/services/license/licenses/udl.dart b/lib/services/license/licenses/udl.dart index 6d21301b59..7f223cce3f 100644 --- a/lib/services/license/licenses/udl.dart +++ b/lib/services/license/licenses/udl.dart @@ -1,7 +1,7 @@ import '../license_state.dart'; -// Version 0.2 -const udlLicenseMetaV2 = LicenseMeta( +// Version 0.1 +const udlLicenseMeta = LicenseMeta( licenseType: LicenseType.udl, licenseDefinitionTxId: 'yRj4a5KMctX_uOmKWCFJIjmY8DeJcusVk6-HzLiM_t8', name: 'Universal Data License', @@ -9,9 +9,9 @@ const udlLicenseMetaV2 = LicenseMeta( hasParams: true, ); -// Version 0.1 -const udlLicenseMeta = LicenseMeta( - licenseType: LicenseType.udl, +// Version 0.2 +const udlLicenseMetaV2 = LicenseMeta( + licenseType: LicenseType.udlV2, licenseDefinitionTxId: 'IVjAM1C3x3GFdc3t9EqMnbtGnpgTuJbaiYZa1lk09_8', name: 'Universal Data License', shortName: 'UDL', diff --git a/test/services/license/license_state_test.dart b/test/services/license/license_state_test.dart index 2b3330ae18..eacbf0d006 100644 --- a/test/services/license/license_state_test.dart +++ b/test/services/license/license_state_test.dart @@ -19,4 +19,60 @@ void main() { reason: 'Map should not contain meta for unknown license type'); }); }); + + group( + 'Ensure we wont change a licenseDefinitionTxId of existing licenses meta', + () { + test( + 'cc0', + () => expect(licenseMetaMap[LicenseType.cc0]!.licenseDefinitionTxId, + 'nF6Mjy_Yy_Gv-DYLq7QPxz5PdXUQ4rtOpbJZdcaFEKw')); + + test( + 'ccBy v1', + () => expect(licenseMetaMap[LicenseType.ccBy]!.licenseDefinitionTxId, + 'rz2DNzn9pnYOU6049Wm6V7kr0BhyfWE6ZD_mqrXMv5A')); + + test( + 'ccBy v2', + () => expect(licenseMetaMap[LicenseType.ccByV2]!.licenseDefinitionTxId, + 'mSOFUrl5mUQvG7VBP36DD39kzJASv9FDe3GxHpcCvRA')); + + test( + 'ccByNC', + () => expect(licenseMetaMap[LicenseType.ccByNC]!.licenseDefinitionTxId, + '9jG6a1fWgQ_wE4R6OGA2Xg9vGRAwpkrQIMC83nC3kvI')); + + test( + 'ccByNCND', + () => expect( + licenseMetaMap[LicenseType.ccByNCND]!.licenseDefinitionTxId, + 'OlTlW1xEw75UC0cdmNqvxc3j6iAmFXrS4usWIBfu_3E')); + + test( + 'ccByNCSA', + () => expect( + licenseMetaMap[LicenseType.ccByNCSA]!.licenseDefinitionTxId, + '2PO2MDRNZLJjgA_0hNGUAD7yXg9nneq-3fxTTLP-uo8')); + + test( + 'ccByND', + () => expect(licenseMetaMap[LicenseType.ccByND]!.licenseDefinitionTxId, + 'XaIMRBMNqTUlHa_hzypkopfRFyAKqit-AWo-OxwIxoo')); + + test( + 'ccBySA', + () => expect(licenseMetaMap[LicenseType.ccBySA]!.licenseDefinitionTxId, + 'sKz-PZ96ApDoy5RTBspxhs1GP-cHommw4_9hEiZ6K3c')); + + test( + 'udl', + () => expect(licenseMetaMap[LicenseType.udl]!.licenseDefinitionTxId, + 'yRj4a5KMctX_uOmKWCFJIjmY8DeJcusVk6-HzLiM_t8')); + + test( + 'udlV2', + () => expect(licenseMetaMap[LicenseType.udlV2]!.licenseDefinitionTxId, + 'IVjAM1C3x3GFdc3t9EqMnbtGnpgTuJbaiYZa1lk09_8')); + }); } From 53051dab64a04a421baaebb4569f8a5265eee7ca Mon Sep 17 00:00:00 2001 From: Thiago Carvalho Date: Fri, 23 Feb 2024 15:40:25 -0300 Subject: [PATCH 09/16] refactor(ANS105) - refactor bloc - remove unnecessary param on license meta --- .../fs_entry_license_bloc.dart | 353 ++++++++++-------- lib/services/license/licenses/cc_by.dart | 1 - 2 files changed, 198 insertions(+), 156 deletions(-) diff --git a/lib/blocs/fs_entry_license/fs_entry_license_bloc.dart b/lib/blocs/fs_entry_license/fs_entry_license_bloc.dart index d9907b93f6..78c7988ca4 100644 --- a/lib/blocs/fs_entry_license/fs_entry_license_bloc.dart +++ b/lib/blocs/fs_entry_license/fs_entry_license_bloc.dart @@ -21,25 +21,6 @@ part 'fs_entry_license_state.dart'; class FsEntryLicenseBloc extends Bloc { - final String driveId; - final List selectedItems; - - // We initialize with UDL license by default - LicenseMeta _selectedLicenseMeta = udlLicenseMeta; - LicenseMeta get selectedLicenseMeta => _selectedLicenseMeta; - - List? filesToLicense; - LicenseParams? licenseParams; - - final ArweaveService _arweave; - final TurboUploadService _turboUploadService; - final DriveDao _driveDao; - final ProfileCubit _profileCubit; - final ArDriveCrypto _crypto; - final LicenseService _licenseService; - - final List errorLog = []; - FsEntryLicenseBloc({ required this.driveId, required this.selectedItems, @@ -61,119 +42,202 @@ class FsEntryLicenseBloc addError(Exception('selectedItems cannot be empty')); } - final profile = _profileCubit.state as ProfileLoggedIn; + on(_onEvent, transformer: restartable()); + } - on( - (event, emit) async { - if (await _profileCubit.logoutIfWalletMismatch()) { - emit(const FsEntryLicenseWalletMismatch()); - return; - } + final String driveId; + final List selectedItems; - if (event is FsEntryLicenseInitial) { - filesToLicense = - await enumerateFiles(items: selectedItems, emit: emit); - if (filesToLicense!.isEmpty) { - emit(const FsEntryLicenseNoFiles()); - } else { - emit(const FsEntryLicenseSelecting()); - } - } + // We initialize with UDL license by default + LicenseMeta _selectedLicenseMeta = udlLicenseMeta; + LicenseMeta get selectedLicenseMeta => _selectedLicenseMeta; - if (event is FsEntryLicenseSelect) { - final licenseType = selectForm.control('licenseType').value; + List? filesToLicense; + LicenseParams? licenseParams; - switch (licenseType) { - case LicenseCategory.udl: - _selectedLicenseMeta = udlLicenseMeta; - break; - case LicenseCategory.cc: - _selectedLicenseMeta = ccByLicenseMeta; - break; - } + final ArweaveService _arweave; + final TurboUploadService _turboUploadService; + final DriveDao _driveDao; + final ProfileCubit _profileCubit; + final ArDriveCrypto _crypto; + final LicenseService _licenseService; - emit(const FsEntryLicenseConfiguring()); - } + final List errorLog = []; - if (event is FsEntryLicenseConfigurationBack) { - emit(const FsEntryLicenseSelecting()); - } + /// Form getters + FormGroup get selectForm => _selectForm; + FormGroup get udlForm => _udlForm; + FormGroup get ccForm => _ccForm; - if (event is FsEntryLicenseConfigurationSubmit) { - final licenseCategory = selectForm.control('licenseType').value; + // Forms + final _selectForm = FormGroup({ + 'licenseType': FormControl( + validators: [Validators.required], + value: LicenseCategory.udl, + ), + }); - if (licenseCategory == LicenseCategory.cc) { - _selectedLicenseMeta = ccForm.control('ccAttributionField').value; - } + final _udlForm = FormGroup({ + 'licenseFeeAmount': FormControl( + validators: [ + Validators.composeOR([ + Validators.pattern( + r'^\d+\.?\d*$', + validationMessage: 'Invalid amount', + ), + Validators.equals(''), + ]), + ], + ), + 'licenseFeeCurrency': FormControl( + validators: [Validators.required], + value: UdlCurrency.u, + ), + 'commercialUse': FormControl( + validators: [Validators.required], + value: UdlCommercialUse.unspecified, + ), + 'derivations': FormControl( + validators: [Validators.required], + value: UdlDerivation.unspecified, + ), + }); - if (_selectedLicenseMeta.licenseType == LicenseType.udl) { - licenseParams = await udlFormToLicenseParams(udlForm); - } else { - addError( - 'Unsupported license configuration: ${_selectedLicenseMeta.licenseType}'); - } + final _ccForm = FormGroup({ + 'ccAttributionField': FormControl( + validators: [Validators.required], + value: ccByLicenseMetaV2, + ), + }); - emit(const FsEntryLicenseReviewing()); - } + Future _onEvent( + FsEntryLicenseEvent event, + Emitter emit, + ) async { + if (event is FsEntryLicenseInitial) { + await _handleInitial(event, emit); + } else if (event is FsEntryLicenseSelect) { + await _handleSelect(event, emit); + } else if (event is FsEntryLicenseConfigurationSubmit) { + await _handleConfigurationSubmit(event, emit); + } else if (event is FsEntryLicenseReviewConfirm) { + await _handleReviewConfirm(event, emit); + } else if (event is FsEntryLicenseReviewBack) { + _handleReviewBack(event, emit); + } else if (event is FsEntryLicenseConfigurationBack) { + _handleConfigurationBack(event, emit); + } else if (event is FsEntryLicenseSuccessClose) { + _handleSuccessClose(event, emit); + } else if (event is FsEntryLicenseFailureTryAgain) { + _handleFailureTryAgain(event, emit); + } else { + addError('Unsupported event: ${event.runtimeType}'); + } + } - if (event is FsEntryLicenseReviewBack) { - if (_selectedLicenseMeta.hasParams) { - licenseParams = null; - emit(const FsEntryLicenseConfiguring()); - } else { - emit(const FsEntryLicenseSelecting()); - } - } + Future _handleInitial( + FsEntryLicenseInitial event, + Emitter emit, + ) async { + filesToLicense = await _enumerateFiles(items: selectedItems, emit: emit); + if (filesToLicense!.isEmpty) { + emit(const FsEntryLicenseNoFiles()); + } else { + emit(const FsEntryLicenseSelecting()); + } + } - if (event is FsEntryLicenseReviewConfirm) { - emit(const FsEntryLicenseLoadInProgress()); - try { - await licenseEntities( - profile: profile, - licenseMeta: _selectedLicenseMeta, - licenseParams: licenseParams, - ); - emit(const FsEntryLicenseSuccess()); - } catch (_, trace) { - addError('Error licensing entities', trace); - emit(const FsEntryLicenseFailure()); - } - } + Future _handleSelect( + FsEntryLicenseSelect event, + Emitter emit, + ) async { + final licenseType = selectForm.control('licenseType').value; + + switch (licenseType) { + case LicenseCategory.udl: + _selectedLicenseMeta = udlLicenseMeta; + break; + case LicenseCategory.cc: + _selectedLicenseMeta = ccByLicenseMeta; + break; + } - if (event is FsEntryLicenseSuccessClose) { - emit(const FsEntryLicenseComplete()); - } + emit(const FsEntryLicenseConfiguring()); + } - if (event is FsEntryLicenseFailureTryAgain) { - emit(const FsEntryLicenseReviewing()); - } - }, - transformer: restartable(), - ); + Future _handleConfigurationSubmit( + FsEntryLicenseConfigurationSubmit event, + Emitter emit, + ) async { + final licenseCategory = selectForm.control('licenseType').value; + + if (licenseCategory == LicenseCategory.cc) { + _selectedLicenseMeta = ccForm.control('ccAttributionField').value; + } + + if (_selectedLicenseMeta.licenseType == LicenseType.udl) { + licenseParams = await _udlFormToLicenseParams(udlForm); + } else { + addError( + 'Unsupported license configuration: ${_selectedLicenseMeta.licenseType}'); + } + + emit(const FsEntryLicenseReviewing()); } - Future udlFormToLicenseParams(FormGroup udlForm) async { - final String? licenseFeeAmountString = - udlForm.control('licenseFeeAmount').value; - final double? licenseFeeAmount = licenseFeeAmountString == null - ? null - : double.tryParse(licenseFeeAmountString); + Future _handleReviewConfirm( + FsEntryLicenseReviewConfirm event, + Emitter emit, + ) async { + emit(const FsEntryLicenseLoadInProgress()); + try { + final profile = _profileCubit.state as ProfileLoggedIn; + await _licenseEntities( + profile: profile, + licenseMeta: _selectedLicenseMeta, + licenseParams: licenseParams, + ); + emit(const FsEntryLicenseSuccess()); + } catch (_, trace) { + addError('Error licensing entities', trace); + emit(const FsEntryLicenseFailure()); + } + } - final UdlCurrency licenseFeeCurrency = - udlForm.control('licenseFeeCurrency').value; - final UdlCommercialUse commercialUse = - udlForm.control('commercialUse').value; - final UdlDerivation derivations = udlForm.control('derivations').value; + void _handleReviewBack( + FsEntryLicenseReviewBack event, + Emitter emit, + ) { + if (_selectedLicenseMeta.hasParams) { + licenseParams = null; + emit(const FsEntryLicenseConfiguring()); + } else { + emit(const FsEntryLicenseSelecting()); + } + } - return UdlLicenseParams( - licenseFeeAmount: licenseFeeAmount, - licenseFeeCurrency: licenseFeeCurrency, - commercialUse: commercialUse, - derivations: derivations, - ); + void _handleConfigurationBack( + FsEntryLicenseConfigurationBack event, + Emitter emit, + ) { + emit(const FsEntryLicenseSelecting()); } - Future> enumerateFiles({ + void _handleSuccessClose( + FsEntryLicenseSuccessClose event, + Emitter emit, + ) { + emit(const FsEntryLicenseComplete()); + } + + void _handleFailureTryAgain( + FsEntryLicenseFailureTryAgain event, + Emitter emit, + ) { + emit(const FsEntryLicenseReviewing()); + } + + Future> _enumerateFiles({ required List items, required Emitter emit, }) async { @@ -213,7 +277,7 @@ class FsEntryLicenseBloc return files.where((file) => file.pinnedDataOwnerAddress == null).toList(); } - Future licenseEntities({ + Future _licenseEntities({ required ProfileLoggedIn profile, required LicenseMeta licenseMeta, LicenseParams? licenseParams, @@ -305,51 +369,30 @@ class FsEntryLicenseBloc } } + Future _udlFormToLicenseParams(FormGroup udlForm) async { + final String? licenseFeeAmountString = + udlForm.control('licenseFeeAmount').value; + final double? licenseFeeAmount = licenseFeeAmountString == null + ? null + : double.tryParse(licenseFeeAmountString); + + final UdlCurrency licenseFeeCurrency = + udlForm.control('licenseFeeCurrency').value; + final UdlCommercialUse commercialUse = + udlForm.control('commercialUse').value; + final UdlDerivation derivations = udlForm.control('derivations').value; + + return UdlLicenseParams( + licenseFeeAmount: licenseFeeAmount, + licenseFeeCurrency: licenseFeeCurrency, + commercialUse: commercialUse, + derivations: derivations, + ); + } + @override void onError(Object error, StackTrace stackTrace) { errorLog.add(error.toString()); super.onError(error, stackTrace); } - - // Forms - - final selectForm = FormGroup({ - 'licenseType': FormControl( - validators: [Validators.required], - value: LicenseCategory.udl, - ), - }); - - final udlForm = FormGroup({ - 'licenseFeeAmount': FormControl( - validators: [ - Validators.composeOR([ - Validators.pattern( - r'^\d+\.?\d*$', - validationMessage: 'Invalid amount', - ), - Validators.equals(''), - ]), - ], - ), - 'licenseFeeCurrency': FormControl( - validators: [Validators.required], - value: UdlCurrency.u, - ), - 'commercialUse': FormControl( - validators: [Validators.required], - value: UdlCommercialUse.unspecified, - ), - 'derivations': FormControl( - validators: [Validators.required], - value: UdlDerivation.unspecified, - ), - }); - - final ccForm = FormGroup({ - 'ccAttributionField': FormControl( - validators: [Validators.required], - value: ccByLicenseMetaV2, - ), - }); } diff --git a/lib/services/license/licenses/cc_by.dart b/lib/services/license/licenses/cc_by.dart index 18715cd831..4a34c3ffb3 100644 --- a/lib/services/license/licenses/cc_by.dart +++ b/lib/services/license/licenses/cc_by.dart @@ -23,7 +23,6 @@ const ccByLicenseMeta = LicenseMeta( licenseDefinitionTxId: 'rz2DNzn9pnYOU6049Wm6V7kr0BhyfWE6ZD_mqrXMv5A', name: 'Attribution', shortName: 'CC-BY', - hasParams: true, ); const ccByLicenseMetaV2 = LicenseMeta( From 29ca1e080adc5fc3089108cbf5d8f5484cd59b7b Mon Sep 17 00:00:00 2001 From: Thiago Carvalho Date: Fri, 23 Feb 2024 16:25:43 -0300 Subject: [PATCH 10/16] fix(udl) add udl v2 and set v2 as default --- lib/blocs/fs_entry_license/fs_entry_license_bloc.dart | 2 +- lib/components/fs_entry_license_form.dart | 2 +- lib/entities/license_assertion.dart | 5 ++++- lib/services/license/license_service.dart | 1 + 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/blocs/fs_entry_license/fs_entry_license_bloc.dart b/lib/blocs/fs_entry_license/fs_entry_license_bloc.dart index 78c7988ca4..076925efa8 100644 --- a/lib/blocs/fs_entry_license/fs_entry_license_bloc.dart +++ b/lib/blocs/fs_entry_license/fs_entry_license_bloc.dart @@ -175,7 +175,7 @@ class FsEntryLicenseBloc _selectedLicenseMeta = ccForm.control('ccAttributionField').value; } - if (_selectedLicenseMeta.licenseType == LicenseType.udl) { + if (_selectedLicenseMeta.licenseType == LicenseType.udlV2) { licenseParams = await _udlFormToLicenseParams(udlForm); } else { addError( diff --git a/lib/components/fs_entry_license_form.dart b/lib/components/fs_entry_license_form.dart index 446cc23854..c61a684816 100644 --- a/lib/components/fs_entry_license_form.dart +++ b/lib/components/fs_entry_license_form.dart @@ -322,7 +322,7 @@ class _FsEntryLicenseFormState extends State { .filesToLicense!), const SizedBox(height: 16), const Divider(height: 24), - licenseType == LicenseType.udl + licenseType == LicenseType.udlV2 ? UdlParamsForm( onChangeLicenseFee: () { setState(() {}); diff --git a/lib/entities/license_assertion.dart b/lib/entities/license_assertion.dart index 601b956620..ff4122af7e 100644 --- a/lib/entities/license_assertion.dart +++ b/lib/entities/license_assertion.dart @@ -55,7 +55,10 @@ class LicenseAssertionEntity with TransactionPropertiesMixin { } return licenseAssertionEntity; } catch (e, stacktrace) { - logger.e('Failed to parse license assertion transaction', e, stacktrace); + logger.e( + 'Failed to parse license assertion transaction. licenseDefinitionTxId: ${transaction.tags.firstWhere((tag) => tag.name == LicenseTag.licenseDefinitionTxId).value}', + e, + stacktrace); throw LicenseAssertionTransactionParseException( transactionId: transaction.id, ); diff --git a/lib/services/license/license_service.dart b/lib/services/license/license_service.dart index 09cce79974..bb6f32c5dc 100644 --- a/lib/services/license/license_service.dart +++ b/lib/services/license/license_service.dart @@ -31,6 +31,7 @@ class LicenseService { }) { switch (licenseType) { case LicenseType.udl: + case LicenseType.udlV2: return UdlLicenseParams.fromAdditionalTags(additionalTags ?? {}); case LicenseType.ccBy: case LicenseType.ccByV2: From 14032a9d3d2167d7cf553143255a5806a62d01b9 Mon Sep 17 00:00:00 2001 From: Thiago Carvalho Date: Mon, 26 Feb 2024 10:45:10 -0300 Subject: [PATCH 11/16] Update fs_entry_move_bloc.dart - copy the selected item list instead of passing the reference of the original list. --- .../fs_entry_move/fs_entry_move_bloc.dart | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/blocs/fs_entry_move/fs_entry_move_bloc.dart b/lib/blocs/fs_entry_move/fs_entry_move_bloc.dart index 340055cacf..b8b29d1354 100644 --- a/lib/blocs/fs_entry_move/fs_entry_move_bloc.dart +++ b/lib/blocs/fs_entry_move/fs_entry_move_bloc.dart @@ -20,7 +20,7 @@ part 'fs_entry_move_state.dart'; class FsEntryMoveBloc extends Bloc { final String driveId; - final List selectedItems; + final List _selectedItems; final ArweaveService _arweave; final TurboUploadService _turboUploadService; @@ -32,7 +32,7 @@ class FsEntryMoveBloc extends Bloc { FsEntryMoveBloc({ required this.driveId, - required this.selectedItems, + required List selectedItems, required ArweaveService arweave, required TurboUploadService turboUploadService, required DriveDao driveDao, @@ -41,7 +41,8 @@ class FsEntryMoveBloc extends Bloc { required ArDriveCrypto crypto, required DriveDetailCubit driveDetailCubit, Platform platform = const LocalPlatform(), - }) : _arweave = arweave, + }) : _selectedItems = List.from(selectedItems, growable: false), + _arweave = arweave, _turboUploadService = turboUploadService, _driveDao = driveDao, _profileCubit = profileCubit, @@ -49,7 +50,7 @@ class FsEntryMoveBloc extends Bloc { _syncCubit = syncCubit, _crypto = crypto, super(const FsEntryMoveLoadInProgress()) { - if (selectedItems.isEmpty) { + if (_selectedItems.isEmpty) { addError(Exception('selectedItems cannot be empty')); } @@ -93,7 +94,7 @@ class FsEntryMoveBloc extends Bloc { FsEntryMoveNameConflict( conflictingItems: conflictingItems, folderInView: folderInView, - allItems: selectedItems, + allItems: _selectedItems, ), ); } @@ -145,7 +146,7 @@ class FsEntryMoveBloc extends Bloc { onData: (FolderWithContents folderWithContents) => FsEntryMoveLoadSuccess( viewingRootFolder: folderWithContents.folder.parentFolderId == null, viewingFolder: folderWithContents, - itemsToMove: selectedItems, + itemsToMove: _selectedItems, ), ); } @@ -156,7 +157,7 @@ class FsEntryMoveBloc extends Bloc { }) async { final conflictingItems = []; try { - for (var itemToMove in selectedItems) { + for (var itemToMove in _selectedItems) { final entityWithSameNameExists = await _driveDao.doesEntityWithNameExist( name: itemToMove.name, @@ -184,7 +185,7 @@ class FsEntryMoveBloc extends Bloc { final isShowingHiddenItems = (_driveDetailCubit.state as DriveDetailLoadSuccess) .isShowingHiddenFiles; - final files = selectedItems.whereType().toList(); + final files = _selectedItems.whereType().toList(); if (!isShowingHiddenItems) { files.removeWhere((element) => element.isHidden); @@ -198,7 +199,7 @@ class FsEntryMoveBloc extends Bloc { files.clear(); - final folders = selectedItems.whereType().toList(); + final folders = _selectedItems.whereType().toList(); if (!isShowingHiddenItems) { folders.removeWhere((element) => element.isHidden); From 86aff61b9c3911b78a9e7fced26c91ed5958521b Mon Sep 17 00:00:00 2001 From: Thiago Carvalho Date: Mon, 26 Feb 2024 12:47:25 -0300 Subject: [PATCH 12/16] fix(cc0 and udlv2) - selected as default the ccBy v2 and udl v2 - add cc0 on the method to verify the license type --- lib/blocs/fs_entry_license/fs_entry_license_bloc.dart | 4 ++-- lib/components/fs_entry_license_form.dart | 2 +- lib/services/license/license_service.dart | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/blocs/fs_entry_license/fs_entry_license_bloc.dart b/lib/blocs/fs_entry_license/fs_entry_license_bloc.dart index 076925efa8..95347a22de 100644 --- a/lib/blocs/fs_entry_license/fs_entry_license_bloc.dart +++ b/lib/blocs/fs_entry_license/fs_entry_license_bloc.dart @@ -155,10 +155,10 @@ class FsEntryLicenseBloc switch (licenseType) { case LicenseCategory.udl: - _selectedLicenseMeta = udlLicenseMeta; + _selectedLicenseMeta = udlLicenseMetaV2; break; case LicenseCategory.cc: - _selectedLicenseMeta = ccByLicenseMeta; + _selectedLicenseMeta = ccByLicenseMetaV2; break; } diff --git a/lib/components/fs_entry_license_form.dart b/lib/components/fs_entry_license_form.dart index c61a684816..f92b9f69a0 100644 --- a/lib/components/fs_entry_license_form.dart +++ b/lib/components/fs_entry_license_form.dart @@ -330,7 +330,7 @@ class _FsEntryLicenseFormState extends State { formGroup: context.watch().udlForm, ) - : licenseType == LicenseType.ccBy + : licenseType == LicenseType.ccByV2 ? CcParamsForm( formGroup: context .watch() diff --git a/lib/services/license/license_service.dart b/lib/services/license/license_service.dart index bb6f32c5dc..8600c3240e 100644 --- a/lib/services/license/license_service.dart +++ b/lib/services/license/license_service.dart @@ -33,6 +33,7 @@ class LicenseService { case LicenseType.udl: case LicenseType.udlV2: return UdlLicenseParams.fromAdditionalTags(additionalTags ?? {}); + case LicenseType.cc0: case LicenseType.ccBy: case LicenseType.ccByV2: case LicenseType.ccByNC: From 5ebac25c501edb517347c9aaa146f4eaf95938ae Mon Sep 17 00:00:00 2001 From: Thiago Carvalho Date: Mon, 26 Feb 2024 15:14:24 -0300 Subject: [PATCH 13/16] add public domain as default and fixes the modal title --- lib/blocs/fs_entry_license/fs_entry_license_bloc.dart | 2 +- lib/components/fs_entry_license_form.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/blocs/fs_entry_license/fs_entry_license_bloc.dart b/lib/blocs/fs_entry_license/fs_entry_license_bloc.dart index 95347a22de..ee59fe792b 100644 --- a/lib/blocs/fs_entry_license/fs_entry_license_bloc.dart +++ b/lib/blocs/fs_entry_license/fs_entry_license_bloc.dart @@ -106,7 +106,7 @@ class FsEntryLicenseBloc final _ccForm = FormGroup({ 'ccAttributionField': FormControl( validators: [Validators.required], - value: ccByLicenseMetaV2, + value: cc0LicenseMeta, ), }); diff --git a/lib/components/fs_entry_license_form.dart b/lib/components/fs_entry_license_form.dart index f92b9f69a0..1018dc310a 100644 --- a/lib/components/fs_entry_license_form.dart +++ b/lib/components/fs_entry_license_form.dart @@ -309,7 +309,7 @@ class _FsEntryLicenseFormState extends State { return ArDriveScrollBar( child: SingleChildScrollView( child: ArDriveStandardModal( - title: 'Configure ${licenseMeta.name}', + title: 'Configure Creative Commons License', width: kLargeDialogWidth, content: Column( mainAxisSize: MainAxisSize.min, From 0849253ee08ef03efde6ceebfd3991771b318214 Mon Sep 17 00:00:00 2001 From: Thiago Carvalho Date: Mon, 26 Feb 2024 15:41:40 -0300 Subject: [PATCH 14/16] Update details_panel.dart fixes the display of the activity panel --- lib/components/details_panel.dart | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/components/details_panel.dart b/lib/components/details_panel.dart index a484d517b7..5bed5b0c89 100644 --- a/lib/components/details_panel.dart +++ b/lib/components/details_panel.dart @@ -1039,10 +1039,10 @@ class DetailsPanelItem extends StatelessWidget { children: [ Flexible( child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.max, children: [ - Flexible( + Expanded( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, @@ -1051,7 +1051,7 @@ class DetailsPanelItem extends StatelessWidget { child: Text( itemTitle, style: ArDriveTypography.body.buttonNormalRegular(), - maxLines: 2, + maxLines: 4, ), ), if (itemSubtitle != null) @@ -1062,7 +1062,11 @@ class DetailsPanelItem extends StatelessWidget { ], ), ), - if (leading != null) Flexible(child: leading!), + if (leading != null) + Padding( + padding: const EdgeInsets.only(left: 8.0), + child: leading!, + ), ], ), ), From b41e51ed208a08f9cc2fc5a56d235e5e6b86e1a9 Mon Sep 17 00:00:00 2001 From: Thiago Carvalho Date: Tue, 27 Feb 2024 12:08:30 -0300 Subject: [PATCH 15/16] Update fs_entry_license_form.dart --- lib/components/fs_entry_license_form.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/components/fs_entry_license_form.dart b/lib/components/fs_entry_license_form.dart index 1018dc310a..76d823f52c 100644 --- a/lib/components/fs_entry_license_form.dart +++ b/lib/components/fs_entry_license_form.dart @@ -306,10 +306,15 @@ class _FsEntryLicenseFormState extends State { .read() .selectedLicenseMeta .licenseType; + final modalTitle = licenseType == LicenseType.udlV2 + ? 'Configure Universal Data License' + : licenseType == LicenseType.ccByV2 + ? 'Configure Creative Commons License' + : 'Unsupported license type'; return ArDriveScrollBar( child: SingleChildScrollView( child: ArDriveStandardModal( - title: 'Configure Creative Commons License', + title: modalTitle, width: kLargeDialogWidth, content: Column( mainAxisSize: MainAxisSize.min, From d6e31c8d77b85f9aa506a40f9f6d8d4bff670839 Mon Sep 17 00:00:00 2001 From: Thiago Carvalho Date: Tue, 27 Feb 2024 13:46:00 -0300 Subject: [PATCH 16/16] bump version and release notes --- android/fastlane/metadata/android/en-US/changelogs/111.txt | 3 +++ pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 android/fastlane/metadata/android/en-US/changelogs/111.txt diff --git a/android/fastlane/metadata/android/en-US/changelogs/111.txt b/android/fastlane/metadata/android/en-US/changelogs/111.txt new file mode 100644 index 0000000000..09abeaa676 --- /dev/null +++ b/android/fastlane/metadata/android/en-US/changelogs/111.txt @@ -0,0 +1,3 @@ +- Updates user interface to include new Creative Commons license options: CC0, CC-BY-NC, CC-BY-NC-ND, CC-BY-NC-SA, CC-BY-ND, and CC-BY-SA. +- Fixes issue with moving files in private drives +- Fixes issue displaying long content in the activity section of the details panel \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index f16538d0cf..84a039995d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: Secure, permanent storage publish_to: 'none' -version: 2.35.0 +version: 2.36.0 environment: sdk: '>=3.0.2 <4.0.0'