feat(discussions): questions on published studysets, answered by learners
A learner asks about a published studyset, a topic in it, or one card, note or step of a release, and others answer, as on Stack Exchange (ADR 0013): questions and answers in the app's editor without images, sounds or videos, comments of up to 600 characters under the question and each answer. There are no votes: the asker accepts an answer, which comes first, then the oldest; the author's posts are marked. The instance reads from git's file list which topics a question's target sits in, so a topic lists what is asked within it and below it. Posts live in two server-only tables, written through endpoints, so the synced tables stay as they are and v0.17.0 keeps syncing. Everyone signed in reads; writing needs an identity and a public profile; the studyset's author hides posts and blocks people with feedback's blocks; a withdrawn studyset takes no new questions; anyone writes at most 30 posts an hour. A post with others' replies under it is emptied when deleted, else deleted; deletePerson deletes what a person wrote the same way, and a published studyset's deletion takes its discussions with it. The migration adds Serverpod's gen_random_uuid_v7(), discussion_post and discussion_seen with their indexes and cascading foreign keys, and changes nothing else. In the app, discussions open from a copy's studyset or topic menu, a studied card's "Ask about this", Explore's summary and the Publish sheet; the Profile tab's inbox lists those the person follows, and its count includes what is new in them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
25237b8011
commit
5efa552489
80 changed files with 28214 additions and 27 deletions
File diff suppressed because one or more lines are too long
78
docs/adr/0013-discussions.md
Normal file
78
docs/adr/0013-discussions.md
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
# ADR 0013: Discussions on published studysets
|
||||
|
||||
- **Status:** accepted
|
||||
- **Date:** 2026-10-15
|
||||
|
||||
## Context
|
||||
|
||||
A published studyset has reports and suggestions (ADR 0005, rule 11): a line between one
|
||||
writer and its author, written as rows in each writer's own synced space and gathered by
|
||||
the instance as they are read. Learners also have questions about what they learn, and
|
||||
other learners can answer them, as on Stack Exchange; the developer's example was
|
||||
"Meaning of 青い衝撃 in this situation?" in Japanese › Grammar. The working notes (11 Oct)
|
||||
planned these as discussions "built on the feedback threads", the step before federation.
|
||||
|
||||
Feedback's storage doesn't fit them well: many people answer, the asker accepts one,
|
||||
posts are edited, and lists are ordered and paged by activity. Rows spread across
|
||||
writers' spaces could only encode acceptance in decision rows, and could never be edited
|
||||
(synced content is only ever blanked). New synced tables would make every app older than
|
||||
the change stop syncing (the sync engine refuses an app whose synced tables differ).
|
||||
|
||||
## Decision
|
||||
|
||||
The developer decided (2026-10-15), each as recommended:
|
||||
|
||||
1. **Server tables, not synced.** `discussion_post` holds questions, answers and
|
||||
comments, `discussion_seen` when each person last read a discussion; both are
|
||||
server-only, written through endpoints, online only. Counting, ordering, accepting and
|
||||
editing are plain queries, posts are ready to federate later (ADR 0005, stages 5 and
|
||||
6), and the synced tables stay as they are, so older apps keep syncing.
|
||||
2. **Questions, answers and comments**, as Stack Exchange has them: comments go under the
|
||||
question and under each answer, never under a comment; questions and answers are rich
|
||||
text in the app's editor (without images, sounds or videos: the person's files are
|
||||
theirs alone, and nobody else could load them), comments plain text of up to 600
|
||||
characters.
|
||||
3. **No votes.** The accepted answer first, then the oldest first; the author's posts
|
||||
marked. Votes cost nothing to fake (rule 8 of ADR 0005), so they count for nothing.
|
||||
The asker accepts an answer, and can take it back.
|
||||
4. **About the studyset, a topic or an item.** A question names what it is about as the
|
||||
release names it: the published studyset or a topic in it, a card, a note or a step,
|
||||
by its id in a release. The instance reads where that sits in the release (git's file
|
||||
list alone) and keeps the topics it is within, so a topic lists what is asked within
|
||||
it and in the topics below it. A copy's items know their ids and releases, so the app
|
||||
asks about them directly; Explore asks about the studyset as a whole, naming no item.
|
||||
5. **Deleting keeps others' replies.** A post nobody else replied under goes for good; a
|
||||
post with others' replies under it stays for them, its title, text and writer gone
|
||||
("Deleted question"). Deleting a person deletes what they wrote the same way, in
|
||||
`deletePerson`'s transaction; deleting a published studyset deletes its discussions.
|
||||
6. **The feedback rules stay**: everyone signed in reads; writing needs an identity and a
|
||||
public profile (posts name their writer); the studyset's author hides posts (then
|
||||
only their writer and the author see them) and blocks people (the same blocks as
|
||||
feedback's: what a blocked person writes shows to them alone). A withdrawn studyset
|
||||
keeps its discussions readable and answerable, but takes no new questions. A person
|
||||
writes at most 30 posts an hour.
|
||||
7. **Where it shows**: a copy's menu opens the discussions within that studyset or topic;
|
||||
a studied card from a release offers "Ask about this"; Explore's summary and the
|
||||
author's Publish sheet open the whole studyset's; the person's inbox (Profile) lists
|
||||
the discussions they follow (on their studysets, or asked, answered or commented in),
|
||||
what others wrote since they last read one marked and counted on the Profile tab.
|
||||
|
||||
## Consequences
|
||||
|
||||
- The synced tables don't change: v0.17.0 keeps syncing with an instance that has
|
||||
discussions.
|
||||
- Writing needs the instance; a question being written stays in the composer meanwhile.
|
||||
- Moderation is the author's alone; an instance's operator has no tools for it yet.
|
||||
- Federation (Fedify, ADR 0005 rule 7) can announce posts as the instance holds them.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- **Rows in each writer's synced space, as feedback**: offline writing, one pattern for
|
||||
both; but no edits, and acceptance and ordering pieced together from many spaces.
|
||||
- **New synced tables**: offline and clean, but older apps stop syncing until updated.
|
||||
- **Votes ordering answers**, or shown without ordering: the familiar way, but votes are
|
||||
free to fake, and Explore already refuses to rank by them.
|
||||
- **Flat replies** (a forum thread): simpler, but no accepted answer, and short remarks mix
|
||||
with answers.
|
||||
- **Everything goes with a deleted question**: simpler, but people's answers would vanish
|
||||
with someone else's choice.
|
||||
|
|
@ -32,6 +32,14 @@ import 'package:halkyon_learn_client/src/protocol/dictionary/dictionary_key_hit.
|
|||
as _ii0vb6vg;
|
||||
import 'package:halkyon_learn_client/src/protocol/dictionary/dictionary_source.dart'
|
||||
as _i8vj64sp;
|
||||
import 'package:halkyon_learn_client/src/protocol/discussion/discussion_refusal.dart'
|
||||
as _ikbvm73t;
|
||||
import 'package:halkyon_learn_client/src/protocol/discussion/discussion_summary.dart'
|
||||
as _i7lxdcg2;
|
||||
import 'package:halkyon_learn_client/src/protocol/discussion/discussion_target.dart'
|
||||
as _iy0rtcfg;
|
||||
import 'package:halkyon_learn_client/src/protocol/discussion/discussion_view.dart'
|
||||
as _i31gsvwz;
|
||||
import 'package:halkyon_learn_client/src/protocol/explore/explore_entry.dart'
|
||||
as _iyigb6vc;
|
||||
import 'package:halkyon_learn_client/src/protocol/explore/explore_query.dart'
|
||||
|
|
@ -508,6 +516,214 @@ class EndpointDictionary extends _isc.EndpointRef {
|
|||
);
|
||||
}
|
||||
|
||||
/// A published studyset's discussions (ADR 0013), for everyone signed in to
|
||||
/// read: questions about it, a topic in it or one of its items, with their
|
||||
/// answers and comments; but for what its author hid, and what people the
|
||||
/// author blocked wrote.
|
||||
/// {@category Endpoint}
|
||||
class EndpointDiscussions extends _isc.EndpointRef {
|
||||
EndpointDiscussions(_isc.EndpointCaller caller) : super(caller);
|
||||
|
||||
@override
|
||||
String get name => 'discussions';
|
||||
|
||||
/// The questions on the published studyset [publicId], those last active
|
||||
/// first, [discussionPage] to a [page] (the first without): within [topic] (a studyset of
|
||||
/// the release, by its id: questions about it and what is inside it),
|
||||
/// about [item] alone, or with [query] in their titles. A page may hold
|
||||
/// fewer than it could, when the caller may not see some. Throws
|
||||
/// [NotFoundException] for a studyset never published.
|
||||
_ida.Future<List<_i7lxdcg2.DiscussionSummary>> list(
|
||||
_isc.UuidValue publicId, {
|
||||
String? topic,
|
||||
_iy0rtcfg.DiscussionTarget? item,
|
||||
String? query,
|
||||
int? page,
|
||||
}) => caller.callServerEndpoint<List<_i7lxdcg2.DiscussionSummary>>(
|
||||
'discussions',
|
||||
'list',
|
||||
{
|
||||
'publicId': publicId,
|
||||
'topic': topic,
|
||||
'item': item,
|
||||
'query': query,
|
||||
'page': page,
|
||||
},
|
||||
);
|
||||
|
||||
/// The question [questionId] with its answers and comments, as the caller
|
||||
/// may see them. Throws [NotFoundException] for one they may not see.
|
||||
_ida.Future<_i31gsvwz.DiscussionView> read(_isc.UuidValue questionId) =>
|
||||
caller.callServerEndpoint<_i31gsvwz.DiscussionView>(
|
||||
'discussions',
|
||||
'read',
|
||||
{'questionId': questionId},
|
||||
);
|
||||
|
||||
/// Why the caller may not ask about the published studyset [publicId];
|
||||
/// null when they may. Throws [NotFoundException] for one never
|
||||
/// published.
|
||||
_ida.Future<_ikbvm73t.DiscussionRefusal?> askRefusal(
|
||||
_isc.UuidValue publicId,
|
||||
) => caller.callServerEndpoint<_ikbvm73t.DiscussionRefusal?>(
|
||||
'discussions',
|
||||
'askRefusal',
|
||||
{'publicId': publicId},
|
||||
);
|
||||
}
|
||||
|
||||
/// Writing in published studysets' discussions (ADR 0013), and the inbox
|
||||
/// of those a person follows. For people with an identity and a public
|
||||
/// profile, whom the studyset's author hasn't blocked: posts name their
|
||||
/// writer.
|
||||
/// {@category Endpoint}
|
||||
class EndpointDiscussion extends _isc.EndpointRef {
|
||||
EndpointDiscussion(_isc.EndpointCaller caller) : super(caller);
|
||||
|
||||
@override
|
||||
String get name => 'discussion';
|
||||
|
||||
/// Asks [title], with [body] (rich text), about [target] of the published
|
||||
/// studyset [publicId] (the studyset as a whole when it names no item);
|
||||
/// returns the question's id. Throws
|
||||
/// [DiscussionRefusedException] when the caller may not, the text won't
|
||||
/// do, the release lacks the target or the studyset was withdrawn, and
|
||||
/// [NotFoundException] for a studyset never published.
|
||||
_ida.Future<_isc.UuidValue> ask(
|
||||
_isc.UuidValue publicId,
|
||||
_iy0rtcfg.DiscussionTarget target,
|
||||
String title,
|
||||
String body,
|
||||
) => caller.callServerEndpoint<_isc.UuidValue>(
|
||||
'discussion',
|
||||
'ask',
|
||||
{
|
||||
'publicId': publicId,
|
||||
'target': target,
|
||||
'title': title,
|
||||
'body': body,
|
||||
},
|
||||
);
|
||||
|
||||
/// Answers the question [questionId] with [body] (rich text); returns the
|
||||
/// answer's id. Throws [NotFoundException] for a question the caller
|
||||
/// can't see, or one deleted, and [DiscussionRefusedException] when they
|
||||
/// may not write or the text won't do.
|
||||
_ida.Future<_isc.UuidValue> answer(
|
||||
_isc.UuidValue questionId,
|
||||
String body,
|
||||
) => caller.callServerEndpoint<_isc.UuidValue>(
|
||||
'discussion',
|
||||
'answer',
|
||||
{
|
||||
'questionId': questionId,
|
||||
'body': body,
|
||||
},
|
||||
);
|
||||
|
||||
/// Comments [text] (plain text) on [onId], a question or an answer;
|
||||
/// returns the comment's id. Throws [NotFoundException] for something the
|
||||
/// caller can't see, a comment, or something deleted, and
|
||||
/// [DiscussionRefusedException] when they may not write or the text won't
|
||||
/// do.
|
||||
_ida.Future<_isc.UuidValue> comment(
|
||||
_isc.UuidValue onId,
|
||||
String text,
|
||||
) => caller.callServerEndpoint<_isc.UuidValue>(
|
||||
'discussion',
|
||||
'comment',
|
||||
{
|
||||
'onId': onId,
|
||||
'text': text,
|
||||
},
|
||||
);
|
||||
|
||||
/// Changes what the caller's post [postId] says to [body] (rich text, a
|
||||
/// comment's plain text), a question's title to [title] where given.
|
||||
/// Throws [NotFoundException] for a post that isn't theirs, or one
|
||||
/// deleted, and [DiscussionRefusedException] when the text won't do.
|
||||
_ida.Future<void> edit(
|
||||
_isc.UuidValue postId,
|
||||
String body, {
|
||||
String? title,
|
||||
}) => caller.callServerEndpoint<void>(
|
||||
'discussion',
|
||||
'edit',
|
||||
{
|
||||
'postId': postId,
|
||||
'body': body,
|
||||
'title': title,
|
||||
},
|
||||
);
|
||||
|
||||
/// Deletes the caller's post [postId]: for good when nobody else replied
|
||||
/// under it, else its title, text and writer go and it stays for the
|
||||
/// replies. Throws [NotFoundException] for a post that isn't theirs.
|
||||
_ida.Future<void> delete(_isc.UuidValue postId) =>
|
||||
caller.callServerEndpoint<void>(
|
||||
'discussion',
|
||||
'delete',
|
||||
{'postId': postId},
|
||||
);
|
||||
|
||||
/// Accepts [answerId] as the answer to the caller's question
|
||||
/// [questionId], in place of any before; null accepts none. Throws
|
||||
/// [NotFoundException] for a question that isn't theirs, or an answer
|
||||
/// that isn't one of its own.
|
||||
_ida.Future<void> accept(
|
||||
_isc.UuidValue questionId,
|
||||
_isc.UuidValue? answerId,
|
||||
) => caller.callServerEndpoint<void>(
|
||||
'discussion',
|
||||
'accept',
|
||||
{
|
||||
'questionId': questionId,
|
||||
'answerId': answerId,
|
||||
},
|
||||
);
|
||||
|
||||
/// Hides the post [postId] on one of the caller's published studysets
|
||||
/// ([hidden]), or shows it again: hidden, only its writer and the author
|
||||
/// see it. Throws [NotFoundException] for a post on anyone else's.
|
||||
_ida.Future<void> hide(
|
||||
_isc.UuidValue postId,
|
||||
bool hidden,
|
||||
) => caller.callServerEndpoint<void>(
|
||||
'discussion',
|
||||
'hide',
|
||||
{
|
||||
'postId': postId,
|
||||
'hidden': hidden,
|
||||
},
|
||||
);
|
||||
|
||||
/// The discussions the caller follows, those last active first: those on
|
||||
/// their published studysets, and those they asked, answered or commented
|
||||
/// in; each saying whether others wrote in it since they last read it.
|
||||
_ida.Future<List<_i7lxdcg2.DiscussionSummary>> inbox() =>
|
||||
caller.callServerEndpoint<List<_i7lxdcg2.DiscussionSummary>>(
|
||||
'discussion',
|
||||
'inbox',
|
||||
{},
|
||||
);
|
||||
|
||||
/// How many discussions of the caller's inbox have something new.
|
||||
_ida.Future<int> unseen() => caller.callServerEndpoint<int>(
|
||||
'discussion',
|
||||
'unseen',
|
||||
{},
|
||||
);
|
||||
|
||||
/// Records that the caller has read the discussion [questionId] as it is
|
||||
/// now. Throws [NotFoundException] for a question there isn't.
|
||||
_ida.Future<void> markSeen(_isc.UuidValue questionId) =>
|
||||
caller.callServerEndpoint<void>(
|
||||
'discussion',
|
||||
'markSeen',
|
||||
{'questionId': questionId},
|
||||
);
|
||||
}
|
||||
|
||||
/// Explore (ADR 0005, rule 4): the studysets published on this instance,
|
||||
/// found by fit (search, languages, kinds of content), never by popularity,
|
||||
/// the newest release first; and their releases, to learn from as a copy.
|
||||
|
|
@ -934,6 +1150,8 @@ class Client extends _isc.ServerpodClientShared {
|
|||
gitHubSignIn = EndpointGitHubSignIn(this);
|
||||
gitHubUpgrade = EndpointGitHubUpgrade(this);
|
||||
dictionary = EndpointDictionary(this);
|
||||
discussions = EndpointDiscussions(this);
|
||||
discussion = EndpointDiscussion(this);
|
||||
explore = EndpointExplore(this);
|
||||
feedback = EndpointFeedback(this);
|
||||
inbox = EndpointInbox(this);
|
||||
|
|
@ -964,6 +1182,10 @@ class Client extends _isc.ServerpodClientShared {
|
|||
|
||||
late final EndpointDictionary dictionary;
|
||||
|
||||
late final EndpointDiscussions discussions;
|
||||
|
||||
late final EndpointDiscussion discussion;
|
||||
|
||||
late final EndpointExplore explore;
|
||||
|
||||
late final EndpointFeedback feedback;
|
||||
|
|
@ -992,6 +1214,8 @@ class Client extends _isc.ServerpodClientShared {
|
|||
'gitHubSignIn': gitHubSignIn,
|
||||
'gitHubUpgrade': gitHubUpgrade,
|
||||
'dictionary': dictionary,
|
||||
'discussions': discussions,
|
||||
'discussion': discussion,
|
||||
'explore': explore,
|
||||
'feedback': feedback,
|
||||
'inbox': inbox,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,239 @@
|
|||
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
|
||||
/* To generate run: "serverpod generate" */
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: library_private_types_in_public_api
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
// ignore_for_file: public_member_api_docs
|
||||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'package:halkyon_learn_client/src/protocol/protocol.dart' as _itx5j3f5;
|
||||
import 'package:serverpod_client/serverpod_client.dart' as _isc;
|
||||
import '../feedback/feedback_person.dart' as _i4q3oto5;
|
||||
|
||||
/// A question, an answer or a comment as the app shows it.
|
||||
abstract class DiscussionPostView
|
||||
implements _isc.SerializableModel, _isc.ProtocolSerialization {
|
||||
DiscussionPostView._({
|
||||
required this.id,
|
||||
required this.kind,
|
||||
this.parentId,
|
||||
this.by,
|
||||
required this.fromAuthor,
|
||||
this.title,
|
||||
required this.body,
|
||||
required this.createdAt,
|
||||
this.editedAt,
|
||||
required this.deleted,
|
||||
required this.hidden,
|
||||
required this.mine,
|
||||
});
|
||||
|
||||
factory DiscussionPostView({
|
||||
required _isc.UuidValue id,
|
||||
required String kind,
|
||||
_isc.UuidValue? parentId,
|
||||
_i4q3oto5.FeedbackPerson? by,
|
||||
required bool fromAuthor,
|
||||
String? title,
|
||||
required String body,
|
||||
required DateTime createdAt,
|
||||
DateTime? editedAt,
|
||||
required bool deleted,
|
||||
required bool hidden,
|
||||
required bool mine,
|
||||
}) = _DiscussionPostViewImpl;
|
||||
|
||||
factory DiscussionPostView.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return DiscussionPostView(
|
||||
id: _isc.UuidValueJsonExtension.fromJson(jsonSerialization['id']),
|
||||
kind: jsonSerialization['kind'] as String,
|
||||
parentId: jsonSerialization['parentId'] == null
|
||||
? null
|
||||
: _isc.UuidValueJsonExtension.fromJson(jsonSerialization['parentId']),
|
||||
by: jsonSerialization['by'] == null
|
||||
? null
|
||||
: _itx5j3f5.Protocol().deserialize<_i4q3oto5.FeedbackPerson>(
|
||||
jsonSerialization['by'],
|
||||
),
|
||||
fromAuthor: _isc.BoolJsonExtension.fromJson(
|
||||
jsonSerialization['fromAuthor'],
|
||||
),
|
||||
title: jsonSerialization['title'] as String?,
|
||||
body: jsonSerialization['body'] as String,
|
||||
createdAt: _isc.DateTimeJsonExtension.fromJson(
|
||||
jsonSerialization['createdAt'],
|
||||
),
|
||||
editedAt: jsonSerialization['editedAt'] == null
|
||||
? null
|
||||
: _isc.DateTimeJsonExtension.fromJson(jsonSerialization['editedAt']),
|
||||
deleted: _isc.BoolJsonExtension.fromJson(jsonSerialization['deleted']),
|
||||
hidden: _isc.BoolJsonExtension.fromJson(jsonSerialization['hidden']),
|
||||
mine: _isc.BoolJsonExtension.fromJson(jsonSerialization['mine']),
|
||||
);
|
||||
}
|
||||
|
||||
_isc.UuidValue id;
|
||||
|
||||
/// 'question', 'answer' or 'comment'.
|
||||
String kind;
|
||||
|
||||
/// What a comment is on, its question or an answer; null otherwise.
|
||||
_isc.UuidValue? parentId;
|
||||
|
||||
/// Who wrote it, as their public profile shows them; null once deleted,
|
||||
/// or when its writer has no public profile any more.
|
||||
_i4q3oto5.FeedbackPerson? by;
|
||||
|
||||
/// Whether its writer is the published studyset's author.
|
||||
bool fromAuthor;
|
||||
|
||||
/// A question's title.
|
||||
String? title;
|
||||
|
||||
/// What it says: rich text (the document's JSON), a comment's plain text;
|
||||
/// empty once deleted.
|
||||
String body;
|
||||
|
||||
/// When it was written and last edited, in UTC.
|
||||
DateTime createdAt;
|
||||
|
||||
DateTime? editedAt;
|
||||
|
||||
bool deleted;
|
||||
|
||||
/// Whether the author hid it: then only its writer and the author see it.
|
||||
bool hidden;
|
||||
|
||||
/// Whether the viewer wrote it.
|
||||
bool mine;
|
||||
|
||||
/// Returns a shallow copy of this [DiscussionPostView]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_isc.useResult
|
||||
DiscussionPostView copyWith({
|
||||
_isc.UuidValue? id,
|
||||
String? kind,
|
||||
_isc.UuidValue? parentId,
|
||||
_i4q3oto5.FeedbackPerson? by,
|
||||
bool? fromAuthor,
|
||||
String? title,
|
||||
String? body,
|
||||
DateTime? createdAt,
|
||||
DateTime? editedAt,
|
||||
bool? deleted,
|
||||
bool? hidden,
|
||||
bool? mine,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'DiscussionPostView',
|
||||
'id': id.toJson(),
|
||||
'kind': kind,
|
||||
if (parentId != null) 'parentId': parentId?.toJson(),
|
||||
if (by != null) 'by': by?.toJson(),
|
||||
'fromAuthor': fromAuthor,
|
||||
if (title != null) 'title': title,
|
||||
'body': body,
|
||||
'createdAt': createdAt.toJson(),
|
||||
if (editedAt != null) 'editedAt': editedAt?.toJson(),
|
||||
'deleted': deleted,
|
||||
'hidden': hidden,
|
||||
'mine': mine,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJsonForProtocol() {
|
||||
return {
|
||||
'__className__': 'DiscussionPostView',
|
||||
'id': id.toJson(),
|
||||
'kind': kind,
|
||||
if (parentId != null) 'parentId': parentId?.toJson(),
|
||||
if (by != null) 'by': by?.toJsonForProtocol(),
|
||||
'fromAuthor': fromAuthor,
|
||||
if (title != null) 'title': title,
|
||||
'body': body,
|
||||
'createdAt': createdAt.toJson(),
|
||||
if (editedAt != null) 'editedAt': editedAt?.toJson(),
|
||||
'deleted': deleted,
|
||||
'hidden': hidden,
|
||||
'mine': mine,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _isc.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _Undefined {}
|
||||
|
||||
class _DiscussionPostViewImpl extends DiscussionPostView {
|
||||
_DiscussionPostViewImpl({
|
||||
required _isc.UuidValue id,
|
||||
required String kind,
|
||||
_isc.UuidValue? parentId,
|
||||
_i4q3oto5.FeedbackPerson? by,
|
||||
required bool fromAuthor,
|
||||
String? title,
|
||||
required String body,
|
||||
required DateTime createdAt,
|
||||
DateTime? editedAt,
|
||||
required bool deleted,
|
||||
required bool hidden,
|
||||
required bool mine,
|
||||
}) : super._(
|
||||
id: id,
|
||||
kind: kind,
|
||||
parentId: parentId,
|
||||
by: by,
|
||||
fromAuthor: fromAuthor,
|
||||
title: title,
|
||||
body: body,
|
||||
createdAt: createdAt,
|
||||
editedAt: editedAt,
|
||||
deleted: deleted,
|
||||
hidden: hidden,
|
||||
mine: mine,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [DiscussionPostView]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_isc.useResult
|
||||
@override
|
||||
DiscussionPostView copyWith({
|
||||
_isc.UuidValue? id,
|
||||
String? kind,
|
||||
Object? parentId = _Undefined,
|
||||
Object? by = _Undefined,
|
||||
bool? fromAuthor,
|
||||
Object? title = _Undefined,
|
||||
String? body,
|
||||
DateTime? createdAt,
|
||||
Object? editedAt = _Undefined,
|
||||
bool? deleted,
|
||||
bool? hidden,
|
||||
bool? mine,
|
||||
}) {
|
||||
return DiscussionPostView(
|
||||
id: id ?? this.id,
|
||||
kind: kind ?? this.kind,
|
||||
parentId: parentId is _isc.UuidValue? ? parentId : this.parentId,
|
||||
by: by is _i4q3oto5.FeedbackPerson? ? by : this.by?.copyWith(),
|
||||
fromAuthor: fromAuthor ?? this.fromAuthor,
|
||||
title: title is String? ? title : this.title,
|
||||
body: body ?? this.body,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
editedAt: editedAt is DateTime? ? editedAt : this.editedAt,
|
||||
deleted: deleted ?? this.deleted,
|
||||
hidden: hidden ?? this.hidden,
|
||||
mine: mine ?? this.mine,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
|
||||
/* To generate run: "serverpod generate" */
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: library_private_types_in_public_api
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
// ignore_for_file: public_member_api_docs
|
||||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'package:serverpod_client/serverpod_client.dart' as _isc;
|
||||
|
||||
/// Why writing in a discussion was refused; nothing changed.
|
||||
enum DiscussionRefusal implements _isc.SerializableModel {
|
||||
/// The person signed in without an identity: anything involving other
|
||||
/// people needs one.
|
||||
noIdentity,
|
||||
|
||||
/// The person has no public profile: posts name their writer.
|
||||
noProfile,
|
||||
|
||||
/// The studyset's author keeps the person from writing on their
|
||||
/// studysets.
|
||||
blocked,
|
||||
|
||||
/// The author took the studyset off Explore: what is there stays
|
||||
/// readable, but nobody asks anything new.
|
||||
withdrawn,
|
||||
|
||||
/// The release doesn't hold what the question would be about.
|
||||
notInRelease,
|
||||
|
||||
/// A title or text left empty.
|
||||
empty,
|
||||
|
||||
/// A title, text or comment longer than allowed.
|
||||
tooLong,
|
||||
|
||||
/// More posts in the last hour than a person may write.
|
||||
tooMany;
|
||||
|
||||
static DiscussionRefusal fromJson(String name) {
|
||||
switch (name) {
|
||||
case 'noIdentity':
|
||||
return DiscussionRefusal.noIdentity;
|
||||
case 'noProfile':
|
||||
return DiscussionRefusal.noProfile;
|
||||
case 'blocked':
|
||||
return DiscussionRefusal.blocked;
|
||||
case 'withdrawn':
|
||||
return DiscussionRefusal.withdrawn;
|
||||
case 'notInRelease':
|
||||
return DiscussionRefusal.notInRelease;
|
||||
case 'empty':
|
||||
return DiscussionRefusal.empty;
|
||||
case 'tooLong':
|
||||
return DiscussionRefusal.tooLong;
|
||||
case 'tooMany':
|
||||
return DiscussionRefusal.tooMany;
|
||||
default:
|
||||
throw ArgumentError(
|
||||
'Value "$name" cannot be converted to "DiscussionRefusal"',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String toJson() => name;
|
||||
|
||||
@override
|
||||
String toString() => name;
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
|
||||
/* To generate run: "serverpod generate" */
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: library_private_types_in_public_api
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
// ignore_for_file: public_member_api_docs
|
||||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'package:serverpod_client/serverpod_client.dart' as _isc;
|
||||
import '../discussion/discussion_refusal.dart' as _i5ff9exj;
|
||||
|
||||
/// Writing in a discussion was refused; nothing changed.
|
||||
abstract class DiscussionRefusedException
|
||||
implements
|
||||
_isc.SerializableException,
|
||||
_isc.SerializableModel,
|
||||
_isc.ProtocolSerialization {
|
||||
DiscussionRefusedException._({required this.reason});
|
||||
|
||||
factory DiscussionRefusedException({
|
||||
required _i5ff9exj.DiscussionRefusal reason,
|
||||
}) = _DiscussionRefusedExceptionImpl;
|
||||
|
||||
factory DiscussionRefusedException.fromJson(
|
||||
Map<String, dynamic> jsonSerialization,
|
||||
) {
|
||||
return DiscussionRefusedException(
|
||||
reason: _i5ff9exj.DiscussionRefusal.fromJson(
|
||||
(jsonSerialization['reason'] as String),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Why it was refused.
|
||||
_i5ff9exj.DiscussionRefusal reason;
|
||||
|
||||
/// Returns a shallow copy of this [DiscussionRefusedException]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_isc.useResult
|
||||
DiscussionRefusedException copyWith({_i5ff9exj.DiscussionRefusal? reason});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'DiscussionRefusedException',
|
||||
'reason': reason.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJsonForProtocol() {
|
||||
return {
|
||||
'__className__': 'DiscussionRefusedException',
|
||||
'reason': reason.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'DiscussionRefusedException(reason: $reason)';
|
||||
}
|
||||
}
|
||||
|
||||
class _DiscussionRefusedExceptionImpl extends DiscussionRefusedException {
|
||||
_DiscussionRefusedExceptionImpl({required _i5ff9exj.DiscussionRefusal reason})
|
||||
: super._(reason: reason);
|
||||
|
||||
/// Returns a shallow copy of this [DiscussionRefusedException]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_isc.useResult
|
||||
@override
|
||||
DiscussionRefusedException copyWith({_i5ff9exj.DiscussionRefusal? reason}) {
|
||||
return DiscussionRefusedException(reason: reason ?? this.reason);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,281 @@
|
|||
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
|
||||
/* To generate run: "serverpod generate" */
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: library_private_types_in_public_api
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
// ignore_for_file: public_member_api_docs
|
||||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'package:halkyon_learn_client/src/protocol/protocol.dart' as _itx5j3f5;
|
||||
import 'package:serverpod_client/serverpod_client.dart' as _isc;
|
||||
import '../discussion/discussion_target.dart' as _iqswgn91;
|
||||
import '../feedback/feedback_person.dart' as _i4q3oto5;
|
||||
|
||||
/// A question as a list shows it.
|
||||
abstract class DiscussionSummary
|
||||
implements _isc.SerializableModel, _isc.ProtocolSerialization {
|
||||
DiscussionSummary._({
|
||||
required this.questionId,
|
||||
required this.publicId,
|
||||
required this.studysetTitle,
|
||||
required this.title,
|
||||
this.asker,
|
||||
required this.askerIsAuthor,
|
||||
required this.target,
|
||||
required this.createdAt,
|
||||
required this.activeAt,
|
||||
required this.answers,
|
||||
required this.accepted,
|
||||
required this.deleted,
|
||||
required this.hidden,
|
||||
required this.mine,
|
||||
required this.unseen,
|
||||
});
|
||||
|
||||
factory DiscussionSummary({
|
||||
required _isc.UuidValue questionId,
|
||||
required _isc.UuidValue publicId,
|
||||
required String studysetTitle,
|
||||
required String title,
|
||||
_i4q3oto5.FeedbackPerson? asker,
|
||||
required bool askerIsAuthor,
|
||||
required _iqswgn91.DiscussionTarget target,
|
||||
required DateTime createdAt,
|
||||
required DateTime activeAt,
|
||||
required int answers,
|
||||
required bool accepted,
|
||||
required bool deleted,
|
||||
required bool hidden,
|
||||
required bool mine,
|
||||
required bool unseen,
|
||||
}) = _DiscussionSummaryImpl;
|
||||
|
||||
factory DiscussionSummary.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return DiscussionSummary(
|
||||
questionId: _isc.UuidValueJsonExtension.fromJson(
|
||||
jsonSerialization['questionId'],
|
||||
),
|
||||
publicId: _isc.UuidValueJsonExtension.fromJson(
|
||||
jsonSerialization['publicId'],
|
||||
),
|
||||
studysetTitle: jsonSerialization['studysetTitle'] as String,
|
||||
title: jsonSerialization['title'] as String,
|
||||
asker: jsonSerialization['asker'] == null
|
||||
? null
|
||||
: _itx5j3f5.Protocol().deserialize<_i4q3oto5.FeedbackPerson>(
|
||||
jsonSerialization['asker'],
|
||||
),
|
||||
askerIsAuthor: _isc.BoolJsonExtension.fromJson(
|
||||
jsonSerialization['askerIsAuthor'],
|
||||
),
|
||||
target: _itx5j3f5.Protocol().deserialize<_iqswgn91.DiscussionTarget>(
|
||||
jsonSerialization['target'],
|
||||
),
|
||||
createdAt: _isc.DateTimeJsonExtension.fromJson(
|
||||
jsonSerialization['createdAt'],
|
||||
),
|
||||
activeAt: _isc.DateTimeJsonExtension.fromJson(
|
||||
jsonSerialization['activeAt'],
|
||||
),
|
||||
answers: jsonSerialization['answers'] as int,
|
||||
accepted: _isc.BoolJsonExtension.fromJson(jsonSerialization['accepted']),
|
||||
deleted: _isc.BoolJsonExtension.fromJson(jsonSerialization['deleted']),
|
||||
hidden: _isc.BoolJsonExtension.fromJson(jsonSerialization['hidden']),
|
||||
mine: _isc.BoolJsonExtension.fromJson(jsonSerialization['mine']),
|
||||
unseen: _isc.BoolJsonExtension.fromJson(jsonSerialization['unseen']),
|
||||
);
|
||||
}
|
||||
|
||||
_isc.UuidValue questionId;
|
||||
|
||||
/// The published studyset it is on, and its title.
|
||||
_isc.UuidValue publicId;
|
||||
|
||||
String studysetTitle;
|
||||
|
||||
/// Its title; empty once deleted.
|
||||
String title;
|
||||
|
||||
/// Who asked it; null once deleted, or without a public profile.
|
||||
_i4q3oto5.FeedbackPerson? asker;
|
||||
|
||||
bool askerIsAuthor;
|
||||
|
||||
/// What it is about.
|
||||
_iqswgn91.DiscussionTarget target;
|
||||
|
||||
/// When it was asked, and when anything last happened in it, in UTC.
|
||||
DateTime createdAt;
|
||||
|
||||
DateTime activeAt;
|
||||
|
||||
/// How many answers the viewer sees, and whether one is accepted.
|
||||
int answers;
|
||||
|
||||
bool accepted;
|
||||
|
||||
bool deleted;
|
||||
|
||||
bool hidden;
|
||||
|
||||
/// Whether the viewer asked it.
|
||||
bool mine;
|
||||
|
||||
/// Whether others wrote in it since the viewer last read it, where it is
|
||||
/// theirs to follow: they asked, answered or commented, or it is on their
|
||||
/// studyset.
|
||||
bool unseen;
|
||||
|
||||
/// Returns a shallow copy of this [DiscussionSummary]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_isc.useResult
|
||||
DiscussionSummary copyWith({
|
||||
_isc.UuidValue? questionId,
|
||||
_isc.UuidValue? publicId,
|
||||
String? studysetTitle,
|
||||
String? title,
|
||||
_i4q3oto5.FeedbackPerson? asker,
|
||||
bool? askerIsAuthor,
|
||||
_iqswgn91.DiscussionTarget? target,
|
||||
DateTime? createdAt,
|
||||
DateTime? activeAt,
|
||||
int? answers,
|
||||
bool? accepted,
|
||||
bool? deleted,
|
||||
bool? hidden,
|
||||
bool? mine,
|
||||
bool? unseen,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'DiscussionSummary',
|
||||
'questionId': questionId.toJson(),
|
||||
'publicId': publicId.toJson(),
|
||||
'studysetTitle': studysetTitle,
|
||||
'title': title,
|
||||
if (asker != null) 'asker': asker?.toJson(),
|
||||
'askerIsAuthor': askerIsAuthor,
|
||||
'target': target.toJson(),
|
||||
'createdAt': createdAt.toJson(),
|
||||
'activeAt': activeAt.toJson(),
|
||||
'answers': answers,
|
||||
'accepted': accepted,
|
||||
'deleted': deleted,
|
||||
'hidden': hidden,
|
||||
'mine': mine,
|
||||
'unseen': unseen,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJsonForProtocol() {
|
||||
return {
|
||||
'__className__': 'DiscussionSummary',
|
||||
'questionId': questionId.toJson(),
|
||||
'publicId': publicId.toJson(),
|
||||
'studysetTitle': studysetTitle,
|
||||
'title': title,
|
||||
if (asker != null) 'asker': asker?.toJsonForProtocol(),
|
||||
'askerIsAuthor': askerIsAuthor,
|
||||
'target': target.toJsonForProtocol(),
|
||||
'createdAt': createdAt.toJson(),
|
||||
'activeAt': activeAt.toJson(),
|
||||
'answers': answers,
|
||||
'accepted': accepted,
|
||||
'deleted': deleted,
|
||||
'hidden': hidden,
|
||||
'mine': mine,
|
||||
'unseen': unseen,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _isc.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _Undefined {}
|
||||
|
||||
class _DiscussionSummaryImpl extends DiscussionSummary {
|
||||
_DiscussionSummaryImpl({
|
||||
required _isc.UuidValue questionId,
|
||||
required _isc.UuidValue publicId,
|
||||
required String studysetTitle,
|
||||
required String title,
|
||||
_i4q3oto5.FeedbackPerson? asker,
|
||||
required bool askerIsAuthor,
|
||||
required _iqswgn91.DiscussionTarget target,
|
||||
required DateTime createdAt,
|
||||
required DateTime activeAt,
|
||||
required int answers,
|
||||
required bool accepted,
|
||||
required bool deleted,
|
||||
required bool hidden,
|
||||
required bool mine,
|
||||
required bool unseen,
|
||||
}) : super._(
|
||||
questionId: questionId,
|
||||
publicId: publicId,
|
||||
studysetTitle: studysetTitle,
|
||||
title: title,
|
||||
asker: asker,
|
||||
askerIsAuthor: askerIsAuthor,
|
||||
target: target,
|
||||
createdAt: createdAt,
|
||||
activeAt: activeAt,
|
||||
answers: answers,
|
||||
accepted: accepted,
|
||||
deleted: deleted,
|
||||
hidden: hidden,
|
||||
mine: mine,
|
||||
unseen: unseen,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [DiscussionSummary]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_isc.useResult
|
||||
@override
|
||||
DiscussionSummary copyWith({
|
||||
_isc.UuidValue? questionId,
|
||||
_isc.UuidValue? publicId,
|
||||
String? studysetTitle,
|
||||
String? title,
|
||||
Object? asker = _Undefined,
|
||||
bool? askerIsAuthor,
|
||||
_iqswgn91.DiscussionTarget? target,
|
||||
DateTime? createdAt,
|
||||
DateTime? activeAt,
|
||||
int? answers,
|
||||
bool? accepted,
|
||||
bool? deleted,
|
||||
bool? hidden,
|
||||
bool? mine,
|
||||
bool? unseen,
|
||||
}) {
|
||||
return DiscussionSummary(
|
||||
questionId: questionId ?? this.questionId,
|
||||
publicId: publicId ?? this.publicId,
|
||||
studysetTitle: studysetTitle ?? this.studysetTitle,
|
||||
title: title ?? this.title,
|
||||
asker: asker is _i4q3oto5.FeedbackPerson?
|
||||
? asker
|
||||
: this.asker?.copyWith(),
|
||||
askerIsAuthor: askerIsAuthor ?? this.askerIsAuthor,
|
||||
target: target ?? this.target.copyWith(),
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
activeAt: activeAt ?? this.activeAt,
|
||||
answers: answers ?? this.answers,
|
||||
accepted: accepted ?? this.accepted,
|
||||
deleted: deleted ?? this.deleted,
|
||||
hidden: hidden ?? this.hidden,
|
||||
mine: mine ?? this.mine,
|
||||
unseen: unseen ?? this.unseen,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
|
||||
/* To generate run: "serverpod generate" */
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: library_private_types_in_public_api
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
// ignore_for_file: public_member_api_docs
|
||||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'package:serverpod_client/serverpod_client.dart' as _isc;
|
||||
|
||||
/// What a question is about: the published studyset or a topic in it
|
||||
/// ('studyset'), or one of its cards, notes or steps ('card', 'note',
|
||||
/// 'step'), by its id in release [version].
|
||||
abstract class DiscussionTarget
|
||||
implements _isc.SerializableModel, _isc.ProtocolSerialization {
|
||||
DiscussionTarget._({
|
||||
required this.kind,
|
||||
required this.item,
|
||||
required this.version,
|
||||
});
|
||||
|
||||
factory DiscussionTarget({
|
||||
required String kind,
|
||||
required String item,
|
||||
required int version,
|
||||
}) = _DiscussionTargetImpl;
|
||||
|
||||
factory DiscussionTarget.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return DiscussionTarget(
|
||||
kind: jsonSerialization['kind'] as String,
|
||||
item: jsonSerialization['item'] as String,
|
||||
version: jsonSerialization['version'] as int,
|
||||
);
|
||||
}
|
||||
|
||||
String kind;
|
||||
|
||||
String item;
|
||||
|
||||
int version;
|
||||
|
||||
/// Returns a shallow copy of this [DiscussionTarget]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_isc.useResult
|
||||
DiscussionTarget copyWith({
|
||||
String? kind,
|
||||
String? item,
|
||||
int? version,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'DiscussionTarget',
|
||||
'kind': kind,
|
||||
'item': item,
|
||||
'version': version,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJsonForProtocol() {
|
||||
return {
|
||||
'__className__': 'DiscussionTarget',
|
||||
'kind': kind,
|
||||
'item': item,
|
||||
'version': version,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _isc.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _DiscussionTargetImpl extends DiscussionTarget {
|
||||
_DiscussionTargetImpl({
|
||||
required String kind,
|
||||
required String item,
|
||||
required int version,
|
||||
}) : super._(
|
||||
kind: kind,
|
||||
item: item,
|
||||
version: version,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [DiscussionTarget]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_isc.useResult
|
||||
@override
|
||||
DiscussionTarget copyWith({
|
||||
String? kind,
|
||||
String? item,
|
||||
int? version,
|
||||
}) {
|
||||
return DiscussionTarget(
|
||||
kind: kind ?? this.kind,
|
||||
item: item ?? this.item,
|
||||
version: version ?? this.version,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,236 @@
|
|||
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
|
||||
/* To generate run: "serverpod generate" */
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: library_private_types_in_public_api
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
// ignore_for_file: public_member_api_docs
|
||||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'package:halkyon_learn_client/src/protocol/protocol.dart' as _itx5j3f5;
|
||||
import 'package:serverpod_client/serverpod_client.dart' as _isc;
|
||||
import '../discussion/discussion_post_view.dart' as _iv1wx9mv;
|
||||
import '../discussion/discussion_refusal.dart' as _i5ff9exj;
|
||||
import '../discussion/discussion_target.dart' as _iqswgn91;
|
||||
|
||||
/// A discussion as the app shows it: the question, its answers, the accepted
|
||||
/// one first, then the oldest first, and the comments on both, oldest first.
|
||||
abstract class DiscussionView
|
||||
implements _isc.SerializableModel, _isc.ProtocolSerialization {
|
||||
DiscussionView._({
|
||||
required this.question,
|
||||
required this.publicId,
|
||||
required this.studysetTitle,
|
||||
required this.location,
|
||||
required this.target,
|
||||
required this.answers,
|
||||
required this.comments,
|
||||
this.acceptedAnswerId,
|
||||
required this.asker,
|
||||
required this.author,
|
||||
this.refusal,
|
||||
});
|
||||
|
||||
factory DiscussionView({
|
||||
required _iv1wx9mv.DiscussionPostView question,
|
||||
required _isc.UuidValue publicId,
|
||||
required String studysetTitle,
|
||||
required String location,
|
||||
required _iqswgn91.DiscussionTarget target,
|
||||
required List<_iv1wx9mv.DiscussionPostView> answers,
|
||||
required List<_iv1wx9mv.DiscussionPostView> comments,
|
||||
_isc.UuidValue? acceptedAnswerId,
|
||||
required bool asker,
|
||||
required bool author,
|
||||
_i5ff9exj.DiscussionRefusal? refusal,
|
||||
}) = _DiscussionViewImpl;
|
||||
|
||||
factory DiscussionView.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return DiscussionView(
|
||||
question: _itx5j3f5.Protocol().deserialize<_iv1wx9mv.DiscussionPostView>(
|
||||
jsonSerialization['question'],
|
||||
),
|
||||
publicId: _isc.UuidValueJsonExtension.fromJson(
|
||||
jsonSerialization['publicId'],
|
||||
),
|
||||
studysetTitle: jsonSerialization['studysetTitle'] as String,
|
||||
location: jsonSerialization['location'] as String,
|
||||
target: _itx5j3f5.Protocol().deserialize<_iqswgn91.DiscussionTarget>(
|
||||
jsonSerialization['target'],
|
||||
),
|
||||
answers: _itx5j3f5.Protocol()
|
||||
.deserialize<List<_iv1wx9mv.DiscussionPostView>>(
|
||||
jsonSerialization['answers'],
|
||||
),
|
||||
comments: _itx5j3f5.Protocol()
|
||||
.deserialize<List<_iv1wx9mv.DiscussionPostView>>(
|
||||
jsonSerialization['comments'],
|
||||
),
|
||||
acceptedAnswerId: jsonSerialization['acceptedAnswerId'] == null
|
||||
? null
|
||||
: _isc.UuidValueJsonExtension.fromJson(
|
||||
jsonSerialization['acceptedAnswerId'],
|
||||
),
|
||||
asker: _isc.BoolJsonExtension.fromJson(jsonSerialization['asker']),
|
||||
author: _isc.BoolJsonExtension.fromJson(jsonSerialization['author']),
|
||||
refusal: jsonSerialization['refusal'] == null
|
||||
? null
|
||||
: _i5ff9exj.DiscussionRefusal.fromJson(
|
||||
(jsonSerialization['refusal'] as String),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_iv1wx9mv.DiscussionPostView question;
|
||||
|
||||
/// The published studyset it is on: its public id, title and location.
|
||||
_isc.UuidValue publicId;
|
||||
|
||||
String studysetTitle;
|
||||
|
||||
String location;
|
||||
|
||||
/// What it is about.
|
||||
_iqswgn91.DiscussionTarget target;
|
||||
|
||||
List<_iv1wx9mv.DiscussionPostView> answers;
|
||||
|
||||
List<_iv1wx9mv.DiscussionPostView> comments;
|
||||
|
||||
_isc.UuidValue? acceptedAnswerId;
|
||||
|
||||
/// Whether the viewer asked it, and so may accept an answer.
|
||||
bool asker;
|
||||
|
||||
/// Whether the viewer is the studyset's author, and so may hide posts.
|
||||
bool author;
|
||||
|
||||
/// Why the viewer may not write in it; null when they may.
|
||||
_i5ff9exj.DiscussionRefusal? refusal;
|
||||
|
||||
/// Returns a shallow copy of this [DiscussionView]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_isc.useResult
|
||||
DiscussionView copyWith({
|
||||
_iv1wx9mv.DiscussionPostView? question,
|
||||
_isc.UuidValue? publicId,
|
||||
String? studysetTitle,
|
||||
String? location,
|
||||
_iqswgn91.DiscussionTarget? target,
|
||||
List<_iv1wx9mv.DiscussionPostView>? answers,
|
||||
List<_iv1wx9mv.DiscussionPostView>? comments,
|
||||
_isc.UuidValue? acceptedAnswerId,
|
||||
bool? asker,
|
||||
bool? author,
|
||||
_i5ff9exj.DiscussionRefusal? refusal,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'DiscussionView',
|
||||
'question': question.toJson(),
|
||||
'publicId': publicId.toJson(),
|
||||
'studysetTitle': studysetTitle,
|
||||
'location': location,
|
||||
'target': target.toJson(),
|
||||
'answers': answers.toJson(valueToJson: (v) => v.toJson()),
|
||||
'comments': comments.toJson(valueToJson: (v) => v.toJson()),
|
||||
if (acceptedAnswerId != null)
|
||||
'acceptedAnswerId': acceptedAnswerId?.toJson(),
|
||||
'asker': asker,
|
||||
'author': author,
|
||||
if (refusal != null) 'refusal': refusal?.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJsonForProtocol() {
|
||||
return {
|
||||
'__className__': 'DiscussionView',
|
||||
'question': question.toJsonForProtocol(),
|
||||
'publicId': publicId.toJson(),
|
||||
'studysetTitle': studysetTitle,
|
||||
'location': location,
|
||||
'target': target.toJsonForProtocol(),
|
||||
'answers': answers.toJson(valueToJson: (v) => v.toJsonForProtocol()),
|
||||
'comments': comments.toJson(valueToJson: (v) => v.toJsonForProtocol()),
|
||||
if (acceptedAnswerId != null)
|
||||
'acceptedAnswerId': acceptedAnswerId?.toJson(),
|
||||
'asker': asker,
|
||||
'author': author,
|
||||
if (refusal != null) 'refusal': refusal?.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _isc.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _Undefined {}
|
||||
|
||||
class _DiscussionViewImpl extends DiscussionView {
|
||||
_DiscussionViewImpl({
|
||||
required _iv1wx9mv.DiscussionPostView question,
|
||||
required _isc.UuidValue publicId,
|
||||
required String studysetTitle,
|
||||
required String location,
|
||||
required _iqswgn91.DiscussionTarget target,
|
||||
required List<_iv1wx9mv.DiscussionPostView> answers,
|
||||
required List<_iv1wx9mv.DiscussionPostView> comments,
|
||||
_isc.UuidValue? acceptedAnswerId,
|
||||
required bool asker,
|
||||
required bool author,
|
||||
_i5ff9exj.DiscussionRefusal? refusal,
|
||||
}) : super._(
|
||||
question: question,
|
||||
publicId: publicId,
|
||||
studysetTitle: studysetTitle,
|
||||
location: location,
|
||||
target: target,
|
||||
answers: answers,
|
||||
comments: comments,
|
||||
acceptedAnswerId: acceptedAnswerId,
|
||||
asker: asker,
|
||||
author: author,
|
||||
refusal: refusal,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [DiscussionView]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_isc.useResult
|
||||
@override
|
||||
DiscussionView copyWith({
|
||||
_iv1wx9mv.DiscussionPostView? question,
|
||||
_isc.UuidValue? publicId,
|
||||
String? studysetTitle,
|
||||
String? location,
|
||||
_iqswgn91.DiscussionTarget? target,
|
||||
List<_iv1wx9mv.DiscussionPostView>? answers,
|
||||
List<_iv1wx9mv.DiscussionPostView>? comments,
|
||||
Object? acceptedAnswerId = _Undefined,
|
||||
bool? asker,
|
||||
bool? author,
|
||||
Object? refusal = _Undefined,
|
||||
}) {
|
||||
return DiscussionView(
|
||||
question: question ?? this.question.copyWith(),
|
||||
publicId: publicId ?? this.publicId,
|
||||
studysetTitle: studysetTitle ?? this.studysetTitle,
|
||||
location: location ?? this.location,
|
||||
target: target ?? this.target.copyWith(),
|
||||
answers: answers ?? this.answers.map((e0) => e0.copyWith()).toList(),
|
||||
comments: comments ?? this.comments.map((e0) => e0.copyWith()).toList(),
|
||||
acceptedAnswerId: acceptedAnswerId is _isc.UuidValue?
|
||||
? acceptedAnswerId
|
||||
: this.acceptedAnswerId,
|
||||
asker: asker ?? this.asker,
|
||||
author: author ?? this.author,
|
||||
refusal: refusal is _i5ff9exj.DiscussionRefusal? ? refusal : this.refusal,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -23,6 +23,8 @@ import 'package:halkyon_learn_client/src/protocol/dictionary/dictionary_key_hit.
|
|||
as _ii0vb6vg;
|
||||
import 'package:halkyon_learn_client/src/protocol/dictionary/dictionary_source.dart'
|
||||
as _i8vj64sp;
|
||||
import 'package:halkyon_learn_client/src/protocol/discussion/discussion_summary.dart'
|
||||
as _i7lxdcg2;
|
||||
import 'package:halkyon_learn_client/src/protocol/explore/explore_entry.dart'
|
||||
as _iyigb6vc;
|
||||
import 'package:halkyon_learn_client/src/protocol/feedback/feedback_view.dart'
|
||||
|
|
@ -58,6 +60,12 @@ import 'dictionary/dictionary_hit.dart' as _ivbh47kc;
|
|||
import 'dictionary/dictionary_key.dart' as _i9gd3xbn;
|
||||
import 'dictionary/dictionary_key_hit.dart' as _i6b9unou;
|
||||
import 'dictionary/dictionary_source.dart' as _ie12eb65;
|
||||
import 'discussion/discussion_post_view.dart' as _ifltqotq;
|
||||
import 'discussion/discussion_refusal.dart' as _iysrt1r7;
|
||||
import 'discussion/discussion_refused_exception.dart' as _i1nsp6ib;
|
||||
import 'discussion/discussion_summary.dart' as _i5mmlb7v;
|
||||
import 'discussion/discussion_target.dart' as _ijf8cxgd;
|
||||
import 'discussion/discussion_view.dart' as _ixby8mme;
|
||||
import 'explore/explore_entry.dart' as _iaxdli27;
|
||||
import 'explore/explore_query.dart' as _ibl9h9o9;
|
||||
import 'explore/release_content.dart' as _ievpeb4f;
|
||||
|
|
@ -114,6 +122,12 @@ export 'dictionary/dictionary_hit.dart';
|
|||
export 'dictionary/dictionary_key.dart';
|
||||
export 'dictionary/dictionary_key_hit.dart';
|
||||
export 'dictionary/dictionary_source.dart';
|
||||
export 'discussion/discussion_post_view.dart';
|
||||
export 'discussion/discussion_refusal.dart';
|
||||
export 'discussion/discussion_refused_exception.dart';
|
||||
export 'discussion/discussion_summary.dart';
|
||||
export 'discussion/discussion_target.dart';
|
||||
export 'discussion/discussion_view.dart';
|
||||
export 'explore/explore_entry.dart';
|
||||
export 'explore/explore_query.dart';
|
||||
export 'explore/release_content.dart';
|
||||
|
|
@ -2364,6 +2378,24 @@ class Protocol extends _isd.DatabaseSerializationManager {
|
|||
if (t == _ie12eb65.DictionarySource) {
|
||||
return _ie12eb65.DictionarySource.fromJson(data) as T;
|
||||
}
|
||||
if (t == _ifltqotq.DiscussionPostView) {
|
||||
return _ifltqotq.DiscussionPostView.fromJson(data) as T;
|
||||
}
|
||||
if (t == _iysrt1r7.DiscussionRefusal) {
|
||||
return _iysrt1r7.DiscussionRefusal.fromJson(data) as T;
|
||||
}
|
||||
if (t == _i1nsp6ib.DiscussionRefusedException) {
|
||||
return _i1nsp6ib.DiscussionRefusedException.fromJson(data) as T;
|
||||
}
|
||||
if (t == _i5mmlb7v.DiscussionSummary) {
|
||||
return _i5mmlb7v.DiscussionSummary.fromJson(data) as T;
|
||||
}
|
||||
if (t == _ijf8cxgd.DiscussionTarget) {
|
||||
return _ijf8cxgd.DiscussionTarget.fromJson(data) as T;
|
||||
}
|
||||
if (t == _ixby8mme.DiscussionView) {
|
||||
return _ixby8mme.DiscussionView.fromJson(data) as T;
|
||||
}
|
||||
if (t == _iaxdli27.ExploreEntry) {
|
||||
return _iaxdli27.ExploreEntry.fromJson(data) as T;
|
||||
}
|
||||
|
|
@ -2561,6 +2593,32 @@ class Protocol extends _isd.DatabaseSerializationManager {
|
|||
return (data != null ? _ie12eb65.DictionarySource.fromJson(data) : null)
|
||||
as T;
|
||||
}
|
||||
if (t == _isc.getType<_ifltqotq.DiscussionPostView?>()) {
|
||||
return (data != null ? _ifltqotq.DiscussionPostView.fromJson(data) : null)
|
||||
as T;
|
||||
}
|
||||
if (t == _isc.getType<_iysrt1r7.DiscussionRefusal?>()) {
|
||||
return (data != null ? _iysrt1r7.DiscussionRefusal.fromJson(data) : null)
|
||||
as T;
|
||||
}
|
||||
if (t == _isc.getType<_i1nsp6ib.DiscussionRefusedException?>()) {
|
||||
return (data != null
|
||||
? _i1nsp6ib.DiscussionRefusedException.fromJson(data)
|
||||
: null)
|
||||
as T;
|
||||
}
|
||||
if (t == _isc.getType<_i5mmlb7v.DiscussionSummary?>()) {
|
||||
return (data != null ? _i5mmlb7v.DiscussionSummary.fromJson(data) : null)
|
||||
as T;
|
||||
}
|
||||
if (t == _isc.getType<_ijf8cxgd.DiscussionTarget?>()) {
|
||||
return (data != null ? _ijf8cxgd.DiscussionTarget.fromJson(data) : null)
|
||||
as T;
|
||||
}
|
||||
if (t == _isc.getType<_ixby8mme.DiscussionView?>()) {
|
||||
return (data != null ? _ixby8mme.DiscussionView.fromJson(data) : null)
|
||||
as T;
|
||||
}
|
||||
if (t == _isc.getType<_iaxdli27.ExploreEntry?>()) {
|
||||
return (data != null ? _iaxdli27.ExploreEntry.fromJson(data) : null) as T;
|
||||
}
|
||||
|
|
@ -2696,6 +2754,12 @@ class Protocol extends _isd.DatabaseSerializationManager {
|
|||
.toList()
|
||||
as T;
|
||||
}
|
||||
if (t == List<_ifltqotq.DiscussionPostView>) {
|
||||
return (data as List)
|
||||
.map((e) => deserialize<_ifltqotq.DiscussionPostView>(e))
|
||||
.toList()
|
||||
as T;
|
||||
}
|
||||
if (t == List<_iaswq7ap.ReleaseItem>) {
|
||||
return (data as List)
|
||||
.map((e) => deserialize<_iaswq7ap.ReleaseItem>(e))
|
||||
|
|
@ -2765,6 +2829,12 @@ class Protocol extends _isd.DatabaseSerializationManager {
|
|||
)
|
||||
as T;
|
||||
}
|
||||
if (t == List<_i7lxdcg2.DiscussionSummary>) {
|
||||
return (data as List)
|
||||
.map((e) => deserialize<_i7lxdcg2.DiscussionSummary>(e))
|
||||
.toList()
|
||||
as T;
|
||||
}
|
||||
if (t == List<_iyigb6vc.ExploreEntry>) {
|
||||
return (data as List)
|
||||
.map((e) => deserialize<_iyigb6vc.ExploreEntry>(e))
|
||||
|
|
@ -2818,6 +2888,12 @@ class Protocol extends _isd.DatabaseSerializationManager {
|
|||
_i9gd3xbn.DictionaryKey => 'DictionaryKey',
|
||||
_i6b9unou.DictionaryKeyHit => 'DictionaryKeyHit',
|
||||
_ie12eb65.DictionarySource => 'DictionarySource',
|
||||
_ifltqotq.DiscussionPostView => 'DiscussionPostView',
|
||||
_iysrt1r7.DiscussionRefusal => 'DiscussionRefusal',
|
||||
_i1nsp6ib.DiscussionRefusedException => 'DiscussionRefusedException',
|
||||
_i5mmlb7v.DiscussionSummary => 'DiscussionSummary',
|
||||
_ijf8cxgd.DiscussionTarget => 'DiscussionTarget',
|
||||
_ixby8mme.DiscussionView => 'DiscussionView',
|
||||
_iaxdli27.ExploreEntry => 'ExploreEntry',
|
||||
_ibl9h9o9.ExploreQuery => 'ExploreQuery',
|
||||
_ievpeb4f.ReleaseContent => 'ReleaseContent',
|
||||
|
|
@ -2914,6 +2990,18 @@ class Protocol extends _isd.DatabaseSerializationManager {
|
|||
return 'DictionaryKeyHit';
|
||||
case _ie12eb65.DictionarySource():
|
||||
return 'DictionarySource';
|
||||
case _ifltqotq.DiscussionPostView():
|
||||
return 'DiscussionPostView';
|
||||
case _iysrt1r7.DiscussionRefusal():
|
||||
return 'DiscussionRefusal';
|
||||
case _i1nsp6ib.DiscussionRefusedException():
|
||||
return 'DiscussionRefusedException';
|
||||
case _i5mmlb7v.DiscussionSummary():
|
||||
return 'DiscussionSummary';
|
||||
case _ijf8cxgd.DiscussionTarget():
|
||||
return 'DiscussionTarget';
|
||||
case _ixby8mme.DiscussionView():
|
||||
return 'DiscussionView';
|
||||
case _iaxdli27.ExploreEntry():
|
||||
return 'ExploreEntry';
|
||||
case _ibl9h9o9.ExploreQuery():
|
||||
|
|
@ -3077,6 +3165,24 @@ class Protocol extends _isd.DatabaseSerializationManager {
|
|||
if (dataClassName == 'DictionarySource') {
|
||||
return deserialize<_ie12eb65.DictionarySource>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'DiscussionPostView') {
|
||||
return deserialize<_ifltqotq.DiscussionPostView>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'DiscussionRefusal') {
|
||||
return deserialize<_iysrt1r7.DiscussionRefusal>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'DiscussionRefusedException') {
|
||||
return deserialize<_i1nsp6ib.DiscussionRefusedException>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'DiscussionSummary') {
|
||||
return deserialize<_i5mmlb7v.DiscussionSummary>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'DiscussionTarget') {
|
||||
return deserialize<_ijf8cxgd.DiscussionTarget>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'DiscussionView') {
|
||||
return deserialize<_ixby8mme.DiscussionView>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'ExploreEntry') {
|
||||
return deserialize<_iaxdli27.ExploreEntry>(data['data']);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1749,11 +1749,78 @@
|
|||
"feedbackUnreachable": "Meldungen und Vorschläge sind gerade nicht erreichbar.",
|
||||
"feedbackRetry": "Erneut versuchen",
|
||||
"inboxTitle": "Posteingang",
|
||||
"inboxIntro": "Meldungen und Vorschläge zu deinen veröffentlichten Lernsets und die, die du geschrieben hast.",
|
||||
"inboxIntro": "Fragen und Antworten, Meldungen und Vorschläge zu deinen veröffentlichten Lernsets und die, in denen du geschrieben hast.",
|
||||
"inboxEmpty": "Noch nichts.",
|
||||
"studysetFeedback": "Meldungen und Vorschläge",
|
||||
"studysetFeedbackEmpty": "Noch hat niemand etwas gemeldet oder vorgeschlagen.",
|
||||
"reportToAuthor": "Fehler im Original melden",
|
||||
"discussionsTitle": "Diskussionen",
|
||||
"discussionsSearch": "Fragen durchsuchen",
|
||||
"discussionsAsk": "Frage stellen",
|
||||
"discussionsEmpty": "Noch keine Fragen",
|
||||
"discussionsEmptyHint": "Stell die erste: zum Lernset, zu einem Thema oder zu einer seiner Karteikarten.",
|
||||
"discussionsNoMatch": "Kein Fragentitel sagt das.",
|
||||
"discussionsMore": "Mehr zeigen",
|
||||
"discussionsUnreachable": "Die Diskussionen sind gerade nicht erreichbar.",
|
||||
"discussionsAnswers": "{count, plural, =0{Keine Antworten} =1{1 Antwort} other{{count} Antworten}}",
|
||||
"@discussionsAnswers": {"placeholders": {"count": {"type": "int"}}},
|
||||
"discussionsBy": "{name} · {date}",
|
||||
"@discussionsBy": {"placeholders": {"name": {"type": "String"}, "date": {"type": "String"}}},
|
||||
"discussionsSomeone": "Jemand",
|
||||
"discussionsDeletedQuestion": "Gelöschte Frage",
|
||||
"discussionsHidden": "Ausgeblendet",
|
||||
"discussionsAboutCard": "Zu einer Karteikarte",
|
||||
"discussionsAboutNote": "Zu einer Notiz",
|
||||
"discussionsAboutStep": "Zu einem Schritt",
|
||||
"discussionsAccepted": "Angenommen",
|
||||
"discussionsOn": "Zu {title}",
|
||||
"@discussionsOn": {"placeholders": {"title": {"type": "String"}}},
|
||||
"discussionRefusalNoIdentity": "Um zu fragen und zu antworten, melde dich im Profil mit einer Identität an: Beiträge zeigen, wer sie geschrieben hat.",
|
||||
"discussionRefusalNoProfile": "Um zu fragen und zu antworten, leg im Profil ein öffentliches Profil an: Beiträge zeigen, wer sie geschrieben hat.",
|
||||
"discussionRefusalBlocked": "Wer dieses Lernset veröffentlicht hat, lässt dich zu seinen Lernsets nichts schreiben.",
|
||||
"discussionRefusalWithdrawn": "Dieses Lernset wurde aus Entdecken genommen: Was hier steht, bleibt, aber Neues wird nicht gefragt.",
|
||||
"discussionRefusalNotInRelease": "Das ist nicht mehr in der Veröffentlichung des Lernsets.",
|
||||
"discussionRefusalEmpty": "Schreib zuerst etwas.",
|
||||
"discussionRefusalTooLong": "Das ist länger als erlaubt.",
|
||||
"discussionRefusalTooMany": "Du hast in der letzten Stunde viel geschrieben. Versuch es etwas später noch einmal.",
|
||||
"discussionRefusalWithFiles": "Bilder und Töne lassen sich nicht posten: Deine Dateien gehören dir allein, also sähe sie niemand sonst.",
|
||||
"discussionFailed": "Die Instanz ist nicht erreichbar. Versuch es noch einmal, sobald du online bist.",
|
||||
"discussionTitle": "Frage",
|
||||
"discussionMissing": "Diese Frage gibt es nicht mehr.",
|
||||
"discussionDeleted": "Das wurde gelöscht.",
|
||||
"discussionEdited": "bearbeitet",
|
||||
"discussionWriteAnswer": "Antwort schreiben",
|
||||
"discussionYourAnswer": "Deine Antwort",
|
||||
"discussionEditHeading": "Bearbeiten",
|
||||
"discussionTitleField": "Titel",
|
||||
"discussionTitleHint": "Was möchtest du wissen?",
|
||||
"discussionPost": "Posten",
|
||||
"discussionComment": "Kommentar hinzufügen",
|
||||
"discussionCommentHint": "Eine kurze Bemerkung",
|
||||
"discussionAccept": "Annehmen",
|
||||
"discussionUnaccept": "Zurücknehmen",
|
||||
"discussionMenu": "Mehr",
|
||||
"discussionMenuEdit": "Bearbeiten",
|
||||
"discussionMenuDelete": "Löschen",
|
||||
"discussionMenuHide": "Ausblenden",
|
||||
"discussionMenuShow": "Wieder zeigen",
|
||||
"discussionMenuBlock": "{name} blockieren",
|
||||
"@discussionMenuBlock": {"placeholders": {"name": {"type": "String"}}},
|
||||
"discussionDeleteTitle": "Das löschen?",
|
||||
"discussionDeleteBody": "Was andere darunter geantwortet haben, bleibt, ohne deinen Text und Namen.",
|
||||
"discussionAnswered": "Antwort gepostet.",
|
||||
"discussionCommented": "Kommentar gepostet.",
|
||||
"discussionSaved": "Gespeichert.",
|
||||
"discussionDeletedDone": "Gelöscht.",
|
||||
"discussionAcceptedDone": "Antwort angenommen.",
|
||||
"discussionUnacceptedDone": "Jetzt ist keine Antwort angenommen.",
|
||||
"discussionHiddenDone": "Ausgeblendet: Nur wer es geschrieben hat und du sehen es.",
|
||||
"discussionShownDone": "Wieder sichtbar.",
|
||||
"discussionBlockedDone": "Blockiert: Was diese Person zu deinen Lernsets schreibt, sieht nur sie selbst.",
|
||||
"discussionAskAbout": "Dazu fragen",
|
||||
"inboxDiscussions": "Diskussionen",
|
||||
"inboxFeedback": "Meldungen und Vorschläge",
|
||||
"inboxDiscussionsEmpty": "Noch keine Diskussionen.",
|
||||
"cardReportToAuthor": "Auch im Original melden",
|
||||
"cardReportToAuthorHint": "Öffentlich: alle, die es lernen, lesen es, mit deinem öffentlichen Profil.",
|
||||
"feedbackReportSent": "Im Original gemeldet.",
|
||||
|
|
|
|||
|
|
@ -2051,11 +2051,78 @@
|
|||
"feedbackUnreachable": "Reports and suggestions can't be reached right now.",
|
||||
"feedbackRetry": "Retry",
|
||||
"inboxTitle": "Inbox",
|
||||
"inboxIntro": "Reports and suggestions on your published studysets, and those you wrote.",
|
||||
"inboxIntro": "Questions and answers, reports and suggestions on your published studysets, and those you wrote in.",
|
||||
"inboxEmpty": "Nothing yet.",
|
||||
"studysetFeedback": "Reports and suggestions",
|
||||
"studysetFeedbackEmpty": "Nobody has reported or suggested anything yet.",
|
||||
"reportToAuthor": "Report to the author",
|
||||
"discussionsTitle": "Discussions",
|
||||
"discussionsSearch": "Search questions",
|
||||
"discussionsAsk": "Ask a question",
|
||||
"discussionsEmpty": "No questions yet",
|
||||
"discussionsEmptyHint": "Ask the first: about the studyset, a topic or one of its flashcards.",
|
||||
"discussionsNoMatch": "No question's title says that.",
|
||||
"discussionsMore": "Show more",
|
||||
"discussionsUnreachable": "The discussions can't be reached right now.",
|
||||
"discussionsAnswers": "{count, plural, =0{No answers} =1{1 answer} other{{count} answers}}",
|
||||
"@discussionsAnswers": {"placeholders": {"count": {"type": "int"}}},
|
||||
"discussionsBy": "{name} · {date}",
|
||||
"@discussionsBy": {"placeholders": {"name": {"type": "String"}, "date": {"type": "String"}}},
|
||||
"discussionsSomeone": "Someone",
|
||||
"discussionsDeletedQuestion": "Deleted question",
|
||||
"discussionsHidden": "Hidden",
|
||||
"discussionsAboutCard": "About a flashcard",
|
||||
"discussionsAboutNote": "About a note",
|
||||
"discussionsAboutStep": "About a step",
|
||||
"discussionsAccepted": "Accepted",
|
||||
"discussionsOn": "On {title}",
|
||||
"@discussionsOn": {"placeholders": {"title": {"type": "String"}}},
|
||||
"discussionRefusalNoIdentity": "To ask and answer, sign in with an identity in Profile: posts show who wrote them.",
|
||||
"discussionRefusalNoProfile": "To ask and answer, make a public profile in Profile: posts show who wrote them.",
|
||||
"discussionRefusalBlocked": "The author of this studyset keeps you from writing on their studysets.",
|
||||
"discussionRefusalWithdrawn": "The author took this studyset off Explore: what is here stays, but nothing new is asked.",
|
||||
"discussionRefusalNotInRelease": "That isn't in the studyset's release any more.",
|
||||
"discussionRefusalEmpty": "Write something first.",
|
||||
"discussionRefusalTooLong": "That's longer than allowed.",
|
||||
"discussionRefusalTooMany": "You've written a lot in the last hour. Try again a little later.",
|
||||
"discussionRefusalWithFiles": "Images and sounds can't be posted: your files are yours alone, so nobody else would see them.",
|
||||
"discussionFailed": "Couldn't reach the instance. Try again once you are online.",
|
||||
"discussionTitle": "Question",
|
||||
"discussionMissing": "This question isn't there any more.",
|
||||
"discussionDeleted": "This was deleted.",
|
||||
"discussionEdited": "edited",
|
||||
"discussionWriteAnswer": "Write an answer",
|
||||
"discussionYourAnswer": "Your answer",
|
||||
"discussionEditHeading": "Edit",
|
||||
"discussionTitleField": "Title",
|
||||
"discussionTitleHint": "What do you want to know?",
|
||||
"discussionPost": "Post",
|
||||
"discussionComment": "Add a comment",
|
||||
"discussionCommentHint": "A short remark",
|
||||
"discussionAccept": "Accept",
|
||||
"discussionUnaccept": "Take back",
|
||||
"discussionMenu": "More",
|
||||
"discussionMenuEdit": "Edit",
|
||||
"discussionMenuDelete": "Delete",
|
||||
"discussionMenuHide": "Hide",
|
||||
"discussionMenuShow": "Show again",
|
||||
"discussionMenuBlock": "Block {name}",
|
||||
"@discussionMenuBlock": {"placeholders": {"name": {"type": "String"}}},
|
||||
"discussionDeleteTitle": "Delete this?",
|
||||
"discussionDeleteBody": "What others replied under it stays, without your text and name.",
|
||||
"discussionAnswered": "Answer posted.",
|
||||
"discussionCommented": "Comment posted.",
|
||||
"discussionSaved": "Saved.",
|
||||
"discussionDeletedDone": "Deleted.",
|
||||
"discussionAcceptedDone": "Answer accepted.",
|
||||
"discussionUnacceptedDone": "No answer accepted now.",
|
||||
"discussionHiddenDone": "Hidden: only its writer and you see it.",
|
||||
"discussionShownDone": "Shown again.",
|
||||
"discussionBlockedDone": "Blocked: what they write on your studysets shows to them alone.",
|
||||
"discussionAskAbout": "Ask about this",
|
||||
"inboxDiscussions": "Discussions",
|
||||
"inboxFeedback": "Reports and suggestions",
|
||||
"inboxDiscussionsEmpty": "No discussions yet.",
|
||||
"cardReportToAuthor": "Send to the author too",
|
||||
"cardReportToAuthorHint": "Public: everyone learning it reads it, with your public profile.",
|
||||
"feedbackReportSent": "Sent to the author.",
|
||||
|
|
|
|||
|
|
@ -10362,7 +10362,7 @@ abstract class AppLocalizations {
|
|||
/// No description provided for @inboxIntro.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Reports and suggestions on your published studysets, and those you wrote.'**
|
||||
/// **'Questions and answers, reports and suggestions on your published studysets, and those you wrote in.'**
|
||||
String get inboxIntro;
|
||||
|
||||
/// No description provided for @inboxEmpty.
|
||||
|
|
@ -10389,6 +10389,384 @@ abstract class AppLocalizations {
|
|||
/// **'Report to the author'**
|
||||
String get reportToAuthor;
|
||||
|
||||
/// No description provided for @discussionsTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Discussions'**
|
||||
String get discussionsTitle;
|
||||
|
||||
/// No description provided for @discussionsSearch.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Search questions'**
|
||||
String get discussionsSearch;
|
||||
|
||||
/// No description provided for @discussionsAsk.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Ask a question'**
|
||||
String get discussionsAsk;
|
||||
|
||||
/// No description provided for @discussionsEmpty.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No questions yet'**
|
||||
String get discussionsEmpty;
|
||||
|
||||
/// No description provided for @discussionsEmptyHint.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Ask the first: about the studyset, a topic or one of its flashcards.'**
|
||||
String get discussionsEmptyHint;
|
||||
|
||||
/// No description provided for @discussionsNoMatch.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No question\'s title says that.'**
|
||||
String get discussionsNoMatch;
|
||||
|
||||
/// No description provided for @discussionsMore.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Show more'**
|
||||
String get discussionsMore;
|
||||
|
||||
/// No description provided for @discussionsUnreachable.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'The discussions can\'t be reached right now.'**
|
||||
String get discussionsUnreachable;
|
||||
|
||||
/// No description provided for @discussionsAnswers.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{count, plural, =0{No answers} =1{1 answer} other{{count} answers}}'**
|
||||
String discussionsAnswers(int count);
|
||||
|
||||
/// No description provided for @discussionsBy.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{name} · {date}'**
|
||||
String discussionsBy(String name, String date);
|
||||
|
||||
/// No description provided for @discussionsSomeone.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Someone'**
|
||||
String get discussionsSomeone;
|
||||
|
||||
/// No description provided for @discussionsDeletedQuestion.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Deleted question'**
|
||||
String get discussionsDeletedQuestion;
|
||||
|
||||
/// No description provided for @discussionsHidden.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Hidden'**
|
||||
String get discussionsHidden;
|
||||
|
||||
/// No description provided for @discussionsAboutCard.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'About a flashcard'**
|
||||
String get discussionsAboutCard;
|
||||
|
||||
/// No description provided for @discussionsAboutNote.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'About a note'**
|
||||
String get discussionsAboutNote;
|
||||
|
||||
/// No description provided for @discussionsAboutStep.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'About a step'**
|
||||
String get discussionsAboutStep;
|
||||
|
||||
/// No description provided for @discussionsAccepted.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Accepted'**
|
||||
String get discussionsAccepted;
|
||||
|
||||
/// No description provided for @discussionsOn.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'On {title}'**
|
||||
String discussionsOn(String title);
|
||||
|
||||
/// No description provided for @discussionRefusalNoIdentity.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'To ask and answer, sign in with an identity in Profile: posts show who wrote them.'**
|
||||
String get discussionRefusalNoIdentity;
|
||||
|
||||
/// No description provided for @discussionRefusalNoProfile.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'To ask and answer, make a public profile in Profile: posts show who wrote them.'**
|
||||
String get discussionRefusalNoProfile;
|
||||
|
||||
/// No description provided for @discussionRefusalBlocked.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'The author of this studyset keeps you from writing on their studysets.'**
|
||||
String get discussionRefusalBlocked;
|
||||
|
||||
/// No description provided for @discussionRefusalWithdrawn.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'The author took this studyset off Explore: what is here stays, but nothing new is asked.'**
|
||||
String get discussionRefusalWithdrawn;
|
||||
|
||||
/// No description provided for @discussionRefusalNotInRelease.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'That isn\'t in the studyset\'s release any more.'**
|
||||
String get discussionRefusalNotInRelease;
|
||||
|
||||
/// No description provided for @discussionRefusalEmpty.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Write something first.'**
|
||||
String get discussionRefusalEmpty;
|
||||
|
||||
/// No description provided for @discussionRefusalTooLong.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'That\'s longer than allowed.'**
|
||||
String get discussionRefusalTooLong;
|
||||
|
||||
/// No description provided for @discussionRefusalTooMany.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'You\'ve written a lot in the last hour. Try again a little later.'**
|
||||
String get discussionRefusalTooMany;
|
||||
|
||||
/// No description provided for @discussionRefusalWithFiles.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Images and sounds can\'t be posted: your files are yours alone, so nobody else would see them.'**
|
||||
String get discussionRefusalWithFiles;
|
||||
|
||||
/// No description provided for @discussionFailed.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Couldn\'t reach the instance. Try again once you are online.'**
|
||||
String get discussionFailed;
|
||||
|
||||
/// No description provided for @discussionTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Question'**
|
||||
String get discussionTitle;
|
||||
|
||||
/// No description provided for @discussionMissing.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'This question isn\'t there any more.'**
|
||||
String get discussionMissing;
|
||||
|
||||
/// No description provided for @discussionDeleted.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'This was deleted.'**
|
||||
String get discussionDeleted;
|
||||
|
||||
/// No description provided for @discussionEdited.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'edited'**
|
||||
String get discussionEdited;
|
||||
|
||||
/// No description provided for @discussionWriteAnswer.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Write an answer'**
|
||||
String get discussionWriteAnswer;
|
||||
|
||||
/// No description provided for @discussionYourAnswer.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Your answer'**
|
||||
String get discussionYourAnswer;
|
||||
|
||||
/// No description provided for @discussionEditHeading.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Edit'**
|
||||
String get discussionEditHeading;
|
||||
|
||||
/// No description provided for @discussionTitleField.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Title'**
|
||||
String get discussionTitleField;
|
||||
|
||||
/// No description provided for @discussionTitleHint.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'What do you want to know?'**
|
||||
String get discussionTitleHint;
|
||||
|
||||
/// No description provided for @discussionPost.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Post'**
|
||||
String get discussionPost;
|
||||
|
||||
/// No description provided for @discussionComment.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Add a comment'**
|
||||
String get discussionComment;
|
||||
|
||||
/// No description provided for @discussionCommentHint.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'A short remark'**
|
||||
String get discussionCommentHint;
|
||||
|
||||
/// No description provided for @discussionAccept.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Accept'**
|
||||
String get discussionAccept;
|
||||
|
||||
/// No description provided for @discussionUnaccept.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Take back'**
|
||||
String get discussionUnaccept;
|
||||
|
||||
/// No description provided for @discussionMenu.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'More'**
|
||||
String get discussionMenu;
|
||||
|
||||
/// No description provided for @discussionMenuEdit.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Edit'**
|
||||
String get discussionMenuEdit;
|
||||
|
||||
/// No description provided for @discussionMenuDelete.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Delete'**
|
||||
String get discussionMenuDelete;
|
||||
|
||||
/// No description provided for @discussionMenuHide.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Hide'**
|
||||
String get discussionMenuHide;
|
||||
|
||||
/// No description provided for @discussionMenuShow.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Show again'**
|
||||
String get discussionMenuShow;
|
||||
|
||||
/// No description provided for @discussionMenuBlock.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Block {name}'**
|
||||
String discussionMenuBlock(String name);
|
||||
|
||||
/// No description provided for @discussionDeleteTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Delete this?'**
|
||||
String get discussionDeleteTitle;
|
||||
|
||||
/// No description provided for @discussionDeleteBody.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'What others replied under it stays, without your text and name.'**
|
||||
String get discussionDeleteBody;
|
||||
|
||||
/// No description provided for @discussionAnswered.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Answer posted.'**
|
||||
String get discussionAnswered;
|
||||
|
||||
/// No description provided for @discussionCommented.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Comment posted.'**
|
||||
String get discussionCommented;
|
||||
|
||||
/// No description provided for @discussionSaved.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Saved.'**
|
||||
String get discussionSaved;
|
||||
|
||||
/// No description provided for @discussionDeletedDone.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Deleted.'**
|
||||
String get discussionDeletedDone;
|
||||
|
||||
/// No description provided for @discussionAcceptedDone.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Answer accepted.'**
|
||||
String get discussionAcceptedDone;
|
||||
|
||||
/// No description provided for @discussionUnacceptedDone.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No answer accepted now.'**
|
||||
String get discussionUnacceptedDone;
|
||||
|
||||
/// No description provided for @discussionHiddenDone.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Hidden: only its writer and you see it.'**
|
||||
String get discussionHiddenDone;
|
||||
|
||||
/// No description provided for @discussionShownDone.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Shown again.'**
|
||||
String get discussionShownDone;
|
||||
|
||||
/// No description provided for @discussionBlockedDone.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Blocked: what they write on your studysets shows to them alone.'**
|
||||
String get discussionBlockedDone;
|
||||
|
||||
/// No description provided for @discussionAskAbout.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Ask about this'**
|
||||
String get discussionAskAbout;
|
||||
|
||||
/// No description provided for @inboxDiscussions.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Discussions'**
|
||||
String get inboxDiscussions;
|
||||
|
||||
/// No description provided for @inboxFeedback.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Reports and suggestions'**
|
||||
String get inboxFeedback;
|
||||
|
||||
/// No description provided for @inboxDiscussionsEmpty.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No discussions yet.'**
|
||||
String get inboxDiscussionsEmpty;
|
||||
|
||||
/// No description provided for @cardReportToAuthor.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
|
|
|||
|
|
@ -6518,7 +6518,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get inboxIntro =>
|
||||
'Meldungen und Vorschläge zu deinen veröffentlichten Lernsets und die, die du geschrieben hast.';
|
||||
'Fragen und Antworten, Meldungen und Vorschläge zu deinen veröffentlichten Lernsets und die, in denen du geschrieben hast.';
|
||||
|
||||
@override
|
||||
String get inboxEmpty => 'Noch nichts.';
|
||||
|
|
@ -6533,6 +6533,223 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
@override
|
||||
String get reportToAuthor => 'Fehler im Original melden';
|
||||
|
||||
@override
|
||||
String get discussionsTitle => 'Diskussionen';
|
||||
|
||||
@override
|
||||
String get discussionsSearch => 'Fragen durchsuchen';
|
||||
|
||||
@override
|
||||
String get discussionsAsk => 'Frage stellen';
|
||||
|
||||
@override
|
||||
String get discussionsEmpty => 'Noch keine Fragen';
|
||||
|
||||
@override
|
||||
String get discussionsEmptyHint =>
|
||||
'Stell die erste: zum Lernset, zu einem Thema oder zu einer seiner Karteikarten.';
|
||||
|
||||
@override
|
||||
String get discussionsNoMatch => 'Kein Fragentitel sagt das.';
|
||||
|
||||
@override
|
||||
String get discussionsMore => 'Mehr zeigen';
|
||||
|
||||
@override
|
||||
String get discussionsUnreachable =>
|
||||
'Die Diskussionen sind gerade nicht erreichbar.';
|
||||
|
||||
@override
|
||||
String discussionsAnswers(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count Antworten',
|
||||
one: '1 Antwort',
|
||||
zero: 'Keine Antworten',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String discussionsBy(String name, String date) {
|
||||
return '$name · $date';
|
||||
}
|
||||
|
||||
@override
|
||||
String get discussionsSomeone => 'Jemand';
|
||||
|
||||
@override
|
||||
String get discussionsDeletedQuestion => 'Gelöschte Frage';
|
||||
|
||||
@override
|
||||
String get discussionsHidden => 'Ausgeblendet';
|
||||
|
||||
@override
|
||||
String get discussionsAboutCard => 'Zu einer Karteikarte';
|
||||
|
||||
@override
|
||||
String get discussionsAboutNote => 'Zu einer Notiz';
|
||||
|
||||
@override
|
||||
String get discussionsAboutStep => 'Zu einem Schritt';
|
||||
|
||||
@override
|
||||
String get discussionsAccepted => 'Angenommen';
|
||||
|
||||
@override
|
||||
String discussionsOn(String title) {
|
||||
return 'Zu $title';
|
||||
}
|
||||
|
||||
@override
|
||||
String get discussionRefusalNoIdentity =>
|
||||
'Um zu fragen und zu antworten, melde dich im Profil mit einer Identität an: Beiträge zeigen, wer sie geschrieben hat.';
|
||||
|
||||
@override
|
||||
String get discussionRefusalNoProfile =>
|
||||
'Um zu fragen und zu antworten, leg im Profil ein öffentliches Profil an: Beiträge zeigen, wer sie geschrieben hat.';
|
||||
|
||||
@override
|
||||
String get discussionRefusalBlocked =>
|
||||
'Wer dieses Lernset veröffentlicht hat, lässt dich zu seinen Lernsets nichts schreiben.';
|
||||
|
||||
@override
|
||||
String get discussionRefusalWithdrawn =>
|
||||
'Dieses Lernset wurde aus Entdecken genommen: Was hier steht, bleibt, aber Neues wird nicht gefragt.';
|
||||
|
||||
@override
|
||||
String get discussionRefusalNotInRelease =>
|
||||
'Das ist nicht mehr in der Veröffentlichung des Lernsets.';
|
||||
|
||||
@override
|
||||
String get discussionRefusalEmpty => 'Schreib zuerst etwas.';
|
||||
|
||||
@override
|
||||
String get discussionRefusalTooLong => 'Das ist länger als erlaubt.';
|
||||
|
||||
@override
|
||||
String get discussionRefusalTooMany =>
|
||||
'Du hast in der letzten Stunde viel geschrieben. Versuch es etwas später noch einmal.';
|
||||
|
||||
@override
|
||||
String get discussionRefusalWithFiles =>
|
||||
'Bilder und Töne lassen sich nicht posten: Deine Dateien gehören dir allein, also sähe sie niemand sonst.';
|
||||
|
||||
@override
|
||||
String get discussionFailed =>
|
||||
'Die Instanz ist nicht erreichbar. Versuch es noch einmal, sobald du online bist.';
|
||||
|
||||
@override
|
||||
String get discussionTitle => 'Frage';
|
||||
|
||||
@override
|
||||
String get discussionMissing => 'Diese Frage gibt es nicht mehr.';
|
||||
|
||||
@override
|
||||
String get discussionDeleted => 'Das wurde gelöscht.';
|
||||
|
||||
@override
|
||||
String get discussionEdited => 'bearbeitet';
|
||||
|
||||
@override
|
||||
String get discussionWriteAnswer => 'Antwort schreiben';
|
||||
|
||||
@override
|
||||
String get discussionYourAnswer => 'Deine Antwort';
|
||||
|
||||
@override
|
||||
String get discussionEditHeading => 'Bearbeiten';
|
||||
|
||||
@override
|
||||
String get discussionTitleField => 'Titel';
|
||||
|
||||
@override
|
||||
String get discussionTitleHint => 'Was möchtest du wissen?';
|
||||
|
||||
@override
|
||||
String get discussionPost => 'Posten';
|
||||
|
||||
@override
|
||||
String get discussionComment => 'Kommentar hinzufügen';
|
||||
|
||||
@override
|
||||
String get discussionCommentHint => 'Eine kurze Bemerkung';
|
||||
|
||||
@override
|
||||
String get discussionAccept => 'Annehmen';
|
||||
|
||||
@override
|
||||
String get discussionUnaccept => 'Zurücknehmen';
|
||||
|
||||
@override
|
||||
String get discussionMenu => 'Mehr';
|
||||
|
||||
@override
|
||||
String get discussionMenuEdit => 'Bearbeiten';
|
||||
|
||||
@override
|
||||
String get discussionMenuDelete => 'Löschen';
|
||||
|
||||
@override
|
||||
String get discussionMenuHide => 'Ausblenden';
|
||||
|
||||
@override
|
||||
String get discussionMenuShow => 'Wieder zeigen';
|
||||
|
||||
@override
|
||||
String discussionMenuBlock(String name) {
|
||||
return '$name blockieren';
|
||||
}
|
||||
|
||||
@override
|
||||
String get discussionDeleteTitle => 'Das löschen?';
|
||||
|
||||
@override
|
||||
String get discussionDeleteBody =>
|
||||
'Was andere darunter geantwortet haben, bleibt, ohne deinen Text und Namen.';
|
||||
|
||||
@override
|
||||
String get discussionAnswered => 'Antwort gepostet.';
|
||||
|
||||
@override
|
||||
String get discussionCommented => 'Kommentar gepostet.';
|
||||
|
||||
@override
|
||||
String get discussionSaved => 'Gespeichert.';
|
||||
|
||||
@override
|
||||
String get discussionDeletedDone => 'Gelöscht.';
|
||||
|
||||
@override
|
||||
String get discussionAcceptedDone => 'Antwort angenommen.';
|
||||
|
||||
@override
|
||||
String get discussionUnacceptedDone => 'Jetzt ist keine Antwort angenommen.';
|
||||
|
||||
@override
|
||||
String get discussionHiddenDone =>
|
||||
'Ausgeblendet: Nur wer es geschrieben hat und du sehen es.';
|
||||
|
||||
@override
|
||||
String get discussionShownDone => 'Wieder sichtbar.';
|
||||
|
||||
@override
|
||||
String get discussionBlockedDone =>
|
||||
'Blockiert: Was diese Person zu deinen Lernsets schreibt, sieht nur sie selbst.';
|
||||
|
||||
@override
|
||||
String get discussionAskAbout => 'Dazu fragen';
|
||||
|
||||
@override
|
||||
String get inboxDiscussions => 'Diskussionen';
|
||||
|
||||
@override
|
||||
String get inboxFeedback => 'Meldungen und Vorschläge';
|
||||
|
||||
@override
|
||||
String get inboxDiscussionsEmpty => 'Noch keine Diskussionen.';
|
||||
|
||||
@override
|
||||
String get cardReportToAuthor => 'Auch im Original melden';
|
||||
|
||||
|
|
|
|||
|
|
@ -6455,7 +6455,7 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get inboxIntro =>
|
||||
'Reports and suggestions on your published studysets, and those you wrote.';
|
||||
'Questions and answers, reports and suggestions on your published studysets, and those you wrote in.';
|
||||
|
||||
@override
|
||||
String get inboxEmpty => 'Nothing yet.';
|
||||
|
|
@ -6470,6 +6470,222 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
@override
|
||||
String get reportToAuthor => 'Report to the author';
|
||||
|
||||
@override
|
||||
String get discussionsTitle => 'Discussions';
|
||||
|
||||
@override
|
||||
String get discussionsSearch => 'Search questions';
|
||||
|
||||
@override
|
||||
String get discussionsAsk => 'Ask a question';
|
||||
|
||||
@override
|
||||
String get discussionsEmpty => 'No questions yet';
|
||||
|
||||
@override
|
||||
String get discussionsEmptyHint =>
|
||||
'Ask the first: about the studyset, a topic or one of its flashcards.';
|
||||
|
||||
@override
|
||||
String get discussionsNoMatch => 'No question\'s title says that.';
|
||||
|
||||
@override
|
||||
String get discussionsMore => 'Show more';
|
||||
|
||||
@override
|
||||
String get discussionsUnreachable =>
|
||||
'The discussions can\'t be reached right now.';
|
||||
|
||||
@override
|
||||
String discussionsAnswers(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count answers',
|
||||
one: '1 answer',
|
||||
zero: 'No answers',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String discussionsBy(String name, String date) {
|
||||
return '$name · $date';
|
||||
}
|
||||
|
||||
@override
|
||||
String get discussionsSomeone => 'Someone';
|
||||
|
||||
@override
|
||||
String get discussionsDeletedQuestion => 'Deleted question';
|
||||
|
||||
@override
|
||||
String get discussionsHidden => 'Hidden';
|
||||
|
||||
@override
|
||||
String get discussionsAboutCard => 'About a flashcard';
|
||||
|
||||
@override
|
||||
String get discussionsAboutNote => 'About a note';
|
||||
|
||||
@override
|
||||
String get discussionsAboutStep => 'About a step';
|
||||
|
||||
@override
|
||||
String get discussionsAccepted => 'Accepted';
|
||||
|
||||
@override
|
||||
String discussionsOn(String title) {
|
||||
return 'On $title';
|
||||
}
|
||||
|
||||
@override
|
||||
String get discussionRefusalNoIdentity =>
|
||||
'To ask and answer, sign in with an identity in Profile: posts show who wrote them.';
|
||||
|
||||
@override
|
||||
String get discussionRefusalNoProfile =>
|
||||
'To ask and answer, make a public profile in Profile: posts show who wrote them.';
|
||||
|
||||
@override
|
||||
String get discussionRefusalBlocked =>
|
||||
'The author of this studyset keeps you from writing on their studysets.';
|
||||
|
||||
@override
|
||||
String get discussionRefusalWithdrawn =>
|
||||
'The author took this studyset off Explore: what is here stays, but nothing new is asked.';
|
||||
|
||||
@override
|
||||
String get discussionRefusalNotInRelease =>
|
||||
'That isn\'t in the studyset\'s release any more.';
|
||||
|
||||
@override
|
||||
String get discussionRefusalEmpty => 'Write something first.';
|
||||
|
||||
@override
|
||||
String get discussionRefusalTooLong => 'That\'s longer than allowed.';
|
||||
|
||||
@override
|
||||
String get discussionRefusalTooMany =>
|
||||
'You\'ve written a lot in the last hour. Try again a little later.';
|
||||
|
||||
@override
|
||||
String get discussionRefusalWithFiles =>
|
||||
'Images and sounds can\'t be posted: your files are yours alone, so nobody else would see them.';
|
||||
|
||||
@override
|
||||
String get discussionFailed =>
|
||||
'Couldn\'t reach the instance. Try again once you are online.';
|
||||
|
||||
@override
|
||||
String get discussionTitle => 'Question';
|
||||
|
||||
@override
|
||||
String get discussionMissing => 'This question isn\'t there any more.';
|
||||
|
||||
@override
|
||||
String get discussionDeleted => 'This was deleted.';
|
||||
|
||||
@override
|
||||
String get discussionEdited => 'edited';
|
||||
|
||||
@override
|
||||
String get discussionWriteAnswer => 'Write an answer';
|
||||
|
||||
@override
|
||||
String get discussionYourAnswer => 'Your answer';
|
||||
|
||||
@override
|
||||
String get discussionEditHeading => 'Edit';
|
||||
|
||||
@override
|
||||
String get discussionTitleField => 'Title';
|
||||
|
||||
@override
|
||||
String get discussionTitleHint => 'What do you want to know?';
|
||||
|
||||
@override
|
||||
String get discussionPost => 'Post';
|
||||
|
||||
@override
|
||||
String get discussionComment => 'Add a comment';
|
||||
|
||||
@override
|
||||
String get discussionCommentHint => 'A short remark';
|
||||
|
||||
@override
|
||||
String get discussionAccept => 'Accept';
|
||||
|
||||
@override
|
||||
String get discussionUnaccept => 'Take back';
|
||||
|
||||
@override
|
||||
String get discussionMenu => 'More';
|
||||
|
||||
@override
|
||||
String get discussionMenuEdit => 'Edit';
|
||||
|
||||
@override
|
||||
String get discussionMenuDelete => 'Delete';
|
||||
|
||||
@override
|
||||
String get discussionMenuHide => 'Hide';
|
||||
|
||||
@override
|
||||
String get discussionMenuShow => 'Show again';
|
||||
|
||||
@override
|
||||
String discussionMenuBlock(String name) {
|
||||
return 'Block $name';
|
||||
}
|
||||
|
||||
@override
|
||||
String get discussionDeleteTitle => 'Delete this?';
|
||||
|
||||
@override
|
||||
String get discussionDeleteBody =>
|
||||
'What others replied under it stays, without your text and name.';
|
||||
|
||||
@override
|
||||
String get discussionAnswered => 'Answer posted.';
|
||||
|
||||
@override
|
||||
String get discussionCommented => 'Comment posted.';
|
||||
|
||||
@override
|
||||
String get discussionSaved => 'Saved.';
|
||||
|
||||
@override
|
||||
String get discussionDeletedDone => 'Deleted.';
|
||||
|
||||
@override
|
||||
String get discussionAcceptedDone => 'Answer accepted.';
|
||||
|
||||
@override
|
||||
String get discussionUnacceptedDone => 'No answer accepted now.';
|
||||
|
||||
@override
|
||||
String get discussionHiddenDone => 'Hidden: only its writer and you see it.';
|
||||
|
||||
@override
|
||||
String get discussionShownDone => 'Shown again.';
|
||||
|
||||
@override
|
||||
String get discussionBlockedDone =>
|
||||
'Blocked: what they write on your studysets shows to them alone.';
|
||||
|
||||
@override
|
||||
String get discussionAskAbout => 'Ask about this';
|
||||
|
||||
@override
|
||||
String get inboxDiscussions => 'Discussions';
|
||||
|
||||
@override
|
||||
String get inboxFeedback => 'Reports and suggestions';
|
||||
|
||||
@override
|
||||
String get inboxDiscussionsEmpty => 'No discussions yet.';
|
||||
|
||||
@override
|
||||
String get cardReportToAuthor => 'Send to the author too';
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@ import 'package:halkyon_learn_flutter/src/feature/card/bloc/card_composer_bloc.d
|
|||
import 'package:halkyon_learn_flutter/src/feature/card/widget/card_browser_screen.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/card/widget/composer/card_composer_screen.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/dictionary/widget/dictionary_screen.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/discussion/model/discussion.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/discussion/widget/discussion_screen.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/discussion/widget/discussions_screen.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/explore/widget/explore_screen.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/feedback/bloc/inbox_count_bloc.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/home/widget/home_screen.dart';
|
||||
|
|
@ -294,6 +297,29 @@ GoRouter buildRouter({String initialLocation = RoutePaths.home}) => GoRouter(
|
|||
page: int.tryParse(state.uri.queryParameters['page'] ?? ''),
|
||||
),
|
||||
),
|
||||
// A published studyset's discussions, and each of them (ADR 0013).
|
||||
GoRoute(
|
||||
path: '/discussions/:publicId',
|
||||
builder: (context, state) => FramedScreen(
|
||||
child: DiscussionsScreen(
|
||||
publicId: state.pathParameters['publicId']!,
|
||||
topic: state.uri.queryParameters['topic'],
|
||||
version: int.tryParse(state.uri.queryParameters['version'] ?? ''),
|
||||
title: state.uri.queryParameters['title'],
|
||||
ask: targetFromQuery(state.uri.queryParameters['ask']),
|
||||
),
|
||||
),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: ':questionId',
|
||||
builder: (context, state) => FramedScreen(
|
||||
child: DiscussionScreen(
|
||||
questionId: state.pathParameters['questionId']!,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
GoRoute(
|
||||
path: RoutePaths.settings,
|
||||
builder: (context, state) => const FramedScreen(child: SettingsScreen()),
|
||||
|
|
|
|||
|
|
@ -92,9 +92,10 @@ class _PersonAppState() extends State<PersonApp> {
|
|||
),
|
||||
// What is new in the inbox, for the Profile tab's badge.
|
||||
BlocProvider(
|
||||
create: (context) =>
|
||||
InboxCountBloc(PersonScope.of(context).feedback)
|
||||
..add(const InboxCountEventRefreshed()),
|
||||
create: (context) => InboxCountBloc(
|
||||
PersonScope.of(context).feedback,
|
||||
PersonScope.of(context).discussions,
|
||||
)..add(const InboxCountEventRefreshed()),
|
||||
),
|
||||
// The Pomodoro timer goes on across every screen.
|
||||
BlocProvider(
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ import 'package:halkyon_learn_flutter/src/feature/dictionary/data/device_diction
|
|||
import 'package:halkyon_learn_flutter/src/feature/dictionary/data/dictionary_repository.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/dictionary/data/japanese_analyser.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/dictionary/data/serverpod_dictionary_gateway.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/discussion/data/discussion_repository.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/discussion/data/serverpod_discussion_gateway.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/explore/data/copy_updater.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/explore/data/explore_repository.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/explore/data/release_copier.dart';
|
||||
|
|
@ -429,6 +431,7 @@ Future<PersonDependencies> _openPerson(
|
|||
);
|
||||
await studysetTabs.open();
|
||||
|
||||
final discussions = DiscussionRepository(ServerpodDiscussionGateway(client));
|
||||
return PersonDependencies(
|
||||
sync: sync,
|
||||
studysets: studysets,
|
||||
|
|
@ -467,6 +470,7 @@ Future<PersonDependencies> _openPerson(
|
|||
paths,
|
||||
assets,
|
||||
),
|
||||
discussions: discussions,
|
||||
onDispose: () async {
|
||||
try {
|
||||
// Deletions first: those still waiting are dropped, those under way
|
||||
|
|
@ -478,6 +482,7 @@ Future<PersonDependencies> _openPerson(
|
|||
await pathDeletions.dispose();
|
||||
await stepDeletions.dispose();
|
||||
await pdfDeletions.dispose();
|
||||
await discussions.dispose();
|
||||
await studysets.dispose();
|
||||
await tags.dispose();
|
||||
await settings.dispose();
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import 'package:halkyon_learn_flutter/src/core/asset/data/asset_repository.dart'
|
|||
import 'package:halkyon_learn_flutter/src/core/sync/sync_service.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/card/data/card_deletions.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/discussion/data/discussion_repository.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/explore/data/explore_repository.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/feedback/data/feedback_repository.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/learning_path/data/learning_path_deletions.dart';
|
||||
|
|
@ -95,6 +96,9 @@ class PersonDependencies({
|
|||
|
||||
/// Reports and suggestions on published studysets.
|
||||
required final FeedbackRepository feedback,
|
||||
|
||||
/// Published studysets' discussions, on the instance.
|
||||
required final DiscussionRepository discussions,
|
||||
final Future<void> Function()? _onDispose,
|
||||
}) {
|
||||
/// Closes what was opened for the person.
|
||||
|
|
|
|||
|
|
@ -102,4 +102,32 @@ abstract final class RoutePaths() {
|
|||
/// A step of a learning path, to write, over the tabs.
|
||||
static String pathStep(String studysetId, String pathId, String stepId) =>
|
||||
'/studysets/$studysetId/paths/$pathId/steps/$stepId';
|
||||
|
||||
/// A published studyset's discussions, over the tabs: those within the
|
||||
/// topic [topic] (a studyset's id in the release [version], which asking
|
||||
/// about it names), named [title], or all; with [ask]
|
||||
/// (`<kind>:<item>:<version>`: a card, note or step), asking about it at
|
||||
/// once.
|
||||
static String discussions(
|
||||
String publicId, {
|
||||
String? topic,
|
||||
int? version,
|
||||
String? title,
|
||||
String? ask,
|
||||
}) {
|
||||
final query = {
|
||||
'topic': ?topic,
|
||||
if (version != null) 'version': '$version',
|
||||
'title': ?title,
|
||||
'ask': ?ask,
|
||||
};
|
||||
return Uri(
|
||||
path: '/discussions/$publicId',
|
||||
queryParameters: query.isEmpty ? null : query,
|
||||
).toString();
|
||||
}
|
||||
|
||||
/// A question on a published studyset, with its answers, over the tabs.
|
||||
static String discussion(String publicId, String questionId) =>
|
||||
'/discussions/$publicId/$questionId';
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue