feat(publish): a release keeps its colour, and a description for Explore
Publishing takes a description for Explore (rich text, as its JSON), which Explore shows in place of the studyset's own: a studyset may introduce itself to strangers otherwise than in the library, where its author may leave it empty. Without one, as from older apps, the studyset's description as before; one that isn't rich text, or over 100,000 characters, counts as none. A release also keeps the studyset's colour, which Explore gives out with it; releases before have none. The publication's releases say their description, for the next publish to start from. Migration release-colour adds a nullable column: ALTER TABLE "studyset_release" ADD COLUMN "colorName" text; the server before it reads and writes the table as ever. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
d5fe51b296
commit
fe6b1f5ed1
21 changed files with 14958 additions and 15 deletions
|
|
@ -746,6 +746,11 @@ class EndpointPublish extends _isc.EndpointRef {
|
||||||
/// Publishes your studyset [studysetId] with everything inside it as its
|
/// Publishes your studyset [studysetId] with everything inside it as its
|
||||||
/// next release, [notes] saying what changed; at the first, under the
|
/// next release, [notes] saying what changed; at the first, under the
|
||||||
/// name [slug] in its location, which stays its name from then on.
|
/// name [slug] in its location, which stays its name from then on.
|
||||||
|
/// [description] (rich text, as its JSON) is what Explore shows of it,
|
||||||
|
/// when given: a studyset may introduce itself to strangers otherwise
|
||||||
|
/// than in the library (decided 2026-10-14); without, the studyset's
|
||||||
|
/// own description. One that isn't rich text, or is larger than
|
||||||
|
/// [maxExploreDescription], counts as not given.
|
||||||
///
|
///
|
||||||
/// Throws [PublishRefusedException] when it can't be published (see
|
/// Throws [PublishRefusedException] when it can't be published (see
|
||||||
/// [PublishRefusal]), and [NotFoundException] for a studyset that isn't
|
/// [PublishRefusal]), and [NotFoundException] for a studyset that isn't
|
||||||
|
|
@ -753,14 +758,16 @@ class EndpointPublish extends _isc.EndpointRef {
|
||||||
_ida.Future<_ij0cqo11.Publication> publish(
|
_ida.Future<_ij0cqo11.Publication> publish(
|
||||||
_isc.UuidValue studysetId,
|
_isc.UuidValue studysetId,
|
||||||
String slug,
|
String slug,
|
||||||
String notes,
|
String notes, {
|
||||||
) => caller.callServerEndpoint<_ij0cqo11.Publication>(
|
String? description,
|
||||||
|
}) => caller.callServerEndpoint<_ij0cqo11.Publication>(
|
||||||
'publish',
|
'publish',
|
||||||
'publish',
|
'publish',
|
||||||
{
|
{
|
||||||
'studysetId': studysetId,
|
'studysetId': studysetId,
|
||||||
'slug': slug,
|
'slug': slug,
|
||||||
'notes': notes,
|
'notes': notes,
|
||||||
|
'description': description,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ abstract class ExploreEntry
|
||||||
this.frontLanguage,
|
this.frontLanguage,
|
||||||
this.backLanguage,
|
this.backLanguage,
|
||||||
this.basedOn,
|
this.basedOn,
|
||||||
|
this.colorName,
|
||||||
required this.license,
|
required this.license,
|
||||||
this.licenseHolder,
|
this.licenseHolder,
|
||||||
this.licenseYear,
|
this.licenseYear,
|
||||||
|
|
@ -57,6 +58,7 @@ abstract class ExploreEntry
|
||||||
String? frontLanguage,
|
String? frontLanguage,
|
||||||
String? backLanguage,
|
String? backLanguage,
|
||||||
String? basedOn,
|
String? basedOn,
|
||||||
|
String? colorName,
|
||||||
required String license,
|
required String license,
|
||||||
String? licenseHolder,
|
String? licenseHolder,
|
||||||
int? licenseYear,
|
int? licenseYear,
|
||||||
|
|
@ -88,6 +90,7 @@ abstract class ExploreEntry
|
||||||
frontLanguage: jsonSerialization['frontLanguage'] as String?,
|
frontLanguage: jsonSerialization['frontLanguage'] as String?,
|
||||||
backLanguage: jsonSerialization['backLanguage'] as String?,
|
backLanguage: jsonSerialization['backLanguage'] as String?,
|
||||||
basedOn: jsonSerialization['basedOn'] as String?,
|
basedOn: jsonSerialization['basedOn'] as String?,
|
||||||
|
colorName: jsonSerialization['colorName'] as String?,
|
||||||
license: jsonSerialization['license'] as String,
|
license: jsonSerialization['license'] as String,
|
||||||
licenseHolder: jsonSerialization['licenseHolder'] as String?,
|
licenseHolder: jsonSerialization['licenseHolder'] as String?,
|
||||||
licenseYear: jsonSerialization['licenseYear'] as int?,
|
licenseYear: jsonSerialization['licenseYear'] as int?,
|
||||||
|
|
@ -135,6 +138,9 @@ abstract class ExploreEntry
|
||||||
/// Where the published studyset it is a copy of is, for a copy.
|
/// Where the published studyset it is a copy of is, for a copy.
|
||||||
String? basedOn;
|
String? basedOn;
|
||||||
|
|
||||||
|
/// Its colour, by the app palette's name for it; null for none.
|
||||||
|
String? colorName;
|
||||||
|
|
||||||
/// Its licence and credits.
|
/// Its licence and credits.
|
||||||
String license;
|
String license;
|
||||||
|
|
||||||
|
|
@ -175,6 +181,7 @@ abstract class ExploreEntry
|
||||||
String? frontLanguage,
|
String? frontLanguage,
|
||||||
String? backLanguage,
|
String? backLanguage,
|
||||||
String? basedOn,
|
String? basedOn,
|
||||||
|
String? colorName,
|
||||||
String? license,
|
String? license,
|
||||||
String? licenseHolder,
|
String? licenseHolder,
|
||||||
int? licenseYear,
|
int? licenseYear,
|
||||||
|
|
@ -203,6 +210,7 @@ abstract class ExploreEntry
|
||||||
if (frontLanguage != null) 'frontLanguage': frontLanguage,
|
if (frontLanguage != null) 'frontLanguage': frontLanguage,
|
||||||
if (backLanguage != null) 'backLanguage': backLanguage,
|
if (backLanguage != null) 'backLanguage': backLanguage,
|
||||||
if (basedOn != null) 'basedOn': basedOn,
|
if (basedOn != null) 'basedOn': basedOn,
|
||||||
|
if (colorName != null) 'colorName': colorName,
|
||||||
'license': license,
|
'license': license,
|
||||||
if (licenseHolder != null) 'licenseHolder': licenseHolder,
|
if (licenseHolder != null) 'licenseHolder': licenseHolder,
|
||||||
if (licenseYear != null) 'licenseYear': licenseYear,
|
if (licenseYear != null) 'licenseYear': licenseYear,
|
||||||
|
|
@ -233,6 +241,7 @@ abstract class ExploreEntry
|
||||||
if (frontLanguage != null) 'frontLanguage': frontLanguage,
|
if (frontLanguage != null) 'frontLanguage': frontLanguage,
|
||||||
if (backLanguage != null) 'backLanguage': backLanguage,
|
if (backLanguage != null) 'backLanguage': backLanguage,
|
||||||
if (basedOn != null) 'basedOn': basedOn,
|
if (basedOn != null) 'basedOn': basedOn,
|
||||||
|
if (colorName != null) 'colorName': colorName,
|
||||||
'license': license,
|
'license': license,
|
||||||
if (licenseHolder != null) 'licenseHolder': licenseHolder,
|
if (licenseHolder != null) 'licenseHolder': licenseHolder,
|
||||||
if (licenseYear != null) 'licenseYear': licenseYear,
|
if (licenseYear != null) 'licenseYear': licenseYear,
|
||||||
|
|
@ -269,6 +278,7 @@ class _ExploreEntryImpl extends ExploreEntry {
|
||||||
String? frontLanguage,
|
String? frontLanguage,
|
||||||
String? backLanguage,
|
String? backLanguage,
|
||||||
String? basedOn,
|
String? basedOn,
|
||||||
|
String? colorName,
|
||||||
required String license,
|
required String license,
|
||||||
String? licenseHolder,
|
String? licenseHolder,
|
||||||
int? licenseYear,
|
int? licenseYear,
|
||||||
|
|
@ -293,6 +303,7 @@ class _ExploreEntryImpl extends ExploreEntry {
|
||||||
frontLanguage: frontLanguage,
|
frontLanguage: frontLanguage,
|
||||||
backLanguage: backLanguage,
|
backLanguage: backLanguage,
|
||||||
basedOn: basedOn,
|
basedOn: basedOn,
|
||||||
|
colorName: colorName,
|
||||||
license: license,
|
license: license,
|
||||||
licenseHolder: licenseHolder,
|
licenseHolder: licenseHolder,
|
||||||
licenseYear: licenseYear,
|
licenseYear: licenseYear,
|
||||||
|
|
@ -323,6 +334,7 @@ class _ExploreEntryImpl extends ExploreEntry {
|
||||||
Object? frontLanguage = _Undefined,
|
Object? frontLanguage = _Undefined,
|
||||||
Object? backLanguage = _Undefined,
|
Object? backLanguage = _Undefined,
|
||||||
Object? basedOn = _Undefined,
|
Object? basedOn = _Undefined,
|
||||||
|
Object? colorName = _Undefined,
|
||||||
String? license,
|
String? license,
|
||||||
Object? licenseHolder = _Undefined,
|
Object? licenseHolder = _Undefined,
|
||||||
Object? licenseYear = _Undefined,
|
Object? licenseYear = _Undefined,
|
||||||
|
|
@ -352,6 +364,7 @@ class _ExploreEntryImpl extends ExploreEntry {
|
||||||
: this.frontLanguage,
|
: this.frontLanguage,
|
||||||
backLanguage: backLanguage is String? ? backLanguage : this.backLanguage,
|
backLanguage: backLanguage is String? ? backLanguage : this.backLanguage,
|
||||||
basedOn: basedOn is String? ? basedOn : this.basedOn,
|
basedOn: basedOn is String? ? basedOn : this.basedOn,
|
||||||
|
colorName: colorName is String? ? colorName : this.colorName,
|
||||||
license: license ?? this.license,
|
license: license ?? this.license,
|
||||||
licenseHolder: licenseHolder is String?
|
licenseHolder: licenseHolder is String?
|
||||||
? licenseHolder
|
? licenseHolder
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ abstract class ReleaseInfo
|
||||||
required this.pathCount,
|
required this.pathCount,
|
||||||
required this.pdfCount,
|
required this.pdfCount,
|
||||||
required this.releasedAt,
|
required this.releasedAt,
|
||||||
|
this.description,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory ReleaseInfo({
|
factory ReleaseInfo({
|
||||||
|
|
@ -37,6 +38,7 @@ abstract class ReleaseInfo
|
||||||
required int pathCount,
|
required int pathCount,
|
||||||
required int pdfCount,
|
required int pdfCount,
|
||||||
required DateTime releasedAt,
|
required DateTime releasedAt,
|
||||||
|
String? description,
|
||||||
}) = _ReleaseInfoImpl;
|
}) = _ReleaseInfoImpl;
|
||||||
|
|
||||||
factory ReleaseInfo.fromJson(Map<String, dynamic> jsonSerialization) {
|
factory ReleaseInfo.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||||
|
|
@ -52,6 +54,7 @@ abstract class ReleaseInfo
|
||||||
releasedAt: _isc.DateTimeJsonExtension.fromJson(
|
releasedAt: _isc.DateTimeJsonExtension.fromJson(
|
||||||
jsonSerialization['releasedAt'],
|
jsonSerialization['releasedAt'],
|
||||||
),
|
),
|
||||||
|
description: jsonSerialization['description'] as String?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -77,6 +80,10 @@ abstract class ReleaseInfo
|
||||||
/// When it was released, in UTC.
|
/// When it was released, in UTC.
|
||||||
DateTime releasedAt;
|
DateTime releasedAt;
|
||||||
|
|
||||||
|
/// What Explore shows of it as its description (rich text, as its JSON);
|
||||||
|
/// null for none.
|
||||||
|
String? description;
|
||||||
|
|
||||||
/// Returns a shallow copy of this [ReleaseInfo]
|
/// Returns a shallow copy of this [ReleaseInfo]
|
||||||
/// with some or all fields replaced by the given arguments.
|
/// with some or all fields replaced by the given arguments.
|
||||||
@_isc.useResult
|
@_isc.useResult
|
||||||
|
|
@ -90,6 +97,7 @@ abstract class ReleaseInfo
|
||||||
int? pathCount,
|
int? pathCount,
|
||||||
int? pdfCount,
|
int? pdfCount,
|
||||||
DateTime? releasedAt,
|
DateTime? releasedAt,
|
||||||
|
String? description,
|
||||||
});
|
});
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
|
|
@ -104,6 +112,7 @@ abstract class ReleaseInfo
|
||||||
'pathCount': pathCount,
|
'pathCount': pathCount,
|
||||||
'pdfCount': pdfCount,
|
'pdfCount': pdfCount,
|
||||||
'releasedAt': releasedAt.toJson(),
|
'releasedAt': releasedAt.toJson(),
|
||||||
|
if (description != null) 'description': description,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -120,6 +129,7 @@ abstract class ReleaseInfo
|
||||||
'pathCount': pathCount,
|
'pathCount': pathCount,
|
||||||
'pdfCount': pdfCount,
|
'pdfCount': pdfCount,
|
||||||
'releasedAt': releasedAt.toJson(),
|
'releasedAt': releasedAt.toJson(),
|
||||||
|
if (description != null) 'description': description,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -129,6 +139,8 @@ abstract class ReleaseInfo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _Undefined {}
|
||||||
|
|
||||||
class _ReleaseInfoImpl extends ReleaseInfo {
|
class _ReleaseInfoImpl extends ReleaseInfo {
|
||||||
_ReleaseInfoImpl({
|
_ReleaseInfoImpl({
|
||||||
required int version,
|
required int version,
|
||||||
|
|
@ -140,6 +152,7 @@ class _ReleaseInfoImpl extends ReleaseInfo {
|
||||||
required int pathCount,
|
required int pathCount,
|
||||||
required int pdfCount,
|
required int pdfCount,
|
||||||
required DateTime releasedAt,
|
required DateTime releasedAt,
|
||||||
|
String? description,
|
||||||
}) : super._(
|
}) : super._(
|
||||||
version: version,
|
version: version,
|
||||||
notes: notes,
|
notes: notes,
|
||||||
|
|
@ -150,6 +163,7 @@ class _ReleaseInfoImpl extends ReleaseInfo {
|
||||||
pathCount: pathCount,
|
pathCount: pathCount,
|
||||||
pdfCount: pdfCount,
|
pdfCount: pdfCount,
|
||||||
releasedAt: releasedAt,
|
releasedAt: releasedAt,
|
||||||
|
description: description,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Returns a shallow copy of this [ReleaseInfo]
|
/// Returns a shallow copy of this [ReleaseInfo]
|
||||||
|
|
@ -166,6 +180,7 @@ class _ReleaseInfoImpl extends ReleaseInfo {
|
||||||
int? pathCount,
|
int? pathCount,
|
||||||
int? pdfCount,
|
int? pdfCount,
|
||||||
DateTime? releasedAt,
|
DateTime? releasedAt,
|
||||||
|
Object? description = _Undefined,
|
||||||
}) {
|
}) {
|
||||||
return ReleaseInfo(
|
return ReleaseInfo(
|
||||||
version: version ?? this.version,
|
version: version ?? this.version,
|
||||||
|
|
@ -177,6 +192,7 @@ class _ReleaseInfoImpl extends ReleaseInfo {
|
||||||
pathCount: pathCount ?? this.pathCount,
|
pathCount: pathCount ?? this.pathCount,
|
||||||
pdfCount: pdfCount ?? this.pdfCount,
|
pdfCount: pdfCount ?? this.pdfCount,
|
||||||
releasedAt: releasedAt ?? this.releasedAt,
|
releasedAt: releasedAt ?? this.releasedAt,
|
||||||
|
description: description is String? ? description : this.description,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -235,6 +235,7 @@ class ExploreEndpoint extends Endpoint {
|
||||||
frontLanguage: latest['frontLanguage'] as String?,
|
frontLanguage: latest['frontLanguage'] as String?,
|
||||||
backLanguage: latest['backLanguage'] as String?,
|
backLanguage: latest['backLanguage'] as String?,
|
||||||
basedOn: latest['basedOn'] as String?,
|
basedOn: latest['basedOn'] as String?,
|
||||||
|
colorName: latest['colorName'] as String?,
|
||||||
license: latest['license'] as String,
|
license: latest['license'] as String,
|
||||||
licenseHolder: latest['licenseHolder'] as String?,
|
licenseHolder: latest['licenseHolder'] as String?,
|
||||||
licenseYear: latest['licenseYear'] as int?,
|
licenseYear: latest['licenseYear'] as int?,
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ fields:
|
||||||
backLanguage: String?
|
backLanguage: String?
|
||||||
### Where the published studyset it is a copy of is, for a copy.
|
### Where the published studyset it is a copy of is, for a copy.
|
||||||
basedOn: String?
|
basedOn: String?
|
||||||
|
### Its colour, by the app palette's name for it; null for none.
|
||||||
|
colorName: String?
|
||||||
### Its licence and credits.
|
### Its licence and credits.
|
||||||
license: String
|
license: String
|
||||||
licenseHolder: String?
|
licenseHolder: String?
|
||||||
|
|
|
||||||
|
|
@ -1084,6 +1084,11 @@ class Endpoints extends _is.EndpointDispatch {
|
||||||
type: _is.getType<String>(),
|
type: _is.getType<String>(),
|
||||||
nullable: false,
|
nullable: false,
|
||||||
),
|
),
|
||||||
|
'description': _is.ParameterDescription(
|
||||||
|
name: 'description',
|
||||||
|
type: _is.getType<String?>(),
|
||||||
|
nullable: true,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
call:
|
call:
|
||||||
(
|
(
|
||||||
|
|
@ -1095,6 +1100,7 @@ class Endpoints extends _is.EndpointDispatch {
|
||||||
params['studysetId'],
|
params['studysetId'],
|
||||||
params['slug'],
|
params['slug'],
|
||||||
params['notes'],
|
params['notes'],
|
||||||
|
description: params['description'],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
'withdraw': _is.MethodConnector(
|
'withdraw': _is.MethodConnector(
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ abstract class ExploreEntry
|
||||||
this.frontLanguage,
|
this.frontLanguage,
|
||||||
this.backLanguage,
|
this.backLanguage,
|
||||||
this.basedOn,
|
this.basedOn,
|
||||||
|
this.colorName,
|
||||||
required this.license,
|
required this.license,
|
||||||
this.licenseHolder,
|
this.licenseHolder,
|
||||||
this.licenseYear,
|
this.licenseYear,
|
||||||
|
|
@ -57,6 +58,7 @@ abstract class ExploreEntry
|
||||||
String? frontLanguage,
|
String? frontLanguage,
|
||||||
String? backLanguage,
|
String? backLanguage,
|
||||||
String? basedOn,
|
String? basedOn,
|
||||||
|
String? colorName,
|
||||||
required String license,
|
required String license,
|
||||||
String? licenseHolder,
|
String? licenseHolder,
|
||||||
int? licenseYear,
|
int? licenseYear,
|
||||||
|
|
@ -88,6 +90,7 @@ abstract class ExploreEntry
|
||||||
frontLanguage: jsonSerialization['frontLanguage'] as String?,
|
frontLanguage: jsonSerialization['frontLanguage'] as String?,
|
||||||
backLanguage: jsonSerialization['backLanguage'] as String?,
|
backLanguage: jsonSerialization['backLanguage'] as String?,
|
||||||
basedOn: jsonSerialization['basedOn'] as String?,
|
basedOn: jsonSerialization['basedOn'] as String?,
|
||||||
|
colorName: jsonSerialization['colorName'] as String?,
|
||||||
license: jsonSerialization['license'] as String,
|
license: jsonSerialization['license'] as String,
|
||||||
licenseHolder: jsonSerialization['licenseHolder'] as String?,
|
licenseHolder: jsonSerialization['licenseHolder'] as String?,
|
||||||
licenseYear: jsonSerialization['licenseYear'] as int?,
|
licenseYear: jsonSerialization['licenseYear'] as int?,
|
||||||
|
|
@ -135,6 +138,9 @@ abstract class ExploreEntry
|
||||||
/// Where the published studyset it is a copy of is, for a copy.
|
/// Where the published studyset it is a copy of is, for a copy.
|
||||||
String? basedOn;
|
String? basedOn;
|
||||||
|
|
||||||
|
/// Its colour, by the app palette's name for it; null for none.
|
||||||
|
String? colorName;
|
||||||
|
|
||||||
/// Its licence and credits.
|
/// Its licence and credits.
|
||||||
String license;
|
String license;
|
||||||
|
|
||||||
|
|
@ -175,6 +181,7 @@ abstract class ExploreEntry
|
||||||
String? frontLanguage,
|
String? frontLanguage,
|
||||||
String? backLanguage,
|
String? backLanguage,
|
||||||
String? basedOn,
|
String? basedOn,
|
||||||
|
String? colorName,
|
||||||
String? license,
|
String? license,
|
||||||
String? licenseHolder,
|
String? licenseHolder,
|
||||||
int? licenseYear,
|
int? licenseYear,
|
||||||
|
|
@ -203,6 +210,7 @@ abstract class ExploreEntry
|
||||||
if (frontLanguage != null) 'frontLanguage': frontLanguage,
|
if (frontLanguage != null) 'frontLanguage': frontLanguage,
|
||||||
if (backLanguage != null) 'backLanguage': backLanguage,
|
if (backLanguage != null) 'backLanguage': backLanguage,
|
||||||
if (basedOn != null) 'basedOn': basedOn,
|
if (basedOn != null) 'basedOn': basedOn,
|
||||||
|
if (colorName != null) 'colorName': colorName,
|
||||||
'license': license,
|
'license': license,
|
||||||
if (licenseHolder != null) 'licenseHolder': licenseHolder,
|
if (licenseHolder != null) 'licenseHolder': licenseHolder,
|
||||||
if (licenseYear != null) 'licenseYear': licenseYear,
|
if (licenseYear != null) 'licenseYear': licenseYear,
|
||||||
|
|
@ -233,6 +241,7 @@ abstract class ExploreEntry
|
||||||
if (frontLanguage != null) 'frontLanguage': frontLanguage,
|
if (frontLanguage != null) 'frontLanguage': frontLanguage,
|
||||||
if (backLanguage != null) 'backLanguage': backLanguage,
|
if (backLanguage != null) 'backLanguage': backLanguage,
|
||||||
if (basedOn != null) 'basedOn': basedOn,
|
if (basedOn != null) 'basedOn': basedOn,
|
||||||
|
if (colorName != null) 'colorName': colorName,
|
||||||
'license': license,
|
'license': license,
|
||||||
if (licenseHolder != null) 'licenseHolder': licenseHolder,
|
if (licenseHolder != null) 'licenseHolder': licenseHolder,
|
||||||
if (licenseYear != null) 'licenseYear': licenseYear,
|
if (licenseYear != null) 'licenseYear': licenseYear,
|
||||||
|
|
@ -269,6 +278,7 @@ class _ExploreEntryImpl extends ExploreEntry {
|
||||||
String? frontLanguage,
|
String? frontLanguage,
|
||||||
String? backLanguage,
|
String? backLanguage,
|
||||||
String? basedOn,
|
String? basedOn,
|
||||||
|
String? colorName,
|
||||||
required String license,
|
required String license,
|
||||||
String? licenseHolder,
|
String? licenseHolder,
|
||||||
int? licenseYear,
|
int? licenseYear,
|
||||||
|
|
@ -293,6 +303,7 @@ class _ExploreEntryImpl extends ExploreEntry {
|
||||||
frontLanguage: frontLanguage,
|
frontLanguage: frontLanguage,
|
||||||
backLanguage: backLanguage,
|
backLanguage: backLanguage,
|
||||||
basedOn: basedOn,
|
basedOn: basedOn,
|
||||||
|
colorName: colorName,
|
||||||
license: license,
|
license: license,
|
||||||
licenseHolder: licenseHolder,
|
licenseHolder: licenseHolder,
|
||||||
licenseYear: licenseYear,
|
licenseYear: licenseYear,
|
||||||
|
|
@ -323,6 +334,7 @@ class _ExploreEntryImpl extends ExploreEntry {
|
||||||
Object? frontLanguage = _Undefined,
|
Object? frontLanguage = _Undefined,
|
||||||
Object? backLanguage = _Undefined,
|
Object? backLanguage = _Undefined,
|
||||||
Object? basedOn = _Undefined,
|
Object? basedOn = _Undefined,
|
||||||
|
Object? colorName = _Undefined,
|
||||||
String? license,
|
String? license,
|
||||||
Object? licenseHolder = _Undefined,
|
Object? licenseHolder = _Undefined,
|
||||||
Object? licenseYear = _Undefined,
|
Object? licenseYear = _Undefined,
|
||||||
|
|
@ -352,6 +364,7 @@ class _ExploreEntryImpl extends ExploreEntry {
|
||||||
: this.frontLanguage,
|
: this.frontLanguage,
|
||||||
backLanguage: backLanguage is String? ? backLanguage : this.backLanguage,
|
backLanguage: backLanguage is String? ? backLanguage : this.backLanguage,
|
||||||
basedOn: basedOn is String? ? basedOn : this.basedOn,
|
basedOn: basedOn is String? ? basedOn : this.basedOn,
|
||||||
|
colorName: colorName is String? ? colorName : this.colorName,
|
||||||
license: license ?? this.license,
|
license: license ?? this.license,
|
||||||
licenseHolder: licenseHolder is String?
|
licenseHolder: licenseHolder is String?
|
||||||
? licenseHolder
|
? licenseHolder
|
||||||
|
|
|
||||||
|
|
@ -3415,6 +3415,12 @@ class Protocol extends _is.DatabaseSerializationManager {
|
||||||
isNullable: true,
|
isNullable: true,
|
||||||
dartType: 'String?',
|
dartType: 'String?',
|
||||||
),
|
),
|
||||||
|
_isp.ColumnDefinition(
|
||||||
|
name: 'colorName',
|
||||||
|
columnType: _isp.ColumnType.text,
|
||||||
|
isNullable: true,
|
||||||
|
dartType: 'String?',
|
||||||
|
),
|
||||||
],
|
],
|
||||||
foreignKeys: [
|
foreignKeys: [
|
||||||
_isp.ForeignKeyDefinition(
|
_isp.ForeignKeyDefinition(
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ abstract class ReleaseInfo
|
||||||
required this.pathCount,
|
required this.pathCount,
|
||||||
required this.pdfCount,
|
required this.pdfCount,
|
||||||
required this.releasedAt,
|
required this.releasedAt,
|
||||||
|
this.description,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory ReleaseInfo({
|
factory ReleaseInfo({
|
||||||
|
|
@ -37,6 +38,7 @@ abstract class ReleaseInfo
|
||||||
required int pathCount,
|
required int pathCount,
|
||||||
required int pdfCount,
|
required int pdfCount,
|
||||||
required DateTime releasedAt,
|
required DateTime releasedAt,
|
||||||
|
String? description,
|
||||||
}) = _ReleaseInfoImpl;
|
}) = _ReleaseInfoImpl;
|
||||||
|
|
||||||
factory ReleaseInfo.fromJson(Map<String, dynamic> jsonSerialization) {
|
factory ReleaseInfo.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||||
|
|
@ -52,6 +54,7 @@ abstract class ReleaseInfo
|
||||||
releasedAt: _is.DateTimeJsonExtension.fromJson(
|
releasedAt: _is.DateTimeJsonExtension.fromJson(
|
||||||
jsonSerialization['releasedAt'],
|
jsonSerialization['releasedAt'],
|
||||||
),
|
),
|
||||||
|
description: jsonSerialization['description'] as String?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -77,6 +80,10 @@ abstract class ReleaseInfo
|
||||||
/// When it was released, in UTC.
|
/// When it was released, in UTC.
|
||||||
DateTime releasedAt;
|
DateTime releasedAt;
|
||||||
|
|
||||||
|
/// What Explore shows of it as its description (rich text, as its JSON);
|
||||||
|
/// null for none.
|
||||||
|
String? description;
|
||||||
|
|
||||||
/// Returns a shallow copy of this [ReleaseInfo]
|
/// Returns a shallow copy of this [ReleaseInfo]
|
||||||
/// with some or all fields replaced by the given arguments.
|
/// with some or all fields replaced by the given arguments.
|
||||||
@_is.useResult
|
@_is.useResult
|
||||||
|
|
@ -90,6 +97,7 @@ abstract class ReleaseInfo
|
||||||
int? pathCount,
|
int? pathCount,
|
||||||
int? pdfCount,
|
int? pdfCount,
|
||||||
DateTime? releasedAt,
|
DateTime? releasedAt,
|
||||||
|
String? description,
|
||||||
});
|
});
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
|
|
@ -104,6 +112,7 @@ abstract class ReleaseInfo
|
||||||
'pathCount': pathCount,
|
'pathCount': pathCount,
|
||||||
'pdfCount': pdfCount,
|
'pdfCount': pdfCount,
|
||||||
'releasedAt': releasedAt.toJson(),
|
'releasedAt': releasedAt.toJson(),
|
||||||
|
if (description != null) 'description': description,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -120,6 +129,7 @@ abstract class ReleaseInfo
|
||||||
'pathCount': pathCount,
|
'pathCount': pathCount,
|
||||||
'pdfCount': pdfCount,
|
'pdfCount': pdfCount,
|
||||||
'releasedAt': releasedAt.toJson(),
|
'releasedAt': releasedAt.toJson(),
|
||||||
|
if (description != null) 'description': description,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -129,6 +139,8 @@ abstract class ReleaseInfo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _Undefined {}
|
||||||
|
|
||||||
class _ReleaseInfoImpl extends ReleaseInfo {
|
class _ReleaseInfoImpl extends ReleaseInfo {
|
||||||
_ReleaseInfoImpl({
|
_ReleaseInfoImpl({
|
||||||
required int version,
|
required int version,
|
||||||
|
|
@ -140,6 +152,7 @@ class _ReleaseInfoImpl extends ReleaseInfo {
|
||||||
required int pathCount,
|
required int pathCount,
|
||||||
required int pdfCount,
|
required int pdfCount,
|
||||||
required DateTime releasedAt,
|
required DateTime releasedAt,
|
||||||
|
String? description,
|
||||||
}) : super._(
|
}) : super._(
|
||||||
version: version,
|
version: version,
|
||||||
notes: notes,
|
notes: notes,
|
||||||
|
|
@ -150,6 +163,7 @@ class _ReleaseInfoImpl extends ReleaseInfo {
|
||||||
pathCount: pathCount,
|
pathCount: pathCount,
|
||||||
pdfCount: pdfCount,
|
pdfCount: pdfCount,
|
||||||
releasedAt: releasedAt,
|
releasedAt: releasedAt,
|
||||||
|
description: description,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Returns a shallow copy of this [ReleaseInfo]
|
/// Returns a shallow copy of this [ReleaseInfo]
|
||||||
|
|
@ -166,6 +180,7 @@ class _ReleaseInfoImpl extends ReleaseInfo {
|
||||||
int? pathCount,
|
int? pathCount,
|
||||||
int? pdfCount,
|
int? pdfCount,
|
||||||
DateTime? releasedAt,
|
DateTime? releasedAt,
|
||||||
|
Object? description = _Undefined,
|
||||||
}) {
|
}) {
|
||||||
return ReleaseInfo(
|
return ReleaseInfo(
|
||||||
version: version ?? this.version,
|
version: version ?? this.version,
|
||||||
|
|
@ -177,6 +192,7 @@ class _ReleaseInfoImpl extends ReleaseInfo {
|
||||||
pathCount: pathCount ?? this.pathCount,
|
pathCount: pathCount ?? this.pathCount,
|
||||||
pdfCount: pdfCount ?? this.pdfCount,
|
pdfCount: pdfCount ?? this.pdfCount,
|
||||||
releasedAt: releasedAt ?? this.releasedAt,
|
releasedAt: releasedAt ?? this.releasedAt,
|
||||||
|
description: description is String? ? description : this.description,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ abstract class StudysetRelease
|
||||||
this.licenseHolder,
|
this.licenseHolder,
|
||||||
this.licenseYear,
|
this.licenseYear,
|
||||||
this.basedOn,
|
this.basedOn,
|
||||||
|
this.colorName,
|
||||||
}) : title = title ?? '';
|
}) : title = title ?? '';
|
||||||
|
|
||||||
factory StudysetRelease({
|
factory StudysetRelease({
|
||||||
|
|
@ -62,6 +63,7 @@ abstract class StudysetRelease
|
||||||
String? licenseHolder,
|
String? licenseHolder,
|
||||||
int? licenseYear,
|
int? licenseYear,
|
||||||
String? basedOn,
|
String? basedOn,
|
||||||
|
String? colorName,
|
||||||
}) = _StudysetReleaseImpl;
|
}) = _StudysetReleaseImpl;
|
||||||
|
|
||||||
factory StudysetRelease.fromJson(Map<String, dynamic> jsonSerialization) {
|
factory StudysetRelease.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||||
|
|
@ -89,6 +91,7 @@ abstract class StudysetRelease
|
||||||
licenseHolder: jsonSerialization['licenseHolder'] as String?,
|
licenseHolder: jsonSerialization['licenseHolder'] as String?,
|
||||||
licenseYear: jsonSerialization['licenseYear'] as int?,
|
licenseYear: jsonSerialization['licenseYear'] as int?,
|
||||||
basedOn: jsonSerialization['basedOn'] as String?,
|
basedOn: jsonSerialization['basedOn'] as String?,
|
||||||
|
colorName: jsonSerialization['colorName'] as String?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -131,7 +134,8 @@ abstract class StudysetRelease
|
||||||
DateTime releasedAt;
|
DateTime releasedAt;
|
||||||
|
|
||||||
/// What Explore shows of it, as its studyset said then: the title, the
|
/// What Explore shows of it, as its studyset said then: the title, the
|
||||||
/// secondary title, the description (rich text, as its JSON), the
|
/// secondary title, the description (rich text, as its JSON; the one its
|
||||||
|
/// author wrote for Explore when publishing, else the studyset's), the
|
||||||
/// languages (BCP 47) and the licence's credits. Kept beside the
|
/// languages (BCP 47) and the licence's credits. Kept beside the
|
||||||
/// repository so the listing never reads git.
|
/// repository so the listing never reads git.
|
||||||
String title;
|
String title;
|
||||||
|
|
@ -152,6 +156,11 @@ abstract class StudysetRelease
|
||||||
/// (`halkyon.eu/@alice/japanese`): what it is based on.
|
/// (`halkyon.eu/@alice/japanese`): what it is based on.
|
||||||
String? basedOn;
|
String? basedOn;
|
||||||
|
|
||||||
|
/// Its colour, by the name the app's palette gives it (`cobalt`), as
|
||||||
|
/// the library shows it; null for none. Kept since 2026-10-14: releases
|
||||||
|
/// before have none.
|
||||||
|
String? colorName;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_is.Table<int?> get table => t;
|
_is.Table<int?> get table => t;
|
||||||
|
|
||||||
|
|
@ -180,6 +189,7 @@ abstract class StudysetRelease
|
||||||
String? licenseHolder,
|
String? licenseHolder,
|
||||||
int? licenseYear,
|
int? licenseYear,
|
||||||
String? basedOn,
|
String? basedOn,
|
||||||
|
String? colorName,
|
||||||
});
|
});
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
|
|
@ -206,6 +216,7 @@ abstract class StudysetRelease
|
||||||
if (licenseHolder != null) 'licenseHolder': licenseHolder,
|
if (licenseHolder != null) 'licenseHolder': licenseHolder,
|
||||||
if (licenseYear != null) 'licenseYear': licenseYear,
|
if (licenseYear != null) 'licenseYear': licenseYear,
|
||||||
if (basedOn != null) 'basedOn': basedOn,
|
if (basedOn != null) 'basedOn': basedOn,
|
||||||
|
if (colorName != null) 'colorName': colorName,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -267,6 +278,7 @@ class _StudysetReleaseImpl extends StudysetRelease {
|
||||||
String? licenseHolder,
|
String? licenseHolder,
|
||||||
int? licenseYear,
|
int? licenseYear,
|
||||||
String? basedOn,
|
String? basedOn,
|
||||||
|
String? colorName,
|
||||||
}) : super._(
|
}) : super._(
|
||||||
id: id,
|
id: id,
|
||||||
publishedStudysetId: publishedStudysetId,
|
publishedStudysetId: publishedStudysetId,
|
||||||
|
|
@ -289,6 +301,7 @@ class _StudysetReleaseImpl extends StudysetRelease {
|
||||||
licenseHolder: licenseHolder,
|
licenseHolder: licenseHolder,
|
||||||
licenseYear: licenseYear,
|
licenseYear: licenseYear,
|
||||||
basedOn: basedOn,
|
basedOn: basedOn,
|
||||||
|
colorName: colorName,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Returns a shallow copy of this [StudysetRelease]
|
/// Returns a shallow copy of this [StudysetRelease]
|
||||||
|
|
@ -317,6 +330,7 @@ class _StudysetReleaseImpl extends StudysetRelease {
|
||||||
Object? licenseHolder = _Undefined,
|
Object? licenseHolder = _Undefined,
|
||||||
Object? licenseYear = _Undefined,
|
Object? licenseYear = _Undefined,
|
||||||
Object? basedOn = _Undefined,
|
Object? basedOn = _Undefined,
|
||||||
|
Object? colorName = _Undefined,
|
||||||
}) {
|
}) {
|
||||||
return StudysetRelease(
|
return StudysetRelease(
|
||||||
id: id is int? ? id : this.id,
|
id: id is int? ? id : this.id,
|
||||||
|
|
@ -346,6 +360,7 @@ class _StudysetReleaseImpl extends StudysetRelease {
|
||||||
: this.licenseHolder,
|
: this.licenseHolder,
|
||||||
licenseYear: licenseYear is int? ? licenseYear : this.licenseYear,
|
licenseYear: licenseYear is int? ? licenseYear : this.licenseYear,
|
||||||
basedOn: basedOn is String? ? basedOn : this.basedOn,
|
basedOn: basedOn is String? ? basedOn : this.basedOn,
|
||||||
|
colorName: colorName is String? ? colorName : this.colorName,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -457,6 +472,11 @@ class StudysetReleaseUpdateTable extends _is.UpdateTable<StudysetReleaseTable> {
|
||||||
table.basedOn,
|
table.basedOn,
|
||||||
value,
|
value,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
_is.ColumnValue<String, String> colorName(String? value) => _is.ColumnValue(
|
||||||
|
table.colorName,
|
||||||
|
value,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class StudysetReleaseTable extends _is.Table<int?> {
|
class StudysetReleaseTable extends _is.Table<int?> {
|
||||||
|
|
@ -544,6 +564,10 @@ class StudysetReleaseTable extends _is.Table<int?> {
|
||||||
'basedOn',
|
'basedOn',
|
||||||
this,
|
this,
|
||||||
);
|
);
|
||||||
|
colorName = _is.ColumnString(
|
||||||
|
'colorName',
|
||||||
|
this,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
late final StudysetReleaseUpdateTable updateTable;
|
late final StudysetReleaseUpdateTable updateTable;
|
||||||
|
|
@ -580,7 +604,8 @@ class StudysetReleaseTable extends _is.Table<int?> {
|
||||||
late final _is.ColumnDateTime releasedAt;
|
late final _is.ColumnDateTime releasedAt;
|
||||||
|
|
||||||
/// What Explore shows of it, as its studyset said then: the title, the
|
/// What Explore shows of it, as its studyset said then: the title, the
|
||||||
/// secondary title, the description (rich text, as its JSON), the
|
/// secondary title, the description (rich text, as its JSON; the one its
|
||||||
|
/// author wrote for Explore when publishing, else the studyset's), the
|
||||||
/// languages (BCP 47) and the licence's credits. Kept beside the
|
/// languages (BCP 47) and the licence's credits. Kept beside the
|
||||||
/// repository so the listing never reads git.
|
/// repository so the listing never reads git.
|
||||||
late final _is.ColumnString title;
|
late final _is.ColumnString title;
|
||||||
|
|
@ -601,6 +626,11 @@ class StudysetReleaseTable extends _is.Table<int?> {
|
||||||
/// (`halkyon.eu/@alice/japanese`): what it is based on.
|
/// (`halkyon.eu/@alice/japanese`): what it is based on.
|
||||||
late final _is.ColumnString basedOn;
|
late final _is.ColumnString basedOn;
|
||||||
|
|
||||||
|
/// Its colour, by the name the app's palette gives it (`cobalt`), as
|
||||||
|
/// the library shows it; null for none. Kept since 2026-10-14: releases
|
||||||
|
/// before have none.
|
||||||
|
late final _is.ColumnString colorName;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<_is.Column> get columns => [
|
List<_is.Column> get columns => [
|
||||||
id,
|
id,
|
||||||
|
|
@ -624,6 +654,7 @@ class StudysetReleaseTable extends _is.Table<int?> {
|
||||||
licenseHolder,
|
licenseHolder,
|
||||||
licenseYear,
|
licenseYear,
|
||||||
basedOn,
|
basedOn,
|
||||||
|
colorName,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,11 @@ class PublishEndpoint extends Endpoint {
|
||||||
/// Publishes your studyset [studysetId] with everything inside it as its
|
/// Publishes your studyset [studysetId] with everything inside it as its
|
||||||
/// next release, [notes] saying what changed; at the first, under the
|
/// next release, [notes] saying what changed; at the first, under the
|
||||||
/// name [slug] in its location, which stays its name from then on.
|
/// name [slug] in its location, which stays its name from then on.
|
||||||
|
/// [description] (rich text, as its JSON) is what Explore shows of it,
|
||||||
|
/// when given: a studyset may introduce itself to strangers otherwise
|
||||||
|
/// than in the library (decided 2026-10-14); without, the studyset's
|
||||||
|
/// own description. One that isn't rich text, or is larger than
|
||||||
|
/// [maxExploreDescription], counts as not given.
|
||||||
///
|
///
|
||||||
/// Throws [PublishRefusedException] when it can't be published (see
|
/// Throws [PublishRefusedException] when it can't be published (see
|
||||||
/// [PublishRefusal]), and [NotFoundException] for a studyset that isn't
|
/// [PublishRefusal]), and [NotFoundException] for a studyset that isn't
|
||||||
|
|
@ -88,8 +93,9 @@ class PublishEndpoint extends Endpoint {
|
||||||
Session session,
|
Session session,
|
||||||
UuidValue studysetId,
|
UuidValue studysetId,
|
||||||
String slug,
|
String slug,
|
||||||
String notes,
|
String notes, {
|
||||||
) async {
|
String? description,
|
||||||
|
}) async {
|
||||||
final person = session.authenticated!.authUserId;
|
final person = session.authenticated!.authUserId;
|
||||||
final key = '${person.uuid}/${studysetId.uuid}';
|
final key = '${person.uuid}/${studysetId.uuid}';
|
||||||
if (!_publishing.add(key)) {
|
if (!_publishing.add(key)) {
|
||||||
|
|
@ -102,6 +108,7 @@ class PublishEndpoint extends Endpoint {
|
||||||
studysetId: studysetId,
|
studysetId: studysetId,
|
||||||
slug: slug.trim(),
|
slug: slug.trim(),
|
||||||
notes: notes.trim(),
|
notes: notes.trim(),
|
||||||
|
description: _richText(description),
|
||||||
);
|
);
|
||||||
} finally {
|
} finally {
|
||||||
_publishing.remove(key);
|
_publishing.remove(key);
|
||||||
|
|
@ -140,6 +147,7 @@ class PublishEndpoint extends Endpoint {
|
||||||
required UuidValue studysetId,
|
required UuidValue studysetId,
|
||||||
required String slug,
|
required String slug,
|
||||||
required String notes,
|
required String notes,
|
||||||
|
required String? description,
|
||||||
}) async {
|
}) async {
|
||||||
// SELECT … FROM "public_profile" WHERE "authUserId" = $1
|
// SELECT … FROM "public_profile" WHERE "authUserId" = $1
|
||||||
final profile = await PublicProfile.db.findFirstRow(
|
final profile = await PublicProfile.db.findFirstRow(
|
||||||
|
|
@ -309,12 +317,14 @@ class PublishEndpoint extends Endpoint {
|
||||||
releasedAt: now,
|
releasedAt: now,
|
||||||
title: text('title') ?? '',
|
title: text('title') ?? '',
|
||||||
secondaryTitle: text('secondaryTitle'),
|
secondaryTitle: text('secondaryTitle'),
|
||||||
description: switch (root['description']) {
|
description:
|
||||||
final Map<String, Object?> description => jsonEncode(
|
description ??
|
||||||
description,
|
switch (root['description']) {
|
||||||
),
|
final Map<String, Object?> description => jsonEncode(
|
||||||
_ => null,
|
description,
|
||||||
},
|
),
|
||||||
|
_ => null,
|
||||||
|
},
|
||||||
frontLanguage: text('frontLanguage'),
|
frontLanguage: text('frontLanguage'),
|
||||||
backLanguage: text('backLanguage'),
|
backLanguage: text('backLanguage'),
|
||||||
licenseHolder: text('licenseHolder'),
|
licenseHolder: text('licenseHolder'),
|
||||||
|
|
@ -323,6 +333,7 @@ class PublishEndpoint extends Endpoint {
|
||||||
_ => null,
|
_ => null,
|
||||||
},
|
},
|
||||||
basedOn: basedOn,
|
basedOn: basedOn,
|
||||||
|
colorName: text('color'),
|
||||||
),
|
),
|
||||||
transaction: transaction,
|
transaction: transaction,
|
||||||
);
|
);
|
||||||
|
|
@ -525,6 +536,7 @@ class PublishEndpoint extends Endpoint {
|
||||||
pathCount: release.pathCount,
|
pathCount: release.pathCount,
|
||||||
pdfCount: release.pdfCount,
|
pdfCount: release.pdfCount,
|
||||||
releasedAt: release.releasedAt,
|
releasedAt: release.releasedAt,
|
||||||
|
description: release.description,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
withdrawn: published.withdrawnAt != null,
|
withdrawn: published.withdrawnAt != null,
|
||||||
|
|
@ -532,3 +544,23 @@ class PublishEndpoint extends Endpoint {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The largest description for Explore taken, as its JSON: some 50 pages
|
||||||
|
/// of text, far beyond what Explore shows.
|
||||||
|
const maxExploreDescription = 100000;
|
||||||
|
|
||||||
|
/// [json] when it is rich text as the app writes it, `{"formatVersion": …,
|
||||||
|
/// "editor": …}`, within [maxExploreDescription]; else null.
|
||||||
|
String? _richText(String? json) {
|
||||||
|
if (json == null || json.length > maxExploreDescription) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return switch (jsonDecode(json)) {
|
||||||
|
{'formatVersion': int(), 'editor': Map<String, Object?>()} => json,
|
||||||
|
_ => null,
|
||||||
|
};
|
||||||
|
} on FormatException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,3 +14,6 @@ fields:
|
||||||
pdfCount: int
|
pdfCount: int
|
||||||
### When it was released, in UTC.
|
### When it was released, in UTC.
|
||||||
releasedAt: DateTime
|
releasedAt: DateTime
|
||||||
|
### What Explore shows of it as its description (rich text, as its JSON);
|
||||||
|
### null for none.
|
||||||
|
description: String?
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,8 @@ fields:
|
||||||
### When it was released, in UTC.
|
### When it was released, in UTC.
|
||||||
releasedAt: DateTime
|
releasedAt: DateTime
|
||||||
### What Explore shows of it, as its studyset said then: the title, the
|
### What Explore shows of it, as its studyset said then: the title, the
|
||||||
### secondary title, the description (rich text, as its JSON), the
|
### secondary title, the description (rich text, as its JSON; the one its
|
||||||
|
### author wrote for Explore when publishing, else the studyset's), the
|
||||||
### languages (BCP 47) and the licence's credits. Kept beside the
|
### languages (BCP 47) and the licence's credits. Kept beside the
|
||||||
### repository so the listing never reads git.
|
### repository so the listing never reads git.
|
||||||
title: String, default=''
|
title: String, default=''
|
||||||
|
|
@ -37,6 +38,10 @@ fields:
|
||||||
### For a copy of a published studyset, where that one is
|
### For a copy of a published studyset, where that one is
|
||||||
### (`halkyon.eu/@alice/japanese`): what it is based on.
|
### (`halkyon.eu/@alice/japanese`): what it is based on.
|
||||||
basedOn: String?
|
basedOn: String?
|
||||||
|
### Its colour, by the name the app's palette gives it (`cobalt`), as
|
||||||
|
### the library shows it; null for none. Kept since 2026-10-14: releases
|
||||||
|
### before have none.
|
||||||
|
colorName: String?
|
||||||
indexes:
|
indexes:
|
||||||
studyset_release_version_idx:
|
studyset_release_version_idx:
|
||||||
fields: publishedStudysetId, version
|
fields: publishedStudysetId, version
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,33 @@
|
||||||
|
{
|
||||||
|
"__className__": "serverpod.DatabaseMigration",
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"__className__": "serverpod.DatabaseMigrationAction",
|
||||||
|
"type": "alterTable",
|
||||||
|
"alterTable": {
|
||||||
|
"__className__": "serverpod.TableMigration",
|
||||||
|
"name": "studyset_release",
|
||||||
|
"schema": "public",
|
||||||
|
"addColumns": [
|
||||||
|
{
|
||||||
|
"__className__": "serverpod.ColumnDefinition",
|
||||||
|
"name": "colorName",
|
||||||
|
"fieldName": "colorName",
|
||||||
|
"columnType": 0,
|
||||||
|
"isNullable": true,
|
||||||
|
"dartType": "String?"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"deleteColumns": [],
|
||||||
|
"modifyColumns": [],
|
||||||
|
"addIndexes": [],
|
||||||
|
"deleteIndexes": [],
|
||||||
|
"addForeignKeys": [],
|
||||||
|
"deleteForeignKeys": [],
|
||||||
|
"warnings": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"warnings": [],
|
||||||
|
"migrationApiVersion": 1
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- ACTION ALTER TABLE
|
||||||
|
--
|
||||||
|
ALTER TABLE "studyset_release" ADD COLUMN "colorName" text;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- MIGRATION VERSION FOR halkyon_learn
|
||||||
|
--
|
||||||
|
INSERT INTO "serverpod_migrations" ("module", "version", "timestamp")
|
||||||
|
VALUES ('halkyon_learn', '20261014153932647-release-colour', now())
|
||||||
|
ON CONFLICT ("module")
|
||||||
|
DO UPDATE SET "version" = '20261014153932647-release-colour', "timestamp" = now();
|
||||||
|
|
||||||
|
--
|
||||||
|
-- MIGRATION VERSION FOR serverpod
|
||||||
|
--
|
||||||
|
INSERT INTO "serverpod_migrations" ("module", "version", "timestamp")
|
||||||
|
VALUES ('serverpod', '20260824182259319', now())
|
||||||
|
ON CONFLICT ("module")
|
||||||
|
DO UPDATE SET "version" = '20260824182259319', "timestamp" = now();
|
||||||
|
|
||||||
|
--
|
||||||
|
-- MIGRATION VERSION FOR serverpod_auth_idp
|
||||||
|
--
|
||||||
|
INSERT INTO "serverpod_migrations" ("module", "version", "timestamp")
|
||||||
|
VALUES ('serverpod_auth_idp', '20260924105404509', now())
|
||||||
|
ON CONFLICT ("module")
|
||||||
|
DO UPDATE SET "version" = '20260924105404509', "timestamp" = now();
|
||||||
|
|
||||||
|
--
|
||||||
|
-- MIGRATION VERSION FOR serverpod_offline_sync
|
||||||
|
--
|
||||||
|
INSERT INTO "serverpod_migrations" ("module", "version", "timestamp")
|
||||||
|
VALUES ('serverpod_offline_sync', '20260914143806119', now())
|
||||||
|
ON CONFLICT ("module")
|
||||||
|
DO UPDATE SET "version" = '20260914143806119', "timestamp" = now();
|
||||||
|
|
||||||
|
--
|
||||||
|
-- MIGRATION VERSION FOR serverpod_auth_core
|
||||||
|
--
|
||||||
|
INSERT INTO "serverpod_migrations" ("module", "version", "timestamp")
|
||||||
|
VALUES ('serverpod_auth_core', '20260924105232991', now())
|
||||||
|
ON CONFLICT ("module")
|
||||||
|
DO UPDATE SET "version" = '20260924105232991', "timestamp" = now();
|
||||||
|
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
|
@ -27,3 +27,4 @@
|
||||||
20261010095547719-assistant-connector
|
20261010095547719-assistant-connector
|
||||||
20261012162411835-studyset-deleted-at
|
20261012162411835-studyset-deleted-at
|
||||||
20261013035552510-dictionary-keys
|
20261013035552510-dictionary-keys
|
||||||
|
20261014153932647-release-colour
|
||||||
|
|
|
||||||
|
|
@ -162,6 +162,7 @@ void main() {
|
||||||
'license': ?license,
|
'license': ?license,
|
||||||
'licenseHolder': 'Alice',
|
'licenseHolder': 'Alice',
|
||||||
'study': {'newPerDay': 10},
|
'study': {'newPerDay': 10},
|
||||||
|
'color': 'cobalt',
|
||||||
'published': ?copyOf,
|
'published': ?copyOf,
|
||||||
'examPlans': [
|
'examPlans': [
|
||||||
{
|
{
|
||||||
|
|
@ -528,6 +529,47 @@ void main() {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("a release keeps the studyset's colour, and what its author wrote "
|
||||||
|
'for Explore as its description; what is no rich text counts as '
|
||||||
|
'nothing written', () async {
|
||||||
|
await profile(alice, 'alice');
|
||||||
|
final content = await japanese(alice);
|
||||||
|
final about = jsonEncode({
|
||||||
|
'formatVersion': 1,
|
||||||
|
'editor': {
|
||||||
|
'document': {'type': 'page'},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
final published = await endpoints.publish.publish(
|
||||||
|
signedIn(alice),
|
||||||
|
content.root,
|
||||||
|
'japanese',
|
||||||
|
'',
|
||||||
|
description: about,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(published.releases.single.description, about);
|
||||||
|
final kept = await StudysetRelease.db.find(session);
|
||||||
|
expect(kept.single.colorName, 'cobalt');
|
||||||
|
expect(kept.single.description, about);
|
||||||
|
|
||||||
|
await revise(
|
||||||
|
alice,
|
||||||
|
content.word,
|
||||||
|
await revisionsOf(content.word),
|
||||||
|
'日本語',
|
||||||
|
);
|
||||||
|
final second = await endpoints.publish.publish(
|
||||||
|
signedIn(alice),
|
||||||
|
content.root,
|
||||||
|
'japanese',
|
||||||
|
'',
|
||||||
|
description: 'not rich text',
|
||||||
|
);
|
||||||
|
expect(second.releases.first.description, isNull);
|
||||||
|
});
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'a version git has but the database lost is never given again',
|
'a version git has but the database lost is never given again',
|
||||||
() async {
|
() async {
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue