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
18 changed files with 14958 additions and 15 deletions
|
|
@ -235,6 +235,7 @@ class ExploreEndpoint extends Endpoint {
|
|||
frontLanguage: latest['frontLanguage'] as String?,
|
||||
backLanguage: latest['backLanguage'] as String?,
|
||||
basedOn: latest['basedOn'] as String?,
|
||||
colorName: latest['colorName'] as String?,
|
||||
license: latest['license'] as String,
|
||||
licenseHolder: latest['licenseHolder'] as String?,
|
||||
licenseYear: latest['licenseYear'] as int?,
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ fields:
|
|||
backLanguage: String?
|
||||
### Where the published studyset it is a copy of is, for a copy.
|
||||
basedOn: String?
|
||||
### Its colour, by the app palette's name for it; null for none.
|
||||
colorName: String?
|
||||
### Its licence and credits.
|
||||
license: String
|
||||
licenseHolder: String?
|
||||
|
|
|
|||
|
|
@ -1084,6 +1084,11 @@ class Endpoints extends _is.EndpointDispatch {
|
|||
type: _is.getType<String>(),
|
||||
nullable: false,
|
||||
),
|
||||
'description': _is.ParameterDescription(
|
||||
name: 'description',
|
||||
type: _is.getType<String?>(),
|
||||
nullable: true,
|
||||
),
|
||||
},
|
||||
call:
|
||||
(
|
||||
|
|
@ -1095,6 +1100,7 @@ class Endpoints extends _is.EndpointDispatch {
|
|||
params['studysetId'],
|
||||
params['slug'],
|
||||
params['notes'],
|
||||
description: params['description'],
|
||||
),
|
||||
),
|
||||
'withdraw': _is.MethodConnector(
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ abstract class ExploreEntry
|
|||
this.frontLanguage,
|
||||
this.backLanguage,
|
||||
this.basedOn,
|
||||
this.colorName,
|
||||
required this.license,
|
||||
this.licenseHolder,
|
||||
this.licenseYear,
|
||||
|
|
@ -57,6 +58,7 @@ abstract class ExploreEntry
|
|||
String? frontLanguage,
|
||||
String? backLanguage,
|
||||
String? basedOn,
|
||||
String? colorName,
|
||||
required String license,
|
||||
String? licenseHolder,
|
||||
int? licenseYear,
|
||||
|
|
@ -88,6 +90,7 @@ abstract class ExploreEntry
|
|||
frontLanguage: jsonSerialization['frontLanguage'] as String?,
|
||||
backLanguage: jsonSerialization['backLanguage'] as String?,
|
||||
basedOn: jsonSerialization['basedOn'] as String?,
|
||||
colorName: jsonSerialization['colorName'] as String?,
|
||||
license: jsonSerialization['license'] as String,
|
||||
licenseHolder: jsonSerialization['licenseHolder'] as String?,
|
||||
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.
|
||||
String? basedOn;
|
||||
|
||||
/// Its colour, by the app palette's name for it; null for none.
|
||||
String? colorName;
|
||||
|
||||
/// Its licence and credits.
|
||||
String license;
|
||||
|
||||
|
|
@ -175,6 +181,7 @@ abstract class ExploreEntry
|
|||
String? frontLanguage,
|
||||
String? backLanguage,
|
||||
String? basedOn,
|
||||
String? colorName,
|
||||
String? license,
|
||||
String? licenseHolder,
|
||||
int? licenseYear,
|
||||
|
|
@ -203,6 +210,7 @@ abstract class ExploreEntry
|
|||
if (frontLanguage != null) 'frontLanguage': frontLanguage,
|
||||
if (backLanguage != null) 'backLanguage': backLanguage,
|
||||
if (basedOn != null) 'basedOn': basedOn,
|
||||
if (colorName != null) 'colorName': colorName,
|
||||
'license': license,
|
||||
if (licenseHolder != null) 'licenseHolder': licenseHolder,
|
||||
if (licenseYear != null) 'licenseYear': licenseYear,
|
||||
|
|
@ -233,6 +241,7 @@ abstract class ExploreEntry
|
|||
if (frontLanguage != null) 'frontLanguage': frontLanguage,
|
||||
if (backLanguage != null) 'backLanguage': backLanguage,
|
||||
if (basedOn != null) 'basedOn': basedOn,
|
||||
if (colorName != null) 'colorName': colorName,
|
||||
'license': license,
|
||||
if (licenseHolder != null) 'licenseHolder': licenseHolder,
|
||||
if (licenseYear != null) 'licenseYear': licenseYear,
|
||||
|
|
@ -269,6 +278,7 @@ class _ExploreEntryImpl extends ExploreEntry {
|
|||
String? frontLanguage,
|
||||
String? backLanguage,
|
||||
String? basedOn,
|
||||
String? colorName,
|
||||
required String license,
|
||||
String? licenseHolder,
|
||||
int? licenseYear,
|
||||
|
|
@ -293,6 +303,7 @@ class _ExploreEntryImpl extends ExploreEntry {
|
|||
frontLanguage: frontLanguage,
|
||||
backLanguage: backLanguage,
|
||||
basedOn: basedOn,
|
||||
colorName: colorName,
|
||||
license: license,
|
||||
licenseHolder: licenseHolder,
|
||||
licenseYear: licenseYear,
|
||||
|
|
@ -323,6 +334,7 @@ class _ExploreEntryImpl extends ExploreEntry {
|
|||
Object? frontLanguage = _Undefined,
|
||||
Object? backLanguage = _Undefined,
|
||||
Object? basedOn = _Undefined,
|
||||
Object? colorName = _Undefined,
|
||||
String? license,
|
||||
Object? licenseHolder = _Undefined,
|
||||
Object? licenseYear = _Undefined,
|
||||
|
|
@ -352,6 +364,7 @@ class _ExploreEntryImpl extends ExploreEntry {
|
|||
: this.frontLanguage,
|
||||
backLanguage: backLanguage is String? ? backLanguage : this.backLanguage,
|
||||
basedOn: basedOn is String? ? basedOn : this.basedOn,
|
||||
colorName: colorName is String? ? colorName : this.colorName,
|
||||
license: license ?? this.license,
|
||||
licenseHolder: licenseHolder is String?
|
||||
? licenseHolder
|
||||
|
|
|
|||
|
|
@ -3415,6 +3415,12 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
isNullable: true,
|
||||
dartType: 'String?',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'colorName',
|
||||
columnType: _isp.ColumnType.text,
|
||||
isNullable: true,
|
||||
dartType: 'String?',
|
||||
),
|
||||
],
|
||||
foreignKeys: [
|
||||
_isp.ForeignKeyDefinition(
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ abstract class ReleaseInfo
|
|||
required this.pathCount,
|
||||
required this.pdfCount,
|
||||
required this.releasedAt,
|
||||
this.description,
|
||||
});
|
||||
|
||||
factory ReleaseInfo({
|
||||
|
|
@ -37,6 +38,7 @@ abstract class ReleaseInfo
|
|||
required int pathCount,
|
||||
required int pdfCount,
|
||||
required DateTime releasedAt,
|
||||
String? description,
|
||||
}) = _ReleaseInfoImpl;
|
||||
|
||||
factory ReleaseInfo.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
|
|
@ -52,6 +54,7 @@ abstract class ReleaseInfo
|
|||
releasedAt: _is.DateTimeJsonExtension.fromJson(
|
||||
jsonSerialization['releasedAt'],
|
||||
),
|
||||
description: jsonSerialization['description'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -77,6 +80,10 @@ abstract class ReleaseInfo
|
|||
/// When it was released, in UTC.
|
||||
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]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
|
|
@ -90,6 +97,7 @@ abstract class ReleaseInfo
|
|||
int? pathCount,
|
||||
int? pdfCount,
|
||||
DateTime? releasedAt,
|
||||
String? description,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
|
|
@ -104,6 +112,7 @@ abstract class ReleaseInfo
|
|||
'pathCount': pathCount,
|
||||
'pdfCount': pdfCount,
|
||||
'releasedAt': releasedAt.toJson(),
|
||||
if (description != null) 'description': description,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -120,6 +129,7 @@ abstract class ReleaseInfo
|
|||
'pathCount': pathCount,
|
||||
'pdfCount': pdfCount,
|
||||
'releasedAt': releasedAt.toJson(),
|
||||
if (description != null) 'description': description,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -129,6 +139,8 @@ abstract class ReleaseInfo
|
|||
}
|
||||
}
|
||||
|
||||
class _Undefined {}
|
||||
|
||||
class _ReleaseInfoImpl extends ReleaseInfo {
|
||||
_ReleaseInfoImpl({
|
||||
required int version,
|
||||
|
|
@ -140,6 +152,7 @@ class _ReleaseInfoImpl extends ReleaseInfo {
|
|||
required int pathCount,
|
||||
required int pdfCount,
|
||||
required DateTime releasedAt,
|
||||
String? description,
|
||||
}) : super._(
|
||||
version: version,
|
||||
notes: notes,
|
||||
|
|
@ -150,6 +163,7 @@ class _ReleaseInfoImpl extends ReleaseInfo {
|
|||
pathCount: pathCount,
|
||||
pdfCount: pdfCount,
|
||||
releasedAt: releasedAt,
|
||||
description: description,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [ReleaseInfo]
|
||||
|
|
@ -166,6 +180,7 @@ class _ReleaseInfoImpl extends ReleaseInfo {
|
|||
int? pathCount,
|
||||
int? pdfCount,
|
||||
DateTime? releasedAt,
|
||||
Object? description = _Undefined,
|
||||
}) {
|
||||
return ReleaseInfo(
|
||||
version: version ?? this.version,
|
||||
|
|
@ -177,6 +192,7 @@ class _ReleaseInfoImpl extends ReleaseInfo {
|
|||
pathCount: pathCount ?? this.pathCount,
|
||||
pdfCount: pdfCount ?? this.pdfCount,
|
||||
releasedAt: releasedAt ?? this.releasedAt,
|
||||
description: description is String? ? description : this.description,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ abstract class StudysetRelease
|
|||
this.licenseHolder,
|
||||
this.licenseYear,
|
||||
this.basedOn,
|
||||
this.colorName,
|
||||
}) : title = title ?? '';
|
||||
|
||||
factory StudysetRelease({
|
||||
|
|
@ -62,6 +63,7 @@ abstract class StudysetRelease
|
|||
String? licenseHolder,
|
||||
int? licenseYear,
|
||||
String? basedOn,
|
||||
String? colorName,
|
||||
}) = _StudysetReleaseImpl;
|
||||
|
||||
factory StudysetRelease.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
|
|
@ -89,6 +91,7 @@ abstract class StudysetRelease
|
|||
licenseHolder: jsonSerialization['licenseHolder'] as String?,
|
||||
licenseYear: jsonSerialization['licenseYear'] as int?,
|
||||
basedOn: jsonSerialization['basedOn'] as String?,
|
||||
colorName: jsonSerialization['colorName'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -131,7 +134,8 @@ abstract class StudysetRelease
|
|||
DateTime releasedAt;
|
||||
|
||||
/// 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
|
||||
/// repository so the listing never reads git.
|
||||
String title;
|
||||
|
|
@ -152,6 +156,11 @@ abstract class StudysetRelease
|
|||
/// (`halkyon.eu/@alice/japanese`): what it is based on.
|
||||
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
|
||||
_is.Table<int?> get table => t;
|
||||
|
||||
|
|
@ -180,6 +189,7 @@ abstract class StudysetRelease
|
|||
String? licenseHolder,
|
||||
int? licenseYear,
|
||||
String? basedOn,
|
||||
String? colorName,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
|
|
@ -206,6 +216,7 @@ abstract class StudysetRelease
|
|||
if (licenseHolder != null) 'licenseHolder': licenseHolder,
|
||||
if (licenseYear != null) 'licenseYear': licenseYear,
|
||||
if (basedOn != null) 'basedOn': basedOn,
|
||||
if (colorName != null) 'colorName': colorName,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -267,6 +278,7 @@ class _StudysetReleaseImpl extends StudysetRelease {
|
|||
String? licenseHolder,
|
||||
int? licenseYear,
|
||||
String? basedOn,
|
||||
String? colorName,
|
||||
}) : super._(
|
||||
id: id,
|
||||
publishedStudysetId: publishedStudysetId,
|
||||
|
|
@ -289,6 +301,7 @@ class _StudysetReleaseImpl extends StudysetRelease {
|
|||
licenseHolder: licenseHolder,
|
||||
licenseYear: licenseYear,
|
||||
basedOn: basedOn,
|
||||
colorName: colorName,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [StudysetRelease]
|
||||
|
|
@ -317,6 +330,7 @@ class _StudysetReleaseImpl extends StudysetRelease {
|
|||
Object? licenseHolder = _Undefined,
|
||||
Object? licenseYear = _Undefined,
|
||||
Object? basedOn = _Undefined,
|
||||
Object? colorName = _Undefined,
|
||||
}) {
|
||||
return StudysetRelease(
|
||||
id: id is int? ? id : this.id,
|
||||
|
|
@ -346,6 +360,7 @@ class _StudysetReleaseImpl extends StudysetRelease {
|
|||
: this.licenseHolder,
|
||||
licenseYear: licenseYear is int? ? licenseYear : this.licenseYear,
|
||||
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,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<String, String> colorName(String? value) => _is.ColumnValue(
|
||||
table.colorName,
|
||||
value,
|
||||
);
|
||||
}
|
||||
|
||||
class StudysetReleaseTable extends _is.Table<int?> {
|
||||
|
|
@ -544,6 +564,10 @@ class StudysetReleaseTable extends _is.Table<int?> {
|
|||
'basedOn',
|
||||
this,
|
||||
);
|
||||
colorName = _is.ColumnString(
|
||||
'colorName',
|
||||
this,
|
||||
);
|
||||
}
|
||||
|
||||
late final StudysetReleaseUpdateTable updateTable;
|
||||
|
|
@ -580,7 +604,8 @@ class StudysetReleaseTable extends _is.Table<int?> {
|
|||
late final _is.ColumnDateTime releasedAt;
|
||||
|
||||
/// 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
|
||||
/// repository so the listing never reads git.
|
||||
late final _is.ColumnString title;
|
||||
|
|
@ -601,6 +626,11 @@ class StudysetReleaseTable extends _is.Table<int?> {
|
|||
/// (`halkyon.eu/@alice/japanese`): what it is based on.
|
||||
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
|
||||
List<_is.Column> get columns => [
|
||||
id,
|
||||
|
|
@ -624,6 +654,7 @@ class StudysetReleaseTable extends _is.Table<int?> {
|
|||
licenseHolder,
|
||||
licenseYear,
|
||||
basedOn,
|
||||
colorName,
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -80,6 +80,11 @@ class PublishEndpoint extends Endpoint {
|
|||
/// Publishes your studyset [studysetId] with everything inside it as its
|
||||
/// next release, [notes] saying what changed; at the first, under the
|
||||
/// 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
|
||||
/// [PublishRefusal]), and [NotFoundException] for a studyset that isn't
|
||||
|
|
@ -88,8 +93,9 @@ class PublishEndpoint extends Endpoint {
|
|||
Session session,
|
||||
UuidValue studysetId,
|
||||
String slug,
|
||||
String notes,
|
||||
) async {
|
||||
String notes, {
|
||||
String? description,
|
||||
}) async {
|
||||
final person = session.authenticated!.authUserId;
|
||||
final key = '${person.uuid}/${studysetId.uuid}';
|
||||
if (!_publishing.add(key)) {
|
||||
|
|
@ -102,6 +108,7 @@ class PublishEndpoint extends Endpoint {
|
|||
studysetId: studysetId,
|
||||
slug: slug.trim(),
|
||||
notes: notes.trim(),
|
||||
description: _richText(description),
|
||||
);
|
||||
} finally {
|
||||
_publishing.remove(key);
|
||||
|
|
@ -140,6 +147,7 @@ class PublishEndpoint extends Endpoint {
|
|||
required UuidValue studysetId,
|
||||
required String slug,
|
||||
required String notes,
|
||||
required String? description,
|
||||
}) async {
|
||||
// SELECT … FROM "public_profile" WHERE "authUserId" = $1
|
||||
final profile = await PublicProfile.db.findFirstRow(
|
||||
|
|
@ -309,12 +317,14 @@ class PublishEndpoint extends Endpoint {
|
|||
releasedAt: now,
|
||||
title: text('title') ?? '',
|
||||
secondaryTitle: text('secondaryTitle'),
|
||||
description: switch (root['description']) {
|
||||
final Map<String, Object?> description => jsonEncode(
|
||||
description,
|
||||
),
|
||||
_ => null,
|
||||
},
|
||||
description:
|
||||
description ??
|
||||
switch (root['description']) {
|
||||
final Map<String, Object?> description => jsonEncode(
|
||||
description,
|
||||
),
|
||||
_ => null,
|
||||
},
|
||||
frontLanguage: text('frontLanguage'),
|
||||
backLanguage: text('backLanguage'),
|
||||
licenseHolder: text('licenseHolder'),
|
||||
|
|
@ -323,6 +333,7 @@ class PublishEndpoint extends Endpoint {
|
|||
_ => null,
|
||||
},
|
||||
basedOn: basedOn,
|
||||
colorName: text('color'),
|
||||
),
|
||||
transaction: transaction,
|
||||
);
|
||||
|
|
@ -525,6 +536,7 @@ class PublishEndpoint extends Endpoint {
|
|||
pathCount: release.pathCount,
|
||||
pdfCount: release.pdfCount,
|
||||
releasedAt: release.releasedAt,
|
||||
description: release.description,
|
||||
),
|
||||
],
|
||||
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
|
||||
### When it was released, in UTC.
|
||||
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.
|
||||
releasedAt: DateTime
|
||||
### 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
|
||||
### repository so the listing never reads git.
|
||||
title: String, default=''
|
||||
|
|
@ -37,6 +38,10 @@ fields:
|
|||
### For a copy of a published studyset, where that one is
|
||||
### (`halkyon.eu/@alice/japanese`): what it is based on.
|
||||
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:
|
||||
studyset_release_version_idx:
|
||||
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
|
||||
20261012162411835-studyset-deleted-at
|
||||
20261013035552510-dictionary-keys
|
||||
20261014153932647-release-colour
|
||||
|
|
|
|||
|
|
@ -162,6 +162,7 @@ void main() {
|
|||
'license': ?license,
|
||||
'licenseHolder': 'Alice',
|
||||
'study': {'newPerDay': 10},
|
||||
'color': 'cobalt',
|
||||
'published': ?copyOf,
|
||||
'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(
|
||||
'a version git has but the database lost is never given again',
|
||||
() async {
|
||||
|
|
|
|||
|
|
@ -1826,8 +1826,9 @@ class _PublishEndpoint {
|
|||
_ist.TestSessionBuilder sessionBuilder,
|
||||
_is.UuidValue studysetId,
|
||||
String slug,
|
||||
String notes,
|
||||
) async {
|
||||
String notes, {
|
||||
String? description,
|
||||
}) async {
|
||||
return _ist.callAwaitableFunctionAndHandleExceptions(() async {
|
||||
var _localUniqueSession =
|
||||
(sessionBuilder as _ist.InternalTestSessionBuilder).internalBuild(
|
||||
|
|
@ -1843,6 +1844,7 @@ class _PublishEndpoint {
|
|||
'studysetId': studysetId,
|
||||
'slug': slug,
|
||||
'notes': notes,
|
||||
'description': description,
|
||||
}),
|
||||
serializationManager: _serializationManager,
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in a new issue