mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 09:28:41 +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 _showOnboarding = true;
|
||||||
bool _isLoaded = false;
|
bool _isLoaded = false;
|
||||||
|
|
||||||
Future<int>? _proofOfWork;
|
(Future<int>?, bool) _proofOfWork = (null, false);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
|
@ -176,11 +176,14 @@ class _AppMainWidgetState extends State<AppMainWidget> {
|
||||||
if (!_isUserCreated && !_showDatabaseMigration) {
|
if (!_isUserCreated && !_showDatabaseMigration) {
|
||||||
// This means the user is in the onboarding screen, so start with the Proof of Work.
|
// 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) {
|
if (proof != null) {
|
||||||
Log.info('Starting with proof of work calculation.');
|
Log.info('Starting with proof of work calculation.');
|
||||||
// Starting with the proof of work.
|
// 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);
|
.write(updates);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> updateAllMediaFiles(
|
||||||
|
MediaFilesCompanion updates,
|
||||||
|
) async {
|
||||||
|
await update(mediaFiles).write(updates);
|
||||||
|
}
|
||||||
|
|
||||||
Future<MediaFile?> getMediaFileById(String mediaId) async {
|
Future<MediaFile?> getMediaFileById(String mediaId) async {
|
||||||
return (select(mediaFiles)..where((t) => t.mediaId.equals(mediaId)))
|
return (select(mediaFiles)..where((t) => t.mediaId.equals(mediaId)))
|
||||||
.getSingleOrNull();
|
.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) {
|
Stream<MediaFile?> watchMedia(String mediaId) {
|
||||||
return (select(mediaFiles)..where((t) => t.mediaId.equals(mediaId)))
|
return (select(mediaFiles)..where((t) => t.mediaId.equals(mediaId)))
|
||||||
.watchSingleOrNull();
|
.watchSingleOrNull();
|
||||||
|
|
@ -87,10 +103,9 @@ class MediaFilesDao extends DatabaseAccessor<TwonlyDB>
|
||||||
Future<List<MediaFile>> getAllMediaFilesPendingUpload() async {
|
Future<List<MediaFile>> getAllMediaFilesPendingUpload() async {
|
||||||
return (select(mediaFiles)
|
return (select(mediaFiles)
|
||||||
..where(
|
..where(
|
||||||
(t) =>
|
(t) => (t.uploadState.equals(UploadState.initialized.name) |
|
||||||
t.uploadState.equals(UploadState.initialized.name) |
|
|
||||||
t.uploadState.equals(UploadState.uploadLimitReached.name) |
|
t.uploadState.equals(UploadState.uploadLimitReached.name) |
|
||||||
t.uploadState.equals(UploadState.preprocessing.name),
|
t.uploadState.equals(UploadState.preprocessing.name)),
|
||||||
))
|
))
|
||||||
.get();
|
.get();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,10 +44,12 @@ class MediaFiles extends Table {
|
||||||
|
|
||||||
BoolColumn get requiresAuthentication =>
|
BoolColumn get requiresAuthentication =>
|
||||||
boolean().withDefault(const Constant(false))();
|
boolean().withDefault(const Constant(false))();
|
||||||
|
|
||||||
BoolColumn get reopenByContact =>
|
BoolColumn get reopenByContact =>
|
||||||
boolean().withDefault(const Constant(false))();
|
boolean().withDefault(const Constant(false))();
|
||||||
|
|
||||||
BoolColumn get stored => boolean().withDefault(const Constant(false))();
|
BoolColumn get stored => boolean().withDefault(const Constant(false))();
|
||||||
|
BoolColumn get isDraftMedia => boolean().withDefault(const Constant(false))();
|
||||||
|
|
||||||
TextColumn get reuploadRequestedBy =>
|
TextColumn get reuploadRequestedBy =>
|
||||||
text().map(IntListTypeConverter()).nullable()();
|
text().map(IntListTypeConverter()).nullable()();
|
||||||
|
|
|
||||||
|
|
@ -1905,6 +1905,16 @@ class $MediaFilesTable extends MediaFiles
|
||||||
defaultConstraints:
|
defaultConstraints:
|
||||||
GeneratedColumn.constraintIsAlways('CHECK ("stored" IN (0, 1))'),
|
GeneratedColumn.constraintIsAlways('CHECK ("stored" IN (0, 1))'),
|
||||||
defaultValue: const Constant(false));
|
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
|
@override
|
||||||
late final GeneratedColumnWithTypeConverter<List<int>?, String>
|
late final GeneratedColumnWithTypeConverter<List<int>?, String>
|
||||||
reuploadRequestedBy = GeneratedColumn<String>(
|
reuploadRequestedBy = GeneratedColumn<String>(
|
||||||
|
|
@ -1968,6 +1978,7 @@ class $MediaFilesTable extends MediaFiles
|
||||||
requiresAuthentication,
|
requiresAuthentication,
|
||||||
reopenByContact,
|
reopenByContact,
|
||||||
stored,
|
stored,
|
||||||
|
isDraftMedia,
|
||||||
reuploadRequestedBy,
|
reuploadRequestedBy,
|
||||||
displayLimitInMilliseconds,
|
displayLimitInMilliseconds,
|
||||||
removeAudio,
|
removeAudio,
|
||||||
|
|
@ -2009,6 +2020,12 @@ class $MediaFilesTable extends MediaFiles
|
||||||
context.handle(_storedMeta,
|
context.handle(_storedMeta,
|
||||||
stored.isAcceptableOrUnknown(data['stored']!, _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')) {
|
if (data.containsKey('display_limit_in_milliseconds')) {
|
||||||
context.handle(
|
context.handle(
|
||||||
_displayLimitInMillisecondsMeta,
|
_displayLimitInMillisecondsMeta,
|
||||||
|
|
@ -2076,6 +2093,8 @@ class $MediaFilesTable extends MediaFiles
|
||||||
DriftSqlType.bool, data['${effectivePrefix}reopen_by_contact'])!,
|
DriftSqlType.bool, data['${effectivePrefix}reopen_by_contact'])!,
|
||||||
stored: attachedDatabase.typeMapping
|
stored: attachedDatabase.typeMapping
|
||||||
.read(DriftSqlType.bool, data['${effectivePrefix}stored'])!,
|
.read(DriftSqlType.bool, data['${effectivePrefix}stored'])!,
|
||||||
|
isDraftMedia: attachedDatabase.typeMapping
|
||||||
|
.read(DriftSqlType.bool, data['${effectivePrefix}is_draft_media'])!,
|
||||||
reuploadRequestedBy: $MediaFilesTable.$converterreuploadRequestedByn
|
reuploadRequestedBy: $MediaFilesTable.$converterreuploadRequestedByn
|
||||||
.fromSql(attachedDatabase.typeMapping.read(DriftSqlType.string,
|
.fromSql(attachedDatabase.typeMapping.read(DriftSqlType.string,
|
||||||
data['${effectivePrefix}reupload_requested_by'])),
|
data['${effectivePrefix}reupload_requested_by'])),
|
||||||
|
|
@ -2129,6 +2148,7 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
||||||
final bool requiresAuthentication;
|
final bool requiresAuthentication;
|
||||||
final bool reopenByContact;
|
final bool reopenByContact;
|
||||||
final bool stored;
|
final bool stored;
|
||||||
|
final bool isDraftMedia;
|
||||||
final List<int>? reuploadRequestedBy;
|
final List<int>? reuploadRequestedBy;
|
||||||
final int? displayLimitInMilliseconds;
|
final int? displayLimitInMilliseconds;
|
||||||
final bool? removeAudio;
|
final bool? removeAudio;
|
||||||
|
|
@ -2145,6 +2165,7 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
||||||
required this.requiresAuthentication,
|
required this.requiresAuthentication,
|
||||||
required this.reopenByContact,
|
required this.reopenByContact,
|
||||||
required this.stored,
|
required this.stored,
|
||||||
|
required this.isDraftMedia,
|
||||||
this.reuploadRequestedBy,
|
this.reuploadRequestedBy,
|
||||||
this.displayLimitInMilliseconds,
|
this.displayLimitInMilliseconds,
|
||||||
this.removeAudio,
|
this.removeAudio,
|
||||||
|
|
@ -2172,6 +2193,7 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
||||||
map['requires_authentication'] = Variable<bool>(requiresAuthentication);
|
map['requires_authentication'] = Variable<bool>(requiresAuthentication);
|
||||||
map['reopen_by_contact'] = Variable<bool>(reopenByContact);
|
map['reopen_by_contact'] = Variable<bool>(reopenByContact);
|
||||||
map['stored'] = Variable<bool>(stored);
|
map['stored'] = Variable<bool>(stored);
|
||||||
|
map['is_draft_media'] = Variable<bool>(isDraftMedia);
|
||||||
if (!nullToAbsent || reuploadRequestedBy != null) {
|
if (!nullToAbsent || reuploadRequestedBy != null) {
|
||||||
map['reupload_requested_by'] = Variable<String>($MediaFilesTable
|
map['reupload_requested_by'] = Variable<String>($MediaFilesTable
|
||||||
.$converterreuploadRequestedByn
|
.$converterreuploadRequestedByn
|
||||||
|
|
@ -2213,6 +2235,7 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
||||||
requiresAuthentication: Value(requiresAuthentication),
|
requiresAuthentication: Value(requiresAuthentication),
|
||||||
reopenByContact: Value(reopenByContact),
|
reopenByContact: Value(reopenByContact),
|
||||||
stored: Value(stored),
|
stored: Value(stored),
|
||||||
|
isDraftMedia: Value(isDraftMedia),
|
||||||
reuploadRequestedBy: reuploadRequestedBy == null && nullToAbsent
|
reuploadRequestedBy: reuploadRequestedBy == null && nullToAbsent
|
||||||
? const Value.absent()
|
? const Value.absent()
|
||||||
: Value(reuploadRequestedBy),
|
: Value(reuploadRequestedBy),
|
||||||
|
|
@ -2254,6 +2277,7 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
||||||
serializer.fromJson<bool>(json['requiresAuthentication']),
|
serializer.fromJson<bool>(json['requiresAuthentication']),
|
||||||
reopenByContact: serializer.fromJson<bool>(json['reopenByContact']),
|
reopenByContact: serializer.fromJson<bool>(json['reopenByContact']),
|
||||||
stored: serializer.fromJson<bool>(json['stored']),
|
stored: serializer.fromJson<bool>(json['stored']),
|
||||||
|
isDraftMedia: serializer.fromJson<bool>(json['isDraftMedia']),
|
||||||
reuploadRequestedBy:
|
reuploadRequestedBy:
|
||||||
serializer.fromJson<List<int>?>(json['reuploadRequestedBy']),
|
serializer.fromJson<List<int>?>(json['reuploadRequestedBy']),
|
||||||
displayLimitInMilliseconds:
|
displayLimitInMilliseconds:
|
||||||
|
|
@ -2280,6 +2304,7 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
||||||
'requiresAuthentication': serializer.toJson<bool>(requiresAuthentication),
|
'requiresAuthentication': serializer.toJson<bool>(requiresAuthentication),
|
||||||
'reopenByContact': serializer.toJson<bool>(reopenByContact),
|
'reopenByContact': serializer.toJson<bool>(reopenByContact),
|
||||||
'stored': serializer.toJson<bool>(stored),
|
'stored': serializer.toJson<bool>(stored),
|
||||||
|
'isDraftMedia': serializer.toJson<bool>(isDraftMedia),
|
||||||
'reuploadRequestedBy': serializer.toJson<List<int>?>(reuploadRequestedBy),
|
'reuploadRequestedBy': serializer.toJson<List<int>?>(reuploadRequestedBy),
|
||||||
'displayLimitInMilliseconds':
|
'displayLimitInMilliseconds':
|
||||||
serializer.toJson<int?>(displayLimitInMilliseconds),
|
serializer.toJson<int?>(displayLimitInMilliseconds),
|
||||||
|
|
@ -2300,6 +2325,7 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
||||||
bool? requiresAuthentication,
|
bool? requiresAuthentication,
|
||||||
bool? reopenByContact,
|
bool? reopenByContact,
|
||||||
bool? stored,
|
bool? stored,
|
||||||
|
bool? isDraftMedia,
|
||||||
Value<List<int>?> reuploadRequestedBy = const Value.absent(),
|
Value<List<int>?> reuploadRequestedBy = const Value.absent(),
|
||||||
Value<int?> displayLimitInMilliseconds = const Value.absent(),
|
Value<int?> displayLimitInMilliseconds = const Value.absent(),
|
||||||
Value<bool?> removeAudio = const Value.absent(),
|
Value<bool?> removeAudio = const Value.absent(),
|
||||||
|
|
@ -2318,6 +2344,7 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
||||||
requiresAuthentication ?? this.requiresAuthentication,
|
requiresAuthentication ?? this.requiresAuthentication,
|
||||||
reopenByContact: reopenByContact ?? this.reopenByContact,
|
reopenByContact: reopenByContact ?? this.reopenByContact,
|
||||||
stored: stored ?? this.stored,
|
stored: stored ?? this.stored,
|
||||||
|
isDraftMedia: isDraftMedia ?? this.isDraftMedia,
|
||||||
reuploadRequestedBy: reuploadRequestedBy.present
|
reuploadRequestedBy: reuploadRequestedBy.present
|
||||||
? reuploadRequestedBy.value
|
? reuploadRequestedBy.value
|
||||||
: this.reuploadRequestedBy,
|
: this.reuploadRequestedBy,
|
||||||
|
|
@ -2352,6 +2379,9 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
||||||
? data.reopenByContact.value
|
? data.reopenByContact.value
|
||||||
: this.reopenByContact,
|
: this.reopenByContact,
|
||||||
stored: data.stored.present ? data.stored.value : this.stored,
|
stored: data.stored.present ? data.stored.value : this.stored,
|
||||||
|
isDraftMedia: data.isDraftMedia.present
|
||||||
|
? data.isDraftMedia.value
|
||||||
|
: this.isDraftMedia,
|
||||||
reuploadRequestedBy: data.reuploadRequestedBy.present
|
reuploadRequestedBy: data.reuploadRequestedBy.present
|
||||||
? data.reuploadRequestedBy.value
|
? data.reuploadRequestedBy.value
|
||||||
: this.reuploadRequestedBy,
|
: this.reuploadRequestedBy,
|
||||||
|
|
@ -2386,6 +2416,7 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
||||||
..write('requiresAuthentication: $requiresAuthentication, ')
|
..write('requiresAuthentication: $requiresAuthentication, ')
|
||||||
..write('reopenByContact: $reopenByContact, ')
|
..write('reopenByContact: $reopenByContact, ')
|
||||||
..write('stored: $stored, ')
|
..write('stored: $stored, ')
|
||||||
|
..write('isDraftMedia: $isDraftMedia, ')
|
||||||
..write('reuploadRequestedBy: $reuploadRequestedBy, ')
|
..write('reuploadRequestedBy: $reuploadRequestedBy, ')
|
||||||
..write('displayLimitInMilliseconds: $displayLimitInMilliseconds, ')
|
..write('displayLimitInMilliseconds: $displayLimitInMilliseconds, ')
|
||||||
..write('removeAudio: $removeAudio, ')
|
..write('removeAudio: $removeAudio, ')
|
||||||
|
|
@ -2407,6 +2438,7 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
||||||
requiresAuthentication,
|
requiresAuthentication,
|
||||||
reopenByContact,
|
reopenByContact,
|
||||||
stored,
|
stored,
|
||||||
|
isDraftMedia,
|
||||||
reuploadRequestedBy,
|
reuploadRequestedBy,
|
||||||
displayLimitInMilliseconds,
|
displayLimitInMilliseconds,
|
||||||
removeAudio,
|
removeAudio,
|
||||||
|
|
@ -2426,6 +2458,7 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
|
||||||
other.requiresAuthentication == this.requiresAuthentication &&
|
other.requiresAuthentication == this.requiresAuthentication &&
|
||||||
other.reopenByContact == this.reopenByContact &&
|
other.reopenByContact == this.reopenByContact &&
|
||||||
other.stored == this.stored &&
|
other.stored == this.stored &&
|
||||||
|
other.isDraftMedia == this.isDraftMedia &&
|
||||||
other.reuploadRequestedBy == this.reuploadRequestedBy &&
|
other.reuploadRequestedBy == this.reuploadRequestedBy &&
|
||||||
other.displayLimitInMilliseconds == this.displayLimitInMilliseconds &&
|
other.displayLimitInMilliseconds == this.displayLimitInMilliseconds &&
|
||||||
other.removeAudio == this.removeAudio &&
|
other.removeAudio == this.removeAudio &&
|
||||||
|
|
@ -2445,6 +2478,7 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
|
||||||
final Value<bool> requiresAuthentication;
|
final Value<bool> requiresAuthentication;
|
||||||
final Value<bool> reopenByContact;
|
final Value<bool> reopenByContact;
|
||||||
final Value<bool> stored;
|
final Value<bool> stored;
|
||||||
|
final Value<bool> isDraftMedia;
|
||||||
final Value<List<int>?> reuploadRequestedBy;
|
final Value<List<int>?> reuploadRequestedBy;
|
||||||
final Value<int?> displayLimitInMilliseconds;
|
final Value<int?> displayLimitInMilliseconds;
|
||||||
final Value<bool?> removeAudio;
|
final Value<bool?> removeAudio;
|
||||||
|
|
@ -2462,6 +2496,7 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
|
||||||
this.requiresAuthentication = const Value.absent(),
|
this.requiresAuthentication = const Value.absent(),
|
||||||
this.reopenByContact = const Value.absent(),
|
this.reopenByContact = const Value.absent(),
|
||||||
this.stored = const Value.absent(),
|
this.stored = const Value.absent(),
|
||||||
|
this.isDraftMedia = const Value.absent(),
|
||||||
this.reuploadRequestedBy = const Value.absent(),
|
this.reuploadRequestedBy = const Value.absent(),
|
||||||
this.displayLimitInMilliseconds = const Value.absent(),
|
this.displayLimitInMilliseconds = const Value.absent(),
|
||||||
this.removeAudio = const Value.absent(),
|
this.removeAudio = const Value.absent(),
|
||||||
|
|
@ -2480,6 +2515,7 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
|
||||||
this.requiresAuthentication = const Value.absent(),
|
this.requiresAuthentication = const Value.absent(),
|
||||||
this.reopenByContact = const Value.absent(),
|
this.reopenByContact = const Value.absent(),
|
||||||
this.stored = const Value.absent(),
|
this.stored = const Value.absent(),
|
||||||
|
this.isDraftMedia = const Value.absent(),
|
||||||
this.reuploadRequestedBy = const Value.absent(),
|
this.reuploadRequestedBy = const Value.absent(),
|
||||||
this.displayLimitInMilliseconds = const Value.absent(),
|
this.displayLimitInMilliseconds = const Value.absent(),
|
||||||
this.removeAudio = const Value.absent(),
|
this.removeAudio = const Value.absent(),
|
||||||
|
|
@ -2499,6 +2535,7 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
|
||||||
Expression<bool>? requiresAuthentication,
|
Expression<bool>? requiresAuthentication,
|
||||||
Expression<bool>? reopenByContact,
|
Expression<bool>? reopenByContact,
|
||||||
Expression<bool>? stored,
|
Expression<bool>? stored,
|
||||||
|
Expression<bool>? isDraftMedia,
|
||||||
Expression<String>? reuploadRequestedBy,
|
Expression<String>? reuploadRequestedBy,
|
||||||
Expression<int>? displayLimitInMilliseconds,
|
Expression<int>? displayLimitInMilliseconds,
|
||||||
Expression<bool>? removeAudio,
|
Expression<bool>? removeAudio,
|
||||||
|
|
@ -2518,6 +2555,7 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
|
||||||
'requires_authentication': requiresAuthentication,
|
'requires_authentication': requiresAuthentication,
|
||||||
if (reopenByContact != null) 'reopen_by_contact': reopenByContact,
|
if (reopenByContact != null) 'reopen_by_contact': reopenByContact,
|
||||||
if (stored != null) 'stored': stored,
|
if (stored != null) 'stored': stored,
|
||||||
|
if (isDraftMedia != null) 'is_draft_media': isDraftMedia,
|
||||||
if (reuploadRequestedBy != null)
|
if (reuploadRequestedBy != null)
|
||||||
'reupload_requested_by': reuploadRequestedBy,
|
'reupload_requested_by': reuploadRequestedBy,
|
||||||
if (displayLimitInMilliseconds != null)
|
if (displayLimitInMilliseconds != null)
|
||||||
|
|
@ -2540,6 +2578,7 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
|
||||||
Value<bool>? requiresAuthentication,
|
Value<bool>? requiresAuthentication,
|
||||||
Value<bool>? reopenByContact,
|
Value<bool>? reopenByContact,
|
||||||
Value<bool>? stored,
|
Value<bool>? stored,
|
||||||
|
Value<bool>? isDraftMedia,
|
||||||
Value<List<int>?>? reuploadRequestedBy,
|
Value<List<int>?>? reuploadRequestedBy,
|
||||||
Value<int?>? displayLimitInMilliseconds,
|
Value<int?>? displayLimitInMilliseconds,
|
||||||
Value<bool?>? removeAudio,
|
Value<bool?>? removeAudio,
|
||||||
|
|
@ -2558,6 +2597,7 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
|
||||||
requiresAuthentication ?? this.requiresAuthentication,
|
requiresAuthentication ?? this.requiresAuthentication,
|
||||||
reopenByContact: reopenByContact ?? this.reopenByContact,
|
reopenByContact: reopenByContact ?? this.reopenByContact,
|
||||||
stored: stored ?? this.stored,
|
stored: stored ?? this.stored,
|
||||||
|
isDraftMedia: isDraftMedia ?? this.isDraftMedia,
|
||||||
reuploadRequestedBy: reuploadRequestedBy ?? this.reuploadRequestedBy,
|
reuploadRequestedBy: reuploadRequestedBy ?? this.reuploadRequestedBy,
|
||||||
displayLimitInMilliseconds:
|
displayLimitInMilliseconds:
|
||||||
displayLimitInMilliseconds ?? this.displayLimitInMilliseconds,
|
displayLimitInMilliseconds ?? this.displayLimitInMilliseconds,
|
||||||
|
|
@ -2599,6 +2639,9 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
|
||||||
if (stored.present) {
|
if (stored.present) {
|
||||||
map['stored'] = Variable<bool>(stored.value);
|
map['stored'] = Variable<bool>(stored.value);
|
||||||
}
|
}
|
||||||
|
if (isDraftMedia.present) {
|
||||||
|
map['is_draft_media'] = Variable<bool>(isDraftMedia.value);
|
||||||
|
}
|
||||||
if (reuploadRequestedBy.present) {
|
if (reuploadRequestedBy.present) {
|
||||||
map['reupload_requested_by'] = Variable<String>($MediaFilesTable
|
map['reupload_requested_by'] = Variable<String>($MediaFilesTable
|
||||||
.$converterreuploadRequestedByn
|
.$converterreuploadRequestedByn
|
||||||
|
|
@ -2642,6 +2685,7 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
|
||||||
..write('requiresAuthentication: $requiresAuthentication, ')
|
..write('requiresAuthentication: $requiresAuthentication, ')
|
||||||
..write('reopenByContact: $reopenByContact, ')
|
..write('reopenByContact: $reopenByContact, ')
|
||||||
..write('stored: $stored, ')
|
..write('stored: $stored, ')
|
||||||
|
..write('isDraftMedia: $isDraftMedia, ')
|
||||||
..write('reuploadRequestedBy: $reuploadRequestedBy, ')
|
..write('reuploadRequestedBy: $reuploadRequestedBy, ')
|
||||||
..write('displayLimitInMilliseconds: $displayLimitInMilliseconds, ')
|
..write('displayLimitInMilliseconds: $displayLimitInMilliseconds, ')
|
||||||
..write('removeAudio: $removeAudio, ')
|
..write('removeAudio: $removeAudio, ')
|
||||||
|
|
@ -9161,6 +9205,7 @@ typedef $$MediaFilesTableCreateCompanionBuilder = MediaFilesCompanion Function({
|
||||||
Value<bool> requiresAuthentication,
|
Value<bool> requiresAuthentication,
|
||||||
Value<bool> reopenByContact,
|
Value<bool> reopenByContact,
|
||||||
Value<bool> stored,
|
Value<bool> stored,
|
||||||
|
Value<bool> isDraftMedia,
|
||||||
Value<List<int>?> reuploadRequestedBy,
|
Value<List<int>?> reuploadRequestedBy,
|
||||||
Value<int?> displayLimitInMilliseconds,
|
Value<int?> displayLimitInMilliseconds,
|
||||||
Value<bool?> removeAudio,
|
Value<bool?> removeAudio,
|
||||||
|
|
@ -9179,6 +9224,7 @@ typedef $$MediaFilesTableUpdateCompanionBuilder = MediaFilesCompanion Function({
|
||||||
Value<bool> requiresAuthentication,
|
Value<bool> requiresAuthentication,
|
||||||
Value<bool> reopenByContact,
|
Value<bool> reopenByContact,
|
||||||
Value<bool> stored,
|
Value<bool> stored,
|
||||||
|
Value<bool> isDraftMedia,
|
||||||
Value<List<int>?> reuploadRequestedBy,
|
Value<List<int>?> reuploadRequestedBy,
|
||||||
Value<int?> displayLimitInMilliseconds,
|
Value<int?> displayLimitInMilliseconds,
|
||||||
Value<bool?> removeAudio,
|
Value<bool?> removeAudio,
|
||||||
|
|
@ -9248,6 +9294,9 @@ class $$MediaFilesTableFilterComposer
|
||||||
ColumnFilters<bool> get stored => $composableBuilder(
|
ColumnFilters<bool> get stored => $composableBuilder(
|
||||||
column: $table.stored, builder: (column) => ColumnFilters(column));
|
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>
|
ColumnWithTypeConverterFilters<List<int>?, List<int>, String>
|
||||||
get reuploadRequestedBy => $composableBuilder(
|
get reuploadRequestedBy => $composableBuilder(
|
||||||
column: $table.reuploadRequestedBy,
|
column: $table.reuploadRequestedBy,
|
||||||
|
|
@ -9331,6 +9380,10 @@ class $$MediaFilesTableOrderingComposer
|
||||||
ColumnOrderings<bool> get stored => $composableBuilder(
|
ColumnOrderings<bool> get stored => $composableBuilder(
|
||||||
column: $table.stored, builder: (column) => ColumnOrderings(column));
|
column: $table.stored, builder: (column) => ColumnOrderings(column));
|
||||||
|
|
||||||
|
ColumnOrderings<bool> get isDraftMedia => $composableBuilder(
|
||||||
|
column: $table.isDraftMedia,
|
||||||
|
builder: (column) => ColumnOrderings(column));
|
||||||
|
|
||||||
ColumnOrderings<String> get reuploadRequestedBy => $composableBuilder(
|
ColumnOrderings<String> get reuploadRequestedBy => $composableBuilder(
|
||||||
column: $table.reuploadRequestedBy,
|
column: $table.reuploadRequestedBy,
|
||||||
builder: (column) => ColumnOrderings(column));
|
builder: (column) => ColumnOrderings(column));
|
||||||
|
|
@ -9394,6 +9447,9 @@ class $$MediaFilesTableAnnotationComposer
|
||||||
GeneratedColumn<bool> get stored =>
|
GeneratedColumn<bool> get stored =>
|
||||||
$composableBuilder(column: $table.stored, builder: (column) => column);
|
$composableBuilder(column: $table.stored, builder: (column) => column);
|
||||||
|
|
||||||
|
GeneratedColumn<bool> get isDraftMedia => $composableBuilder(
|
||||||
|
column: $table.isDraftMedia, builder: (column) => column);
|
||||||
|
|
||||||
GeneratedColumnWithTypeConverter<List<int>?, String>
|
GeneratedColumnWithTypeConverter<List<int>?, String>
|
||||||
get reuploadRequestedBy => $composableBuilder(
|
get reuploadRequestedBy => $composableBuilder(
|
||||||
column: $table.reuploadRequestedBy, builder: (column) => column);
|
column: $table.reuploadRequestedBy, builder: (column) => column);
|
||||||
|
|
@ -9471,6 +9527,7 @@ class $$MediaFilesTableTableManager extends RootTableManager<
|
||||||
Value<bool> requiresAuthentication = const Value.absent(),
|
Value<bool> requiresAuthentication = const Value.absent(),
|
||||||
Value<bool> reopenByContact = const Value.absent(),
|
Value<bool> reopenByContact = const Value.absent(),
|
||||||
Value<bool> stored = const Value.absent(),
|
Value<bool> stored = const Value.absent(),
|
||||||
|
Value<bool> isDraftMedia = const Value.absent(),
|
||||||
Value<List<int>?> reuploadRequestedBy = const Value.absent(),
|
Value<List<int>?> reuploadRequestedBy = const Value.absent(),
|
||||||
Value<int?> displayLimitInMilliseconds = const Value.absent(),
|
Value<int?> displayLimitInMilliseconds = const Value.absent(),
|
||||||
Value<bool?> removeAudio = const Value.absent(),
|
Value<bool?> removeAudio = const Value.absent(),
|
||||||
|
|
@ -9489,6 +9546,7 @@ class $$MediaFilesTableTableManager extends RootTableManager<
|
||||||
requiresAuthentication: requiresAuthentication,
|
requiresAuthentication: requiresAuthentication,
|
||||||
reopenByContact: reopenByContact,
|
reopenByContact: reopenByContact,
|
||||||
stored: stored,
|
stored: stored,
|
||||||
|
isDraftMedia: isDraftMedia,
|
||||||
reuploadRequestedBy: reuploadRequestedBy,
|
reuploadRequestedBy: reuploadRequestedBy,
|
||||||
displayLimitInMilliseconds: displayLimitInMilliseconds,
|
displayLimitInMilliseconds: displayLimitInMilliseconds,
|
||||||
removeAudio: removeAudio,
|
removeAudio: removeAudio,
|
||||||
|
|
@ -9507,6 +9565,7 @@ class $$MediaFilesTableTableManager extends RootTableManager<
|
||||||
Value<bool> requiresAuthentication = const Value.absent(),
|
Value<bool> requiresAuthentication = const Value.absent(),
|
||||||
Value<bool> reopenByContact = const Value.absent(),
|
Value<bool> reopenByContact = const Value.absent(),
|
||||||
Value<bool> stored = const Value.absent(),
|
Value<bool> stored = const Value.absent(),
|
||||||
|
Value<bool> isDraftMedia = const Value.absent(),
|
||||||
Value<List<int>?> reuploadRequestedBy = const Value.absent(),
|
Value<List<int>?> reuploadRequestedBy = const Value.absent(),
|
||||||
Value<int?> displayLimitInMilliseconds = const Value.absent(),
|
Value<int?> displayLimitInMilliseconds = const Value.absent(),
|
||||||
Value<bool?> removeAudio = const Value.absent(),
|
Value<bool?> removeAudio = const Value.absent(),
|
||||||
|
|
@ -9525,6 +9584,7 @@ class $$MediaFilesTableTableManager extends RootTableManager<
|
||||||
requiresAuthentication: requiresAuthentication,
|
requiresAuthentication: requiresAuthentication,
|
||||||
reopenByContact: reopenByContact,
|
reopenByContact: reopenByContact,
|
||||||
stored: stored,
|
stored: stored,
|
||||||
|
isDraftMedia: isDraftMedia,
|
||||||
reuploadRequestedBy: reuploadRequestedBy,
|
reuploadRequestedBy: reuploadRequestedBy,
|
||||||
displayLimitInMilliseconds: displayLimitInMilliseconds,
|
displayLimitInMilliseconds: displayLimitInMilliseconds,
|
||||||
removeAudio: removeAudio,
|
removeAudio: removeAudio,
|
||||||
|
|
|
||||||
|
|
@ -817,5 +817,6 @@
|
||||||
"deleteChatAfterAWeek": "einer Woche.",
|
"deleteChatAfterAWeek": "einer Woche.",
|
||||||
"deleteChatAfterAMonth": "einem Monat.",
|
"deleteChatAfterAMonth": "einem Monat.",
|
||||||
"deleteChatAfterAYear": "einem Jahr.",
|
"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.",
|
"deleteChatAfterAWeek": "one week.",
|
||||||
"deleteChatAfterAMonth": "one month.",
|
"deleteChatAfterAMonth": "one month.",
|
||||||
"deleteChatAfterAYear": "one year.",
|
"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:
|
/// In en, this message translates to:
|
||||||
/// **'Your twonly-Score'**
|
/// **'Your twonly-Score'**
|
||||||
String get yourTwonlyScore;
|
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
|
class _AppLocalizationsDelegate
|
||||||
|
|
|
||||||
|
|
@ -1477,4 +1477,8 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get yourTwonlyScore => 'Dein twonly-Score';
|
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
|
@override
|
||||||
String get yourTwonlyScore => 'Your twonly-Score';
|
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.
|
/// errors or network changes.
|
||||||
class ApiService {
|
class ApiService {
|
||||||
ApiService();
|
ApiService();
|
||||||
final String apiHost = kReleaseMode ? 'api.twonly.eu' : '10.99.0.140:3030';
|
// final String apiHost = kReleaseMode ? 'api.twonly.eu' : '10.99.0.140:3030';
|
||||||
final String apiSecure = kReleaseMode ? 's' : '';
|
final String apiHost = kReleaseMode ? 'api.twonly.eu' : 'dev.twonly.eu';
|
||||||
|
final String apiSecure = kReleaseMode ? 's' : 's';
|
||||||
|
|
||||||
bool appIsOutdated = false;
|
bool appIsOutdated = false;
|
||||||
bool isAuthenticated = false;
|
bool isAuthenticated = false;
|
||||||
|
|
@ -508,15 +509,19 @@ class ApiService {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Response_ProofOfWork?> getProofOfWork() async {
|
Future<(Response_ProofOfWork?, bool)> getProofOfWork() async {
|
||||||
final handshake = Handshake()..requestPOW = Handshake_RequestPOW();
|
final handshake = Handshake()..requestPOW = Handshake_RequestPOW();
|
||||||
final req = createClientToServerFromHandshake(handshake);
|
final req = createClientToServerFromHandshake(handshake);
|
||||||
final result = await sendRequestSync(req, authenticated: false);
|
final result = await sendRequestSync(req, authenticated: false);
|
||||||
if (result.isError) {
|
if (result.isError) {
|
||||||
Log.error('could not request proof of work params', result);
|
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 {
|
Future<Result> downloadDone(List<int> token) async {
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,24 @@ Future<void> finishStartedPreprocessing() async {
|
||||||
await twonlyDB.mediaFilesDao.getAllMediaFilesPendingUpload();
|
await twonlyDB.mediaFilesDao.getAllMediaFilesPendingUpload();
|
||||||
|
|
||||||
for (final mediaFile in mediaFiles) {
|
for (final mediaFile in mediaFiles) {
|
||||||
|
if (mediaFile.isDraftMedia) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
final service = await MediaFileService.fromMedia(mediaFile);
|
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);
|
await startBackgroundMediaUpload(service);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Log.error(e);
|
Log.error(e);
|
||||||
|
|
@ -35,18 +51,24 @@ Future<void> finishStartedPreprocessing() async {
|
||||||
|
|
||||||
Future<MediaFileService?> initializeMediaUpload(
|
Future<MediaFileService?> initializeMediaUpload(
|
||||||
MediaType type,
|
MediaType type,
|
||||||
int? displayLimitInMilliseconds,
|
int? displayLimitInMilliseconds, {
|
||||||
) async {
|
bool isDraftMedia = false,
|
||||||
|
}) async {
|
||||||
final chacha20 = FlutterChacha20.poly1305Aead();
|
final chacha20 = FlutterChacha20.poly1305Aead();
|
||||||
final encryptionKey = await (await chacha20.newSecretKey()).extract();
|
final encryptionKey = await (await chacha20.newSecretKey()).extract();
|
||||||
final encryptionNonce = chacha20.newNonce();
|
final encryptionNonce = chacha20.newNonce();
|
||||||
|
|
||||||
|
await twonlyDB.mediaFilesDao.updateAllMediaFiles(
|
||||||
|
const MediaFilesCompanion(isDraftMedia: Value(false)),
|
||||||
|
);
|
||||||
|
|
||||||
final mediaFile = await twonlyDB.mediaFilesDao.insertMedia(
|
final mediaFile = await twonlyDB.mediaFilesDao.insertMedia(
|
||||||
MediaFilesCompanion(
|
MediaFilesCompanion(
|
||||||
uploadState: const Value(UploadState.initialized),
|
uploadState: const Value(UploadState.initialized),
|
||||||
displayLimitInMilliseconds: Value(displayLimitInMilliseconds),
|
displayLimitInMilliseconds: Value(displayLimitInMilliseconds),
|
||||||
encryptionKey: Value(Uint8List.fromList(encryptionKey.bytes)),
|
encryptionKey: Value(Uint8List.fromList(encryptionKey.bytes)),
|
||||||
encryptionNonce: Value(Uint8List.fromList(encryptionNonce)),
|
encryptionNonce: Value(Uint8List.fromList(encryptionNonce)),
|
||||||
|
isDraftMedia: Value(isDraftMedia),
|
||||||
type: Value(type),
|
type: Value(type),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -58,6 +80,11 @@ Future<void> insertMediaFileInMessagesTable(
|
||||||
MediaFileService mediaService,
|
MediaFileService mediaService,
|
||||||
List<String> groupIds,
|
List<String> groupIds,
|
||||||
) async {
|
) async {
|
||||||
|
await twonlyDB.mediaFilesDao.updateAllMediaFiles(
|
||||||
|
const MediaFilesCompanion(
|
||||||
|
isDraftMedia: Value(false),
|
||||||
|
),
|
||||||
|
);
|
||||||
for (final groupId in groupIds) {
|
for (final groupId in groupIds) {
|
||||||
final message = await twonlyDB.messagesDao.insertMessage(
|
final message = await twonlyDB.messagesDao.insertMessage(
|
||||||
MessagesCompanion(
|
MessagesCompanion(
|
||||||
|
|
|
||||||
|
|
@ -65,20 +65,24 @@ Future<void> compressAndOverlayVideo(MediaFileService media) async {
|
||||||
if (media.tempPath.existsSync()) {
|
if (media.tempPath.existsSync()) {
|
||||||
media.tempPath.deleteSync();
|
media.tempPath.deleteSync();
|
||||||
}
|
}
|
||||||
|
if (media.ffmpegOutputPath.existsSync()) {
|
||||||
|
media.ffmpegOutputPath.deleteSync();
|
||||||
|
}
|
||||||
|
|
||||||
final stopwatch = Stopwatch()..start();
|
final stopwatch = Stopwatch()..start();
|
||||||
var command =
|
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) {
|
if (media.removeAudio) {
|
||||||
command =
|
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 session = await FFmpegKit.execute(command);
|
||||||
final returnCode = await session.getReturnCode();
|
final returnCode = await session.getReturnCode();
|
||||||
|
|
||||||
if (ReturnCode.isSuccess(returnCode)) {
|
if (ReturnCode.isSuccess(returnCode)) {
|
||||||
|
media.ffmpegOutputPath.copySync(media.tempPath.path);
|
||||||
stopwatch.stop();
|
stopwatch.stop();
|
||||||
Log.info(
|
Log.info(
|
||||||
'It took ${stopwatch.elapsedMilliseconds}ms to compress the video',
|
'It took ${stopwatch.elapsedMilliseconds}ms to compress the video',
|
||||||
|
|
|
||||||
|
|
@ -45,11 +45,16 @@ class MediaFileService {
|
||||||
var delete = true;
|
var delete = true;
|
||||||
|
|
||||||
final service = await MediaFileService.fromMediaId(mediaId);
|
final service = await MediaFileService.fromMediaId(mediaId);
|
||||||
|
|
||||||
if (service == null) {
|
if (service == null) {
|
||||||
Log.error(
|
Log.error(
|
||||||
'Purging media file, as it is not in the database $mediaId.',
|
'Purging media file, as it is not in the database $mediaId.',
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
if (service.mediaFile.isDraftMedia) {
|
||||||
|
delete = false;
|
||||||
|
}
|
||||||
|
|
||||||
final messages =
|
final messages =
|
||||||
await twonlyDB.messagesDao.getMessagesByMediaId(mediaId);
|
await twonlyDB.messagesDao.getMessagesByMediaId(mediaId);
|
||||||
|
|
||||||
|
|
@ -302,6 +307,10 @@ class MediaFileService {
|
||||||
'tmp',
|
'tmp',
|
||||||
namePrefix: '.original',
|
namePrefix: '.original',
|
||||||
);
|
);
|
||||||
|
File get ffmpegOutputPath => _buildFilePath(
|
||||||
|
'tmp',
|
||||||
|
namePrefix: '.ffmpeg',
|
||||||
|
);
|
||||||
File get overlayImagePath => _buildFilePath(
|
File get overlayImagePath => _buildFilePath(
|
||||||
'tmp',
|
'tmp',
|
||||||
namePrefix: '.overlay',
|
namePrefix: '.overlay',
|
||||||
|
|
|
||||||
|
|
@ -253,7 +253,7 @@ String getPushNotificationText(PushNotification pushNotification) {
|
||||||
PushKind.twonly.name: lang.notificationTwonly(inGroup),
|
PushKind.twonly.name: lang.notificationTwonly(inGroup),
|
||||||
PushKind.video.name: lang.notificationVideo(inGroup),
|
PushKind.video.name: lang.notificationVideo(inGroup),
|
||||||
PushKind.image.name: lang.notificationImage(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.contactRequest.name: lang.notificationContactRequest,
|
||||||
PushKind.acceptRequest.name: lang.notificationAcceptRequest,
|
PushKind.acceptRequest.name: lang.notificationAcceptRequest,
|
||||||
PushKind.storedMediaFile.name: lang.notificationStoredMediaFile,
|
PushKind.storedMediaFile.name: lang.notificationStoredMediaFile,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:camera/camera.dart';
|
import 'package:camera/camera.dart';
|
||||||
|
import 'package:drift/drift.dart' show Value;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_android_volume_keydown/flutter_android_volume_keydown.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(
|
final mediaFileService = await initializeMediaUpload(
|
||||||
type,
|
type,
|
||||||
gUser.defaultShowTime,
|
gUser.defaultShowTime,
|
||||||
|
isDraftMedia: true,
|
||||||
);
|
);
|
||||||
if (!mounted) return true;
|
if (!mounted) return true;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,13 @@
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
|
import 'package:drift/drift.dart' show Value;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:hashlib/random.dart';
|
import 'package:hashlib/random.dart';
|
||||||
import 'package:screenshot/screenshot.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/daos/contacts.dao.dart';
|
||||||
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
|
|
@ -82,6 +84,8 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
||||||
} else {
|
} else {
|
||||||
if (widget.mediaFileService.tempPath.existsSync()) {
|
if (widget.mediaFileService.tempPath.existsSync()) {
|
||||||
loadImage(widget.mediaFileService.tempPath.readAsBytes());
|
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;
|
isDisposed = true;
|
||||||
layers.clear();
|
layers.clear();
|
||||||
videoController?.dispose();
|
videoController?.dispose();
|
||||||
|
twonlyDB.mediaFilesDao.updateAllMediaFiles(
|
||||||
|
const MediaFilesCompanion(
|
||||||
|
isDraftMedia: Value(false),
|
||||||
|
),
|
||||||
|
);
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -388,6 +397,10 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
||||||
|
|
||||||
Future<void> loadImage(Future<Uint8List?> imageBytesFuture) async {
|
Future<void> loadImage(Future<Uint8List?> imageBytesFuture) async {
|
||||||
imageBytes = await imageBytesFuture;
|
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);
|
await currentImage.load(imageBytes);
|
||||||
if (isDisposed) return;
|
if (isDisposed) return;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -476,8 +476,8 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
||||||
if (videoController != null)
|
if (videoController != null)
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
child: VideoPlayer(videoController!),
|
child: VideoPlayer(videoController!),
|
||||||
),
|
)
|
||||||
if (currentMedia != null &&
|
else if (currentMedia != null &&
|
||||||
currentMedia!.mediaFile.type == MediaType.image ||
|
currentMedia!.mediaFile.type == MediaType.image ||
|
||||||
currentMedia!.mediaFile.type == MediaType.gif)
|
currentMedia!.mediaFile.type == MediaType.gif)
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,12 @@ class _FlameCounterWidgetState extends State<FlameCounterWidget> {
|
||||||
if (widget.groupId == null && widget.contactId != null) {
|
if (widget.groupId == null && widget.contactId != null) {
|
||||||
final group = await twonlyDB.groupsDao.getDirectChat(widget.contactId!);
|
final group = await twonlyDB.groupsDao.getDirectChat(widget.contactId!);
|
||||||
groupId = group?.groupId;
|
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) {
|
if (groupId != null) {
|
||||||
isBestFriend = gUser.myBestFriendGroupId == groupId;
|
isBestFriend = gUser.myBestFriendGroupId == groupId;
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,8 @@ class _MaxFlameListTitleState extends State<MaxFlameListTitle> {
|
||||||
_flameCounterSub = stream.listen((counter) {
|
_flameCounterSub = stream.listen((counter) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
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(
|
await twonlyDB.groupsDao.updateGroup(
|
||||||
_groupId,
|
_groupId,
|
||||||
GroupsCompanion(
|
GroupsCompanion(
|
||||||
flameCounter: Value(_directChat!.maxFlameCounter - 1),
|
flameCounter: Value(_directChat!.maxFlameCounter),
|
||||||
lastFlameCounterChange: Value(DateTime.now()),
|
lastFlameCounterChange: Value(DateTime.now()),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -84,7 +85,7 @@ class _MaxFlameListTitleState extends State<MaxFlameListTitle> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (_directChat == null ||
|
if (_directChat == null ||
|
||||||
_directChat!.maxFlameCounter == 0 ||
|
_directChat!.maxFlameCounter == 0 ||
|
||||||
_flameCounter >= (_directChat!.maxFlameCounter + 1) ||
|
_flameCounter >= _directChat!.maxFlameCounter ||
|
||||||
_directChat!.maxFlameCounterFrom!
|
_directChat!.maxFlameCounterFrom!
|
||||||
.isBefore(DateTime.now().subtract(const Duration(days: 4)))) {
|
.isBefore(DateTime.now().subtract(const Duration(days: 4)))) {
|
||||||
return Container();
|
return Container();
|
||||||
|
|
@ -97,7 +98,7 @@ class _MaxFlameListTitleState extends State<MaxFlameListTitle> {
|
||||||
emoji: '🔥',
|
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:flutter_local_notifications/flutter_local_notifications.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:screenshot/screenshot.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/services/notifications/setup.notifications.dart';
|
||||||
import 'package:twonly/src/utils/misc.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_components/camera_preview.dart';
|
||||||
import 'package:twonly/src/views/camera/camera_preview_controller_view.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/chats/chat_list.view.dart';
|
||||||
import 'package:twonly/src/views/memories/memories.view.dart';
|
import 'package:twonly/src/views/memories/memories.view.dart';
|
||||||
|
|
||||||
|
|
@ -145,6 +148,21 @@ class HomeViewState extends State<HomeView> {
|
||||||
globalUpdateOfHomeViewPageIndex(0);
|
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
|
@override
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ class RegisterView extends StatefulWidget {
|
||||||
});
|
});
|
||||||
|
|
||||||
final Function callbackOnSuccess;
|
final Function callbackOnSuccess;
|
||||||
final Future<int>? proofOfWork;
|
final (Future<int>?, bool) proofOfWork;
|
||||||
@override
|
@override
|
||||||
State<RegisterView> createState() => _RegisterViewState();
|
State<RegisterView> createState() => _RegisterViewState();
|
||||||
}
|
}
|
||||||
|
|
@ -36,10 +36,20 @@ class _RegisterViewState extends State<RegisterView> {
|
||||||
final TextEditingController usernameController = TextEditingController();
|
final TextEditingController usernameController = TextEditingController();
|
||||||
final TextEditingController inviteCodeController = TextEditingController();
|
final TextEditingController inviteCodeController = TextEditingController();
|
||||||
|
|
||||||
|
bool _registrationDisabled = false;
|
||||||
bool _isTryingToRegister = false;
|
bool _isTryingToRegister = false;
|
||||||
bool _isValidUserName = false;
|
bool _isValidUserName = false;
|
||||||
bool _showUserNameError = 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 {
|
Future<void> createNewUser() async {
|
||||||
if (!_isValidUserName) {
|
if (!_isValidUserName) {
|
||||||
setState(() {
|
setState(() {
|
||||||
|
|
@ -57,11 +67,12 @@ class _RegisterViewState extends State<RegisterView> {
|
||||||
|
|
||||||
late int proof;
|
late int proof;
|
||||||
|
|
||||||
if (widget.proofOfWork != null) {
|
if (proofOfWork != null) {
|
||||||
proof = await widget.proofOfWork!;
|
proof = await proofOfWork!;
|
||||||
} else {
|
} else {
|
||||||
final pow = await apiService.getProofOfWork();
|
final (pow, registrationDisabled) = await apiService.getProofOfWork();
|
||||||
if (pow == null) {
|
if (pow == null) {
|
||||||
|
_registrationDisabled = registrationDisabled;
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
showNetworkIssue(context);
|
showNetworkIssue(context);
|
||||||
}
|
}
|
||||||
|
|
@ -82,6 +93,10 @@ class _RegisterViewState extends State<RegisterView> {
|
||||||
Log.info('Got user_id ${res.value} from server');
|
Log.info('Got user_id ${res.value} from server');
|
||||||
userId = res.value.userid.toInt() as int;
|
userId = res.value.userid.toInt() as int;
|
||||||
} else {
|
} else {
|
||||||
|
if (res.error == ErrorCode.RegistrationDisabled) {
|
||||||
|
_registrationDisabled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (res.error == ErrorCode.UserIdAlreadyTaken) {
|
if (res.error == ErrorCode.UserIdAlreadyTaken) {
|
||||||
Log.error('User ID already token. Tying again.');
|
Log.error('User ID already token. Tying again.');
|
||||||
await deleteLocalUserData();
|
await deleteLocalUserData();
|
||||||
|
|
@ -127,6 +142,43 @@ class _RegisterViewState extends State<RegisterView> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
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) {
|
InputDecoration getInputDecoration(String hintText) {
|
||||||
return InputDecoration(hintText: hintText, fillColor: Colors.grey[400]);
|
return InputDecoration(hintText: hintText, fillColor: Colors.grey[400]);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue