mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 07:48:40 +00:00
draft images, multiple bug fixes
This commit is contained in:
parent
aebf6de4a5
commit
80d6f85350
21 changed files with 261 additions and 28 deletions
|
|
@ -156,7 +156,7 @@ class _AppMainWidgetState extends State<AppMainWidget> {
|
|||
bool _showOnboarding = true;
|
||||
bool _isLoaded = false;
|
||||
|
||||
Future<int>? _proofOfWork;
|
||||
(Future<int>?, bool) _proofOfWork = (null, false);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
|
@ -176,11 +176,14 @@ class _AppMainWidgetState extends State<AppMainWidget> {
|
|||
if (!_isUserCreated && !_showDatabaseMigration) {
|
||||
// This means the user is in the onboarding screen, so start with the Proof of Work.
|
||||
|
||||
final proof = await apiService.getProofOfWork();
|
||||
final (proof, disabled) = await apiService.getProofOfWork();
|
||||
if (proof != null) {
|
||||
Log.info('Starting with proof of work calculation.');
|
||||
// Starting with the proof of work.
|
||||
_proofOfWork = calculatePoW(proof.prefix, proof.difficulty.toInt());
|
||||
_proofOfWork =
|
||||
(calculatePoW(proof.prefix, proof.difficulty.toInt()), false);
|
||||
} else {
|
||||
_proofOfWork = (null, disabled);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -50,11 +50,27 @@ class MediaFilesDao extends DatabaseAccessor<TwonlyDB>
|
|||
.write(updates);
|
||||
}
|
||||
|
||||
Future<void> updateAllMediaFiles(
|
||||
MediaFilesCompanion updates,
|
||||
) async {
|
||||
await update(mediaFiles).write(updates);
|
||||
}
|
||||
|
||||
Future<MediaFile?> getMediaFileById(String mediaId) async {
|
||||
return (select(mediaFiles)..where((t) => t.mediaId.equals(mediaId)))
|
||||
.getSingleOrNull();
|
||||
}
|
||||
|
||||
Future<MediaFile?> getDraftMediaFile() async {
|
||||
final medias = await (select(mediaFiles)
|
||||
..where((t) => t.isDraftMedia.equals(true)))
|
||||
.get();
|
||||
if (medias.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
return medias.first;
|
||||
}
|
||||
|
||||
Stream<MediaFile?> watchMedia(String mediaId) {
|
||||
return (select(mediaFiles)..where((t) => t.mediaId.equals(mediaId)))
|
||||
.watchSingleOrNull();
|
||||
|
|
@ -87,10 +103,9 @@ class MediaFilesDao extends DatabaseAccessor<TwonlyDB>
|
|||
Future<List<MediaFile>> getAllMediaFilesPendingUpload() async {
|
||||
return (select(mediaFiles)
|
||||
..where(
|
||||
(t) =>
|
||||
t.uploadState.equals(UploadState.initialized.name) |
|
||||
(t) => (t.uploadState.equals(UploadState.initialized.name) |
|
||||
t.uploadState.equals(UploadState.uploadLimitReached.name) |
|
||||
t.uploadState.equals(UploadState.preprocessing.name),
|
||||
t.uploadState.equals(UploadState.preprocessing.name)),
|
||||
))
|
||||
.get();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,10 +44,12 @@ class MediaFiles extends Table {
|
|||
|
||||
BoolColumn get requiresAuthentication =>
|
||||
boolean().withDefault(const Constant(false))();
|
||||
|
||||
BoolColumn get reopenByContact =>
|
||||
boolean().withDefault(const Constant(false))();
|
||||
|
||||
BoolColumn get stored => boolean().withDefault(const Constant(false))();
|
||||
BoolColumn get isDraftMedia => boolean().withDefault(const Constant(false))();
|
||||
|
||||
TextColumn get reuploadRequestedBy =>
|
||||
text().map(IntListTypeConverter()).nullable()();
|
||||
|
|
|
|||
|
|
@ -1905,6 +1905,16 @@ class $MediaFilesTable extends MediaFiles
|
|||
defaultConstraints:
|
||||
GeneratedColumn.constraintIsAlways('CHECK ("stored" IN (0, 1))'),
|
||||
defaultValue: const Constant(false));
|
||||
static const VerificationMeta _isDraftMediaMeta =
|
||||
const VerificationMeta('isDraftMedia');
|
||||
@override
|
||||
late final GeneratedColumn<bool> isDraftMedia = GeneratedColumn<bool>(
|
||||
'is_draft_media', aliasedName, false,
|
||||
type: DriftSqlType.bool,
|
||||
requiredDuringInsert: false,
|
||||
defaultConstraints: GeneratedColumn.constraintIsAlways(
|
||||
'CHECK ("is_draft_media" IN (0, 1))'),
|
||||
defaultValue: const Constant(false));
|
||||
@override
|
||||
late final GeneratedColumnWithTypeConverter<List<int>?, String>
|
||||
reuploadRequestedBy = GeneratedColumn<String>(
|
||||
|
|
@ -1968,6 +1978,7 @@ class $MediaFilesTable extends MediaFiles
|
|||
requiresAuthentication,
|
||||
reopenByContact,
|
||||
stored,
|
||||
isDraftMedia,
|
||||
reuploadRequestedBy,
|
||||
displayLimitInMilliseconds,
|
||||
removeAudio,
|
||||
|
|
@ -2009,6 +2020,12 @@ class $MediaFilesTable extends MediaFiles
|
|||
context.handle(_storedMeta,
|
||||
stored.isAcceptableOrUnknown(data['stored']!, _storedMeta));
|
||||
}
|
||||
if (data.containsKey('is_draft_media')) {
|
||||
context.handle(
|
||||
_isDraftMediaMeta,
|
||||
isDraftMedia.isAcceptableOrUnknown(
|
||||
data['is_draft_media']!, _isDraftMediaMeta));
|
||||
}
|
||||
if (data.containsKey('display_limit_in_milliseconds')) {
|
||||
context.handle(
|
||||
_displayLimitInMillisecondsMeta,
|
||||
|
|
@ -2076,6 +2093,8 @@ class $MediaFilesTable extends MediaFiles
|
|||
DriftSqlType.bool, data['${effectivePrefix}reopen_by_contact'])!,
|
||||
stored: attachedDatabase.typeMapping
|
||||
.read(DriftSqlType.bool, data['${effectivePrefix}stored'])!,
|
||||
isDraftMedia: attachedDatabase.typeMapping
|
||||
.read(DriftSqlType.bool, data['${effectivePrefix}is_draft_media'])!,
|
||||
reuploadRequestedBy: $MediaFilesTable.$converterreuploadRequestedByn
|
||||
.fromSql(attachedDatabase.typeMapping.read(DriftSqlType.string,
|
||||
data['${effectivePrefix}reupload_requested_by'])),
|
||||
|
|
@ -2129,6 +2148,7 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
|||
final bool requiresAuthentication;
|
||||
final bool reopenByContact;
|
||||
final bool stored;
|
||||
final bool isDraftMedia;
|
||||
final List<int>? reuploadRequestedBy;
|
||||
final int? displayLimitInMilliseconds;
|
||||
final bool? removeAudio;
|
||||
|
|
@ -2145,6 +2165,7 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
|||
required this.requiresAuthentication,
|
||||
required this.reopenByContact,
|
||||
required this.stored,
|
||||
required this.isDraftMedia,
|
||||
this.reuploadRequestedBy,
|
||||
this.displayLimitInMilliseconds,
|
||||
this.removeAudio,
|
||||
|
|
@ -2172,6 +2193,7 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
|||
map['requires_authentication'] = Variable<bool>(requiresAuthentication);
|
||||
map['reopen_by_contact'] = Variable<bool>(reopenByContact);
|
||||
map['stored'] = Variable<bool>(stored);
|
||||
map['is_draft_media'] = Variable<bool>(isDraftMedia);
|
||||
if (!nullToAbsent || reuploadRequestedBy != null) {
|
||||
map['reupload_requested_by'] = Variable<String>($MediaFilesTable
|
||||
.$converterreuploadRequestedByn
|
||||
|
|
@ -2213,6 +2235,7 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
|||
requiresAuthentication: Value(requiresAuthentication),
|
||||
reopenByContact: Value(reopenByContact),
|
||||
stored: Value(stored),
|
||||
isDraftMedia: Value(isDraftMedia),
|
||||
reuploadRequestedBy: reuploadRequestedBy == null && nullToAbsent
|
||||
? const Value.absent()
|
||||
: Value(reuploadRequestedBy),
|
||||
|
|
@ -2254,6 +2277,7 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
|||
serializer.fromJson<bool>(json['requiresAuthentication']),
|
||||
reopenByContact: serializer.fromJson<bool>(json['reopenByContact']),
|
||||
stored: serializer.fromJson<bool>(json['stored']),
|
||||
isDraftMedia: serializer.fromJson<bool>(json['isDraftMedia']),
|
||||
reuploadRequestedBy:
|
||||
serializer.fromJson<List<int>?>(json['reuploadRequestedBy']),
|
||||
displayLimitInMilliseconds:
|
||||
|
|
@ -2280,6 +2304,7 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
|||
'requiresAuthentication': serializer.toJson<bool>(requiresAuthentication),
|
||||
'reopenByContact': serializer.toJson<bool>(reopenByContact),
|
||||
'stored': serializer.toJson<bool>(stored),
|
||||
'isDraftMedia': serializer.toJson<bool>(isDraftMedia),
|
||||
'reuploadRequestedBy': serializer.toJson<List<int>?>(reuploadRequestedBy),
|
||||
'displayLimitInMilliseconds':
|
||||
serializer.toJson<int?>(displayLimitInMilliseconds),
|
||||
|
|
@ -2300,6 +2325,7 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
|||
bool? requiresAuthentication,
|
||||
bool? reopenByContact,
|
||||
bool? stored,
|
||||
bool? isDraftMedia,
|
||||
Value<List<int>?> reuploadRequestedBy = const Value.absent(),
|
||||
Value<int?> displayLimitInMilliseconds = const Value.absent(),
|
||||
Value<bool?> removeAudio = const Value.absent(),
|
||||
|
|
@ -2318,6 +2344,7 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
|||
requiresAuthentication ?? this.requiresAuthentication,
|
||||
reopenByContact: reopenByContact ?? this.reopenByContact,
|
||||
stored: stored ?? this.stored,
|
||||
isDraftMedia: isDraftMedia ?? this.isDraftMedia,
|
||||
reuploadRequestedBy: reuploadRequestedBy.present
|
||||
? reuploadRequestedBy.value
|
||||
: this.reuploadRequestedBy,
|
||||
|
|
@ -2352,6 +2379,9 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
|||
? data.reopenByContact.value
|
||||
: this.reopenByContact,
|
||||
stored: data.stored.present ? data.stored.value : this.stored,
|
||||
isDraftMedia: data.isDraftMedia.present
|
||||
? data.isDraftMedia.value
|
||||
: this.isDraftMedia,
|
||||
reuploadRequestedBy: data.reuploadRequestedBy.present
|
||||
? data.reuploadRequestedBy.value
|
||||
: this.reuploadRequestedBy,
|
||||
|
|
@ -2386,6 +2416,7 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
|||
..write('requiresAuthentication: $requiresAuthentication, ')
|
||||
..write('reopenByContact: $reopenByContact, ')
|
||||
..write('stored: $stored, ')
|
||||
..write('isDraftMedia: $isDraftMedia, ')
|
||||
..write('reuploadRequestedBy: $reuploadRequestedBy, ')
|
||||
..write('displayLimitInMilliseconds: $displayLimitInMilliseconds, ')
|
||||
..write('removeAudio: $removeAudio, ')
|
||||
|
|
@ -2407,6 +2438,7 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
|||
requiresAuthentication,
|
||||
reopenByContact,
|
||||
stored,
|
||||
isDraftMedia,
|
||||
reuploadRequestedBy,
|
||||
displayLimitInMilliseconds,
|
||||
removeAudio,
|
||||
|
|
@ -2426,6 +2458,7 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
|||
other.requiresAuthentication == this.requiresAuthentication &&
|
||||
other.reopenByContact == this.reopenByContact &&
|
||||
other.stored == this.stored &&
|
||||
other.isDraftMedia == this.isDraftMedia &&
|
||||
other.reuploadRequestedBy == this.reuploadRequestedBy &&
|
||||
other.displayLimitInMilliseconds == this.displayLimitInMilliseconds &&
|
||||
other.removeAudio == this.removeAudio &&
|
||||
|
|
@ -2445,6 +2478,7 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
|
|||
final Value<bool> requiresAuthentication;
|
||||
final Value<bool> reopenByContact;
|
||||
final Value<bool> stored;
|
||||
final Value<bool> isDraftMedia;
|
||||
final Value<List<int>?> reuploadRequestedBy;
|
||||
final Value<int?> displayLimitInMilliseconds;
|
||||
final Value<bool?> removeAudio;
|
||||
|
|
@ -2462,6 +2496,7 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
|
|||
this.requiresAuthentication = const Value.absent(),
|
||||
this.reopenByContact = const Value.absent(),
|
||||
this.stored = const Value.absent(),
|
||||
this.isDraftMedia = const Value.absent(),
|
||||
this.reuploadRequestedBy = const Value.absent(),
|
||||
this.displayLimitInMilliseconds = const Value.absent(),
|
||||
this.removeAudio = const Value.absent(),
|
||||
|
|
@ -2480,6 +2515,7 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
|
|||
this.requiresAuthentication = const Value.absent(),
|
||||
this.reopenByContact = const Value.absent(),
|
||||
this.stored = const Value.absent(),
|
||||
this.isDraftMedia = const Value.absent(),
|
||||
this.reuploadRequestedBy = const Value.absent(),
|
||||
this.displayLimitInMilliseconds = const Value.absent(),
|
||||
this.removeAudio = const Value.absent(),
|
||||
|
|
@ -2499,6 +2535,7 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
|
|||
Expression<bool>? requiresAuthentication,
|
||||
Expression<bool>? reopenByContact,
|
||||
Expression<bool>? stored,
|
||||
Expression<bool>? isDraftMedia,
|
||||
Expression<String>? reuploadRequestedBy,
|
||||
Expression<int>? displayLimitInMilliseconds,
|
||||
Expression<bool>? removeAudio,
|
||||
|
|
@ -2518,6 +2555,7 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
|
|||
'requires_authentication': requiresAuthentication,
|
||||
if (reopenByContact != null) 'reopen_by_contact': reopenByContact,
|
||||
if (stored != null) 'stored': stored,
|
||||
if (isDraftMedia != null) 'is_draft_media': isDraftMedia,
|
||||
if (reuploadRequestedBy != null)
|
||||
'reupload_requested_by': reuploadRequestedBy,
|
||||
if (displayLimitInMilliseconds != null)
|
||||
|
|
@ -2540,6 +2578,7 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
|
|||
Value<bool>? requiresAuthentication,
|
||||
Value<bool>? reopenByContact,
|
||||
Value<bool>? stored,
|
||||
Value<bool>? isDraftMedia,
|
||||
Value<List<int>?>? reuploadRequestedBy,
|
||||
Value<int?>? displayLimitInMilliseconds,
|
||||
Value<bool?>? removeAudio,
|
||||
|
|
@ -2558,6 +2597,7 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
|
|||
requiresAuthentication ?? this.requiresAuthentication,
|
||||
reopenByContact: reopenByContact ?? this.reopenByContact,
|
||||
stored: stored ?? this.stored,
|
||||
isDraftMedia: isDraftMedia ?? this.isDraftMedia,
|
||||
reuploadRequestedBy: reuploadRequestedBy ?? this.reuploadRequestedBy,
|
||||
displayLimitInMilliseconds:
|
||||
displayLimitInMilliseconds ?? this.displayLimitInMilliseconds,
|
||||
|
|
@ -2599,6 +2639,9 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
|
|||
if (stored.present) {
|
||||
map['stored'] = Variable<bool>(stored.value);
|
||||
}
|
||||
if (isDraftMedia.present) {
|
||||
map['is_draft_media'] = Variable<bool>(isDraftMedia.value);
|
||||
}
|
||||
if (reuploadRequestedBy.present) {
|
||||
map['reupload_requested_by'] = Variable<String>($MediaFilesTable
|
||||
.$converterreuploadRequestedByn
|
||||
|
|
@ -2642,6 +2685,7 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
|
|||
..write('requiresAuthentication: $requiresAuthentication, ')
|
||||
..write('reopenByContact: $reopenByContact, ')
|
||||
..write('stored: $stored, ')
|
||||
..write('isDraftMedia: $isDraftMedia, ')
|
||||
..write('reuploadRequestedBy: $reuploadRequestedBy, ')
|
||||
..write('displayLimitInMilliseconds: $displayLimitInMilliseconds, ')
|
||||
..write('removeAudio: $removeAudio, ')
|
||||
|
|
@ -9161,6 +9205,7 @@ typedef $$MediaFilesTableCreateCompanionBuilder = MediaFilesCompanion Function({
|
|||
Value<bool> requiresAuthentication,
|
||||
Value<bool> reopenByContact,
|
||||
Value<bool> stored,
|
||||
Value<bool> isDraftMedia,
|
||||
Value<List<int>?> reuploadRequestedBy,
|
||||
Value<int?> displayLimitInMilliseconds,
|
||||
Value<bool?> removeAudio,
|
||||
|
|
@ -9179,6 +9224,7 @@ typedef $$MediaFilesTableUpdateCompanionBuilder = MediaFilesCompanion Function({
|
|||
Value<bool> requiresAuthentication,
|
||||
Value<bool> reopenByContact,
|
||||
Value<bool> stored,
|
||||
Value<bool> isDraftMedia,
|
||||
Value<List<int>?> reuploadRequestedBy,
|
||||
Value<int?> displayLimitInMilliseconds,
|
||||
Value<bool?> removeAudio,
|
||||
|
|
@ -9248,6 +9294,9 @@ class $$MediaFilesTableFilterComposer
|
|||
ColumnFilters<bool> get stored => $composableBuilder(
|
||||
column: $table.stored, builder: (column) => ColumnFilters(column));
|
||||
|
||||
ColumnFilters<bool> get isDraftMedia => $composableBuilder(
|
||||
column: $table.isDraftMedia, builder: (column) => ColumnFilters(column));
|
||||
|
||||
ColumnWithTypeConverterFilters<List<int>?, List<int>, String>
|
||||
get reuploadRequestedBy => $composableBuilder(
|
||||
column: $table.reuploadRequestedBy,
|
||||
|
|
@ -9331,6 +9380,10 @@ class $$MediaFilesTableOrderingComposer
|
|||
ColumnOrderings<bool> get stored => $composableBuilder(
|
||||
column: $table.stored, builder: (column) => ColumnOrderings(column));
|
||||
|
||||
ColumnOrderings<bool> get isDraftMedia => $composableBuilder(
|
||||
column: $table.isDraftMedia,
|
||||
builder: (column) => ColumnOrderings(column));
|
||||
|
||||
ColumnOrderings<String> get reuploadRequestedBy => $composableBuilder(
|
||||
column: $table.reuploadRequestedBy,
|
||||
builder: (column) => ColumnOrderings(column));
|
||||
|
|
@ -9394,6 +9447,9 @@ class $$MediaFilesTableAnnotationComposer
|
|||
GeneratedColumn<bool> get stored =>
|
||||
$composableBuilder(column: $table.stored, builder: (column) => column);
|
||||
|
||||
GeneratedColumn<bool> get isDraftMedia => $composableBuilder(
|
||||
column: $table.isDraftMedia, builder: (column) => column);
|
||||
|
||||
GeneratedColumnWithTypeConverter<List<int>?, String>
|
||||
get reuploadRequestedBy => $composableBuilder(
|
||||
column: $table.reuploadRequestedBy, builder: (column) => column);
|
||||
|
|
@ -9471,6 +9527,7 @@ class $$MediaFilesTableTableManager extends RootTableManager<
|
|||
Value<bool> requiresAuthentication = const Value.absent(),
|
||||
Value<bool> reopenByContact = const Value.absent(),
|
||||
Value<bool> stored = const Value.absent(),
|
||||
Value<bool> isDraftMedia = const Value.absent(),
|
||||
Value<List<int>?> reuploadRequestedBy = const Value.absent(),
|
||||
Value<int?> displayLimitInMilliseconds = const Value.absent(),
|
||||
Value<bool?> removeAudio = const Value.absent(),
|
||||
|
|
@ -9489,6 +9546,7 @@ class $$MediaFilesTableTableManager extends RootTableManager<
|
|||
requiresAuthentication: requiresAuthentication,
|
||||
reopenByContact: reopenByContact,
|
||||
stored: stored,
|
||||
isDraftMedia: isDraftMedia,
|
||||
reuploadRequestedBy: reuploadRequestedBy,
|
||||
displayLimitInMilliseconds: displayLimitInMilliseconds,
|
||||
removeAudio: removeAudio,
|
||||
|
|
@ -9507,6 +9565,7 @@ class $$MediaFilesTableTableManager extends RootTableManager<
|
|||
Value<bool> requiresAuthentication = const Value.absent(),
|
||||
Value<bool> reopenByContact = const Value.absent(),
|
||||
Value<bool> stored = const Value.absent(),
|
||||
Value<bool> isDraftMedia = const Value.absent(),
|
||||
Value<List<int>?> reuploadRequestedBy = const Value.absent(),
|
||||
Value<int?> displayLimitInMilliseconds = const Value.absent(),
|
||||
Value<bool?> removeAudio = const Value.absent(),
|
||||
|
|
@ -9525,6 +9584,7 @@ class $$MediaFilesTableTableManager extends RootTableManager<
|
|||
requiresAuthentication: requiresAuthentication,
|
||||
reopenByContact: reopenByContact,
|
||||
stored: stored,
|
||||
isDraftMedia: isDraftMedia,
|
||||
reuploadRequestedBy: reuploadRequestedBy,
|
||||
displayLimitInMilliseconds: displayLimitInMilliseconds,
|
||||
removeAudio: removeAudio,
|
||||
|
|
|
|||
|
|
@ -817,5 +817,6 @@
|
|||
"deleteChatAfterAWeek": "einer Woche.",
|
||||
"deleteChatAfterAMonth": "einem Monat.",
|
||||
"deleteChatAfterAYear": "einem Jahr.",
|
||||
"yourTwonlyScore": "Dein twonly-Score"
|
||||
"yourTwonlyScore": "Dein twonly-Score",
|
||||
"registrationClosed": "Aufgrund des aktuell sehr hohen Aufkommens haben wir die Registrierung vorübergehend deaktiviert, damit der Dienst zuverlässig bleibt. Bitte versuche es in ein paar Tagen noch einmal."
|
||||
}
|
||||
|
|
@ -595,5 +595,6 @@
|
|||
"deleteChatAfterAWeek": "one week.",
|
||||
"deleteChatAfterAMonth": "one month.",
|
||||
"deleteChatAfterAYear": "one year.",
|
||||
"yourTwonlyScore": "Your twonly-Score"
|
||||
"yourTwonlyScore": "Your twonly-Score",
|
||||
"registrationClosed": "Due to the current high volume of registrations, we have temporarily disabled registration to ensure that the service remains reliable. Please try again in a few days."
|
||||
}
|
||||
|
|
@ -2677,6 +2677,12 @@ abstract class AppLocalizations {
|
|||
/// In en, this message translates to:
|
||||
/// **'Your twonly-Score'**
|
||||
String get yourTwonlyScore;
|
||||
|
||||
/// No description provided for @registrationClosed.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Due to the current high volume of registrations, we have temporarily disabled registration to ensure that the service remains reliable. Please try again in a few days.'**
|
||||
String get registrationClosed;
|
||||
}
|
||||
|
||||
class _AppLocalizationsDelegate
|
||||
|
|
|
|||
|
|
@ -1477,4 +1477,8 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get yourTwonlyScore => 'Dein twonly-Score';
|
||||
|
||||
@override
|
||||
String get registrationClosed =>
|
||||
'Aufgrund des aktuell sehr hohen Aufkommens haben wir die Registrierung vorübergehend deaktiviert, damit der Dienst zuverlässig bleibt. Bitte versuche es in ein paar Tagen noch einmal.';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1467,4 +1467,8 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get yourTwonlyScore => 'Your twonly-Score';
|
||||
|
||||
@override
|
||||
String get registrationClosed =>
|
||||
'Due to the current high volume of registrations, we have temporarily disabled registration to ensure that the service remains reliable. Please try again in a few days.';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,8 +51,9 @@ final lockRetransStore = Mutex();
|
|||
/// errors or network changes.
|
||||
class ApiService {
|
||||
ApiService();
|
||||
final String apiHost = kReleaseMode ? 'api.twonly.eu' : '10.99.0.140:3030';
|
||||
final String apiSecure = kReleaseMode ? 's' : '';
|
||||
// final String apiHost = kReleaseMode ? 'api.twonly.eu' : '10.99.0.140:3030';
|
||||
final String apiHost = kReleaseMode ? 'api.twonly.eu' : 'dev.twonly.eu';
|
||||
final String apiSecure = kReleaseMode ? 's' : 's';
|
||||
|
||||
bool appIsOutdated = false;
|
||||
bool isAuthenticated = false;
|
||||
|
|
@ -508,15 +509,19 @@ class ApiService {
|
|||
return null;
|
||||
}
|
||||
|
||||
Future<Response_ProofOfWork?> getProofOfWork() async {
|
||||
Future<(Response_ProofOfWork?, bool)> getProofOfWork() async {
|
||||
final handshake = Handshake()..requestPOW = Handshake_RequestPOW();
|
||||
final req = createClientToServerFromHandshake(handshake);
|
||||
final result = await sendRequestSync(req, authenticated: false);
|
||||
if (result.isError) {
|
||||
Log.error('could not request proof of work params', result);
|
||||
return null;
|
||||
if (result.error == ErrorCode.RegistrationDisabled) {
|
||||
return (null, true);
|
||||
}
|
||||
Log.error('could not request proof of work params', result);
|
||||
return (null, false);
|
||||
}
|
||||
return result.value.proofOfWork as Response_ProofOfWork;
|
||||
return (result.value.proofOfWork as Response_ProofOfWork, false);
|
||||
}
|
||||
|
||||
Future<Result> downloadDone(List<int> token) async {
|
||||
|
|
|
|||
|
|
@ -24,8 +24,24 @@ Future<void> finishStartedPreprocessing() async {
|
|||
await twonlyDB.mediaFilesDao.getAllMediaFilesPendingUpload();
|
||||
|
||||
for (final mediaFile in mediaFiles) {
|
||||
if (mediaFile.isDraftMedia) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
final service = await MediaFileService.fromMedia(mediaFile);
|
||||
if (!service.originalPath.existsSync() &&
|
||||
!service.uploadRequestPath.existsSync()) {
|
||||
if (service.storedPath.existsSync()) {
|
||||
// media files was just stored..
|
||||
continue;
|
||||
}
|
||||
Log.info(
|
||||
'Deleted media files, as originalPath and uploadRequestPath both do not exists',
|
||||
);
|
||||
// the file does not exists anymore.
|
||||
await twonlyDB.mediaFilesDao.deleteMediaFile(mediaFile.mediaId);
|
||||
continue;
|
||||
}
|
||||
await startBackgroundMediaUpload(service);
|
||||
} catch (e) {
|
||||
Log.error(e);
|
||||
|
|
@ -35,18 +51,24 @@ Future<void> finishStartedPreprocessing() async {
|
|||
|
||||
Future<MediaFileService?> initializeMediaUpload(
|
||||
MediaType type,
|
||||
int? displayLimitInMilliseconds,
|
||||
) async {
|
||||
int? displayLimitInMilliseconds, {
|
||||
bool isDraftMedia = false,
|
||||
}) async {
|
||||
final chacha20 = FlutterChacha20.poly1305Aead();
|
||||
final encryptionKey = await (await chacha20.newSecretKey()).extract();
|
||||
final encryptionNonce = chacha20.newNonce();
|
||||
|
||||
await twonlyDB.mediaFilesDao.updateAllMediaFiles(
|
||||
const MediaFilesCompanion(isDraftMedia: Value(false)),
|
||||
);
|
||||
|
||||
final mediaFile = await twonlyDB.mediaFilesDao.insertMedia(
|
||||
MediaFilesCompanion(
|
||||
uploadState: const Value(UploadState.initialized),
|
||||
displayLimitInMilliseconds: Value(displayLimitInMilliseconds),
|
||||
encryptionKey: Value(Uint8List.fromList(encryptionKey.bytes)),
|
||||
encryptionNonce: Value(Uint8List.fromList(encryptionNonce)),
|
||||
isDraftMedia: Value(isDraftMedia),
|
||||
type: Value(type),
|
||||
),
|
||||
);
|
||||
|
|
@ -58,6 +80,11 @@ Future<void> insertMediaFileInMessagesTable(
|
|||
MediaFileService mediaService,
|
||||
List<String> groupIds,
|
||||
) async {
|
||||
await twonlyDB.mediaFilesDao.updateAllMediaFiles(
|
||||
const MediaFilesCompanion(
|
||||
isDraftMedia: Value(false),
|
||||
),
|
||||
);
|
||||
for (final groupId in groupIds) {
|
||||
final message = await twonlyDB.messagesDao.insertMessage(
|
||||
MessagesCompanion(
|
||||
|
|
|
|||
|
|
@ -65,20 +65,24 @@ Future<void> compressAndOverlayVideo(MediaFileService media) async {
|
|||
if (media.tempPath.existsSync()) {
|
||||
media.tempPath.deleteSync();
|
||||
}
|
||||
if (media.ffmpegOutputPath.existsSync()) {
|
||||
media.ffmpegOutputPath.deleteSync();
|
||||
}
|
||||
|
||||
final stopwatch = Stopwatch()..start();
|
||||
var command =
|
||||
'-i "${media.originalPath.path}" -i "${media.overlayImagePath.path}" -filter_complex "[1:v][0:v]scale2ref=w=ref_w:h=ref_h[ovr][base];[base][ovr]overlay=0:0" -map "0:a?" -preset veryfast -crf 28 -c:a aac -b:a 64k "${media.tempPath.path}"';
|
||||
'-i "${media.originalPath.path}" -i "${media.overlayImagePath.path}" -filter_complex "[1:v][0:v]scale2ref=w=ref_w:h=ref_h[ovr][base];[base][ovr]overlay=0:0" -map "0:a?" -preset veryfast -crf 28 -c:a aac -b:a 64k "${media.ffmpegOutputPath.path}"';
|
||||
|
||||
if (media.removeAudio) {
|
||||
command =
|
||||
'-i "${media.originalPath.path}" -i "${media.overlayImagePath.path}" -filter_complex "[1:v][0:v]scale2ref=w=ref_w:h=ref_h[ovr][base];[base][ovr]overlay=0:0" -preset veryfast -crf 28 -an "${media.tempPath.path}"';
|
||||
'-i "${media.originalPath.path}" -i "${media.overlayImagePath.path}" -filter_complex "[1:v][0:v]scale2ref=w=ref_w:h=ref_h[ovr][base];[base][ovr]overlay=0:0" -preset veryfast -crf 28 -an "${media.ffmpegOutputPath.path}"';
|
||||
}
|
||||
|
||||
final session = await FFmpegKit.execute(command);
|
||||
final returnCode = await session.getReturnCode();
|
||||
|
||||
if (ReturnCode.isSuccess(returnCode)) {
|
||||
media.ffmpegOutputPath.copySync(media.tempPath.path);
|
||||
stopwatch.stop();
|
||||
Log.info(
|
||||
'It took ${stopwatch.elapsedMilliseconds}ms to compress the video',
|
||||
|
|
|
|||
|
|
@ -45,11 +45,16 @@ class MediaFileService {
|
|||
var delete = true;
|
||||
|
||||
final service = await MediaFileService.fromMediaId(mediaId);
|
||||
|
||||
if (service == null) {
|
||||
Log.error(
|
||||
'Purging media file, as it is not in the database $mediaId.',
|
||||
);
|
||||
} else {
|
||||
if (service.mediaFile.isDraftMedia) {
|
||||
delete = false;
|
||||
}
|
||||
|
||||
final messages =
|
||||
await twonlyDB.messagesDao.getMessagesByMediaId(mediaId);
|
||||
|
||||
|
|
@ -302,6 +307,10 @@ class MediaFileService {
|
|||
'tmp',
|
||||
namePrefix: '.original',
|
||||
);
|
||||
File get ffmpegOutputPath => _buildFilePath(
|
||||
'tmp',
|
||||
namePrefix: '.ffmpeg',
|
||||
);
|
||||
File get overlayImagePath => _buildFilePath(
|
||||
'tmp',
|
||||
namePrefix: '.overlay',
|
||||
|
|
|
|||
|
|
@ -253,7 +253,7 @@ String getPushNotificationText(PushNotification pushNotification) {
|
|||
PushKind.twonly.name: lang.notificationTwonly(inGroup),
|
||||
PushKind.video.name: lang.notificationVideo(inGroup),
|
||||
PushKind.image.name: lang.notificationImage(inGroup),
|
||||
PushKind.video.name: lang.notificationAudio(inGroup),
|
||||
PushKind.audio.name: lang.notificationAudio(inGroup),
|
||||
PushKind.contactRequest.name: lang.notificationContactRequest,
|
||||
PushKind.acceptRequest.name: lang.notificationAcceptRequest,
|
||||
PushKind.storedMediaFile.name: lang.notificationStoredMediaFile,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'package:camera/camera.dart';
|
||||
import 'package:drift/drift.dart' show Value;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_android_volume_keydown/flutter_android_volume_keydown.dart';
|
||||
|
|
@ -352,6 +353,7 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
|||
final mediaFileService = await initializeMediaUpload(
|
||||
type,
|
||||
gUser.defaultShowTime,
|
||||
isDraftMedia: true,
|
||||
);
|
||||
if (!mounted) return true;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,11 +2,13 @@
|
|||
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'package:drift/drift.dart' show Value;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:hashlib/random.dart';
|
||||
import 'package:screenshot/screenshot.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
||||
import 'package:twonly/src/database/twonly.db.dart';
|
||||
|
|
@ -82,6 +84,8 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
|||
} else {
|
||||
if (widget.mediaFileService.tempPath.existsSync()) {
|
||||
loadImage(widget.mediaFileService.tempPath.readAsBytes());
|
||||
} else if (widget.mediaFileService.originalPath.existsSync()) {
|
||||
loadImage(widget.mediaFileService.originalPath.readAsBytes());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -106,6 +110,11 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
|||
isDisposed = true;
|
||||
layers.clear();
|
||||
videoController?.dispose();
|
||||
twonlyDB.mediaFilesDao.updateAllMediaFiles(
|
||||
const MediaFilesCompanion(
|
||||
isDraftMedia: Value(false),
|
||||
),
|
||||
);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
|
@ -388,6 +397,10 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
|||
|
||||
Future<void> loadImage(Future<Uint8List?> imageBytesFuture) async {
|
||||
imageBytes = await imageBytesFuture;
|
||||
|
||||
// store this image so it can be used as a draft in case the app is restarted
|
||||
mediaService.originalPath.writeAsBytesSync(imageBytes!.toList());
|
||||
|
||||
await currentImage.load(imageBytes);
|
||||
if (isDisposed) return;
|
||||
|
||||
|
|
|
|||
|
|
@ -476,8 +476,8 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
|||
if (videoController != null)
|
||||
Positioned.fill(
|
||||
child: VideoPlayer(videoController!),
|
||||
),
|
||||
if (currentMedia != null &&
|
||||
)
|
||||
else if (currentMedia != null &&
|
||||
currentMedia!.mediaFile.type == MediaType.image ||
|
||||
currentMedia!.mediaFile.type == MediaType.gif)
|
||||
Positioned.fill(
|
||||
|
|
|
|||
|
|
@ -41,6 +41,12 @@ class _FlameCounterWidgetState extends State<FlameCounterWidget> {
|
|||
if (widget.groupId == null && widget.contactId != null) {
|
||||
final group = await twonlyDB.groupsDao.getDirectChat(widget.contactId!);
|
||||
groupId = group?.groupId;
|
||||
} else if (groupId != null) {
|
||||
// do not display the flame counter for groups
|
||||
final group = await twonlyDB.groupsDao.getGroup(groupId);
|
||||
if (!(group?.isDirectChat ?? false)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (groupId != null) {
|
||||
isBestFriend = gUser.myBestFriendGroupId == groupId;
|
||||
|
|
|
|||
|
|
@ -36,7 +36,8 @@ class _MaxFlameListTitleState extends State<MaxFlameListTitle> {
|
|||
_flameCounterSub = stream.listen((counter) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_flameCounter = counter;
|
||||
_flameCounter = counter -
|
||||
1; // in the watchFlameCounter a one is added, so remove this here
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -73,7 +74,7 @@ class _MaxFlameListTitleState extends State<MaxFlameListTitle> {
|
|||
await twonlyDB.groupsDao.updateGroup(
|
||||
_groupId,
|
||||
GroupsCompanion(
|
||||
flameCounter: Value(_directChat!.maxFlameCounter - 1),
|
||||
flameCounter: Value(_directChat!.maxFlameCounter),
|
||||
lastFlameCounterChange: Value(DateTime.now()),
|
||||
),
|
||||
);
|
||||
|
|
@ -84,7 +85,7 @@ class _MaxFlameListTitleState extends State<MaxFlameListTitle> {
|
|||
Widget build(BuildContext context) {
|
||||
if (_directChat == null ||
|
||||
_directChat!.maxFlameCounter == 0 ||
|
||||
_flameCounter >= (_directChat!.maxFlameCounter + 1) ||
|
||||
_flameCounter >= _directChat!.maxFlameCounter ||
|
||||
_directChat!.maxFlameCounterFrom!
|
||||
.isBefore(DateTime.now().subtract(const Duration(days: 4)))) {
|
||||
return Container();
|
||||
|
|
@ -97,7 +98,7 @@ class _MaxFlameListTitleState extends State<MaxFlameListTitle> {
|
|||
emoji: '🔥',
|
||||
),
|
||||
),
|
||||
text: 'Restore your ${_directChat!.maxFlameCounter} lost flames',
|
||||
text: 'Restore your ${_directChat!.maxFlameCounter + 1} lost flames',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,13 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:screenshot/screenshot.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
||||
import 'package:twonly/src/services/notifications/setup.notifications.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/views/camera/camera_preview_components/camera_preview.dart';
|
||||
import 'package:twonly/src/views/camera/camera_preview_controller_view.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor_view.dart';
|
||||
import 'package:twonly/src/views/chats/chat_list.view.dart';
|
||||
import 'package:twonly/src/views/memories/memories.view.dart';
|
||||
|
||||
|
|
@ -145,6 +148,21 @@ class HomeViewState extends State<HomeView> {
|
|||
globalUpdateOfHomeViewPageIndex(0);
|
||||
}
|
||||
}
|
||||
|
||||
final draftMedia = await twonlyDB.mediaFilesDao.getDraftMediaFile();
|
||||
if (draftMedia != null) {
|
||||
final service = await MediaFileService.fromMedia(draftMedia);
|
||||
if (!mounted) return;
|
||||
await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => ShareImageEditorView(
|
||||
mediaFileService: service,
|
||||
sharedFromGallery: true,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class RegisterView extends StatefulWidget {
|
|||
});
|
||||
|
||||
final Function callbackOnSuccess;
|
||||
final Future<int>? proofOfWork;
|
||||
final (Future<int>?, bool) proofOfWork;
|
||||
@override
|
||||
State<RegisterView> createState() => _RegisterViewState();
|
||||
}
|
||||
|
|
@ -36,10 +36,20 @@ class _RegisterViewState extends State<RegisterView> {
|
|||
final TextEditingController usernameController = TextEditingController();
|
||||
final TextEditingController inviteCodeController = TextEditingController();
|
||||
|
||||
bool _registrationDisabled = false;
|
||||
bool _isTryingToRegister = false;
|
||||
bool _isValidUserName = false;
|
||||
bool _showUserNameError = false;
|
||||
|
||||
late Future<int>? proofOfWork;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
proofOfWork = widget.proofOfWork.$1;
|
||||
_registrationDisabled = widget.proofOfWork.$2;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
Future<void> createNewUser() async {
|
||||
if (!_isValidUserName) {
|
||||
setState(() {
|
||||
|
|
@ -57,11 +67,12 @@ class _RegisterViewState extends State<RegisterView> {
|
|||
|
||||
late int proof;
|
||||
|
||||
if (widget.proofOfWork != null) {
|
||||
proof = await widget.proofOfWork!;
|
||||
if (proofOfWork != null) {
|
||||
proof = await proofOfWork!;
|
||||
} else {
|
||||
final pow = await apiService.getProofOfWork();
|
||||
final (pow, registrationDisabled) = await apiService.getProofOfWork();
|
||||
if (pow == null) {
|
||||
_registrationDisabled = registrationDisabled;
|
||||
if (mounted) {
|
||||
showNetworkIssue(context);
|
||||
}
|
||||
|
|
@ -82,6 +93,10 @@ class _RegisterViewState extends State<RegisterView> {
|
|||
Log.info('Got user_id ${res.value} from server');
|
||||
userId = res.value.userid.toInt() as int;
|
||||
} else {
|
||||
if (res.error == ErrorCode.RegistrationDisabled) {
|
||||
_registrationDisabled = true;
|
||||
return;
|
||||
}
|
||||
if (res.error == ErrorCode.UserIdAlreadyTaken) {
|
||||
Log.error('User ID already token. Tying again.');
|
||||
await deleteLocalUserData();
|
||||
|
|
@ -127,6 +142,43 @@ class _RegisterViewState extends State<RegisterView> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_registrationDisabled) {
|
||||
return Scaffold(
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 10, right: 10),
|
||||
child: ListView(
|
||||
children: [
|
||||
const SizedBox(height: 50),
|
||||
Text(
|
||||
context.lang.registerTitle,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(fontSize: 30),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 30),
|
||||
child: Text(
|
||||
context.lang.registerSlogan,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(fontSize: 12),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 130),
|
||||
Text(
|
||||
context.lang.registrationClosed,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
color: Colors.red,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
InputDecoration getInputDecoration(String hintText) {
|
||||
return InputDecoration(hintText: hintText, fillColor: Colors.grey[400]);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue