✨ Basic large screen support
This commit is contained in:
parent
fceb3edbc6
commit
b39c8c770e
@ -28,32 +28,32 @@ class Account {
|
|||||||
});
|
});
|
||||||
|
|
||||||
factory Account.fromJson(Map<String, dynamic> json) => Account(
|
factory Account.fromJson(Map<String, dynamic> json) => Account(
|
||||||
id: json["id"],
|
id: json['id'],
|
||||||
createdAt: DateTime.parse(json["created_at"]),
|
createdAt: DateTime.parse(json['created_at']),
|
||||||
updatedAt: DateTime.parse(json["updated_at"]),
|
updatedAt: DateTime.parse(json['updated_at']),
|
||||||
deletedAt: json["deleted_at"],
|
deletedAt: json['deleted_at'],
|
||||||
name: json["name"],
|
name: json['name'],
|
||||||
nick: json["nick"],
|
nick: json['nick'],
|
||||||
avatar: json["avatar"],
|
avatar: json['avatar'],
|
||||||
banner: json["banner"],
|
banner: json['banner'],
|
||||||
description: json["description"],
|
description: json['description'],
|
||||||
emailAddress: json["email_address"],
|
emailAddress: json['email_address'],
|
||||||
powerLevel: json["power_level"],
|
powerLevel: json['power_level'],
|
||||||
externalId: json["external_id"],
|
externalId: json['external_id'],
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
"id": id,
|
'id': id,
|
||||||
"created_at": createdAt.toIso8601String(),
|
'created_at': createdAt.toIso8601String(),
|
||||||
"updated_at": updatedAt.toIso8601String(),
|
'updated_at': updatedAt.toIso8601String(),
|
||||||
"deleted_at": deletedAt,
|
'deleted_at': deletedAt,
|
||||||
"name": name,
|
'name': name,
|
||||||
"nick": nick,
|
'nick': nick,
|
||||||
"avatar": avatar,
|
'avatar': avatar,
|
||||||
"banner": banner,
|
'banner': banner,
|
||||||
"description": description,
|
'description': description,
|
||||||
"email_address": emailAddress,
|
'email_address': emailAddress,
|
||||||
"power_level": powerLevel,
|
'power_level': powerLevel,
|
||||||
"external_id": externalId,
|
'external_id': externalId,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -28,32 +28,32 @@ class Author {
|
|||||||
});
|
});
|
||||||
|
|
||||||
factory Author.fromJson(Map<String, dynamic> json) => Author(
|
factory Author.fromJson(Map<String, dynamic> json) => Author(
|
||||||
id: json["id"],
|
id: json['id'],
|
||||||
createdAt: DateTime.parse(json["created_at"]),
|
createdAt: DateTime.parse(json['created_at']),
|
||||||
updatedAt: DateTime.parse(json["updated_at"]),
|
updatedAt: DateTime.parse(json['updated_at']),
|
||||||
deletedAt: json["deleted_at"],
|
deletedAt: json['deleted_at'],
|
||||||
name: json["name"],
|
name: json['name'],
|
||||||
nick: json["nick"],
|
nick: json['nick'],
|
||||||
avatar: json["avatar"],
|
avatar: json['avatar'],
|
||||||
banner: json["banner"],
|
banner: json['banner'],
|
||||||
description: json["description"],
|
description: json['description'],
|
||||||
emailAddress: json["email_address"],
|
emailAddress: json['email_address'],
|
||||||
powerLevel: json["power_level"],
|
powerLevel: json['power_level'],
|
||||||
externalId: json["external_id"],
|
externalId: json['external_id'],
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
"id": id,
|
'id': id,
|
||||||
"created_at": createdAt.toIso8601String(),
|
'created_at': createdAt.toIso8601String(),
|
||||||
"updated_at": updatedAt.toIso8601String(),
|
'updated_at': updatedAt.toIso8601String(),
|
||||||
"deleted_at": deletedAt,
|
'deleted_at': deletedAt,
|
||||||
"name": name,
|
'name': name,
|
||||||
"nick": nick,
|
'nick': nick,
|
||||||
"avatar": avatar,
|
'avatar': avatar,
|
||||||
"banner": banner,
|
'banner': banner,
|
||||||
"description": description,
|
'description': description,
|
||||||
"email_address": emailAddress,
|
'email_address': emailAddress,
|
||||||
"power_level": powerLevel,
|
'power_level': powerLevel,
|
||||||
"external_id": externalId,
|
'external_id': externalId,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -25,28 +25,28 @@ class Call {
|
|||||||
});
|
});
|
||||||
|
|
||||||
factory Call.fromJson(Map<String, dynamic> json) => Call(
|
factory Call.fromJson(Map<String, dynamic> json) => Call(
|
||||||
id: json["id"],
|
id: json['id'],
|
||||||
createdAt: DateTime.parse(json["created_at"]),
|
createdAt: DateTime.parse(json['created_at']),
|
||||||
updatedAt: DateTime.parse(json["updated_at"]),
|
updatedAt: DateTime.parse(json['updated_at']),
|
||||||
deletedAt: json["deleted_at"],
|
deletedAt: json['deleted_at'],
|
||||||
endedAt:
|
endedAt:
|
||||||
json["ended_at"] != null ? DateTime.parse(json["ended_at"]) : null,
|
json['ended_at'] != null ? DateTime.parse(json['ended_at']) : null,
|
||||||
externalId: json["external_id"],
|
externalId: json['external_id'],
|
||||||
founderId: json["founder_id"],
|
founderId: json['founder_id'],
|
||||||
channelId: json["channel_id"],
|
channelId: json['channel_id'],
|
||||||
channel: Channel.fromJson(json["channel"]),
|
channel: Channel.fromJson(json['channel']),
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
"id": id,
|
'id': id,
|
||||||
"created_at": createdAt.toIso8601String(),
|
'created_at': createdAt.toIso8601String(),
|
||||||
"updated_at": updatedAt.toIso8601String(),
|
'updated_at': updatedAt.toIso8601String(),
|
||||||
"deleted_at": deletedAt,
|
'deleted_at': deletedAt,
|
||||||
"ended_at": endedAt?.toIso8601String(),
|
'ended_at': endedAt?.toIso8601String(),
|
||||||
"external_id": externalId,
|
'external_id': externalId,
|
||||||
"founder_id": founderId,
|
'founder_id': founderId,
|
||||||
"channel_id": channelId,
|
'channel_id': channelId,
|
||||||
"channel": channel.toJson(),
|
'channel': channel.toJson(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,35 +32,35 @@ class Channel {
|
|||||||
});
|
});
|
||||||
|
|
||||||
factory Channel.fromJson(Map<String, dynamic> json) => Channel(
|
factory Channel.fromJson(Map<String, dynamic> json) => Channel(
|
||||||
id: json["id"],
|
id: json['id'],
|
||||||
createdAt: DateTime.parse(json["created_at"]),
|
createdAt: DateTime.parse(json['created_at']),
|
||||||
updatedAt: DateTime.parse(json["updated_at"]),
|
updatedAt: DateTime.parse(json['updated_at']),
|
||||||
deletedAt: json["deleted_at"],
|
deletedAt: json['deleted_at'],
|
||||||
alias: json["alias"],
|
alias: json['alias'],
|
||||||
name: json["name"],
|
name: json['name'],
|
||||||
description: json["description"],
|
description: json['description'],
|
||||||
members: json["members"],
|
members: json['members'],
|
||||||
calls: json["calls"],
|
calls: json['calls'],
|
||||||
type: json["type"],
|
type: json['type'],
|
||||||
account: Account.fromJson(json["account"]),
|
account: Account.fromJson(json['account']),
|
||||||
accountId: json["account_id"],
|
accountId: json['account_id'],
|
||||||
realmId: json["realm_id"],
|
realmId: json['realm_id'],
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
"id": id,
|
'id': id,
|
||||||
"created_at": createdAt.toIso8601String(),
|
'created_at': createdAt.toIso8601String(),
|
||||||
"updated_at": updatedAt.toIso8601String(),
|
'updated_at': updatedAt.toIso8601String(),
|
||||||
"deleted_at": deletedAt,
|
'deleted_at': deletedAt,
|
||||||
"alias": alias,
|
'alias': alias,
|
||||||
"name": name,
|
'name': name,
|
||||||
"description": description,
|
'description': description,
|
||||||
"members": members,
|
'members': members,
|
||||||
"calls": calls,
|
'calls': calls,
|
||||||
"type": type,
|
'type': type,
|
||||||
"account": account,
|
'account': account,
|
||||||
"account_id": accountId,
|
'account_id': accountId,
|
||||||
"realm_id": realmId,
|
'realm_id': realmId,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,24 +86,24 @@ class ChannelMember {
|
|||||||
});
|
});
|
||||||
|
|
||||||
factory ChannelMember.fromJson(Map<String, dynamic> json) => ChannelMember(
|
factory ChannelMember.fromJson(Map<String, dynamic> json) => ChannelMember(
|
||||||
id: json["id"],
|
id: json['id'],
|
||||||
createdAt: DateTime.parse(json["created_at"]),
|
createdAt: DateTime.parse(json['created_at']),
|
||||||
updatedAt: DateTime.parse(json["updated_at"]),
|
updatedAt: DateTime.parse(json['updated_at']),
|
||||||
deletedAt: json["deleted_at"],
|
deletedAt: json['deleted_at'],
|
||||||
channelId: json["channel_id"],
|
channelId: json['channel_id'],
|
||||||
accountId: json["account_id"],
|
accountId: json['account_id'],
|
||||||
account: Account.fromJson(json["account"]),
|
account: Account.fromJson(json['account']),
|
||||||
notify: json["notify"],
|
notify: json['notify'],
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
"id": id,
|
'id': id,
|
||||||
"created_at": createdAt.toIso8601String(),
|
'created_at': createdAt.toIso8601String(),
|
||||||
"updated_at": updatedAt.toIso8601String(),
|
'updated_at': updatedAt.toIso8601String(),
|
||||||
"deleted_at": deletedAt,
|
'deleted_at': deletedAt,
|
||||||
"channel_id": channelId,
|
'channel_id': channelId,
|
||||||
"account_id": accountId,
|
'account_id': accountId,
|
||||||
"account": account.toJson(),
|
'account': account.toJson(),
|
||||||
"notify": notify,
|
'notify': notify,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -26,29 +26,29 @@ class Friendship {
|
|||||||
});
|
});
|
||||||
|
|
||||||
factory Friendship.fromJson(Map<String, dynamic> json) => Friendship(
|
factory Friendship.fromJson(Map<String, dynamic> json) => Friendship(
|
||||||
id: json["id"],
|
id: json['id'],
|
||||||
createdAt: DateTime.parse(json["created_at"]),
|
createdAt: DateTime.parse(json['created_at']),
|
||||||
updatedAt: DateTime.parse(json["updated_at"]),
|
updatedAt: DateTime.parse(json['updated_at']),
|
||||||
deletedAt: json["deleted_at"],
|
deletedAt: json['deleted_at'],
|
||||||
accountId: json["account_id"],
|
accountId: json['account_id'],
|
||||||
relatedId: json["related_id"],
|
relatedId: json['related_id'],
|
||||||
blockedBy: json["blocked_by"],
|
blockedBy: json['blocked_by'],
|
||||||
account: Account.fromJson(json["account"]),
|
account: Account.fromJson(json['account']),
|
||||||
related: Account.fromJson(json["related"]),
|
related: Account.fromJson(json['related']),
|
||||||
status: json["status"],
|
status: json['status'],
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
"id": id,
|
'id': id,
|
||||||
"created_at": createdAt.toIso8601String(),
|
'created_at': createdAt.toIso8601String(),
|
||||||
"updated_at": updatedAt.toIso8601String(),
|
'updated_at': updatedAt.toIso8601String(),
|
||||||
"deleted_at": deletedAt,
|
'deleted_at': deletedAt,
|
||||||
"account_id": accountId,
|
'account_id': accountId,
|
||||||
"related_id": relatedId,
|
'related_id': relatedId,
|
||||||
"blocked_by": blockedBy,
|
'blocked_by': blockedBy,
|
||||||
"account": account.toJson(),
|
'account': account.toJson(),
|
||||||
"related": related.toJson(),
|
'related': related.toJson(),
|
||||||
"status": status,
|
'status': status,
|
||||||
};
|
};
|
||||||
|
|
||||||
Account getOtherside(int selfId) {
|
Account getOtherside(int selfId) {
|
||||||
|
@ -36,42 +36,42 @@ class Message {
|
|||||||
});
|
});
|
||||||
|
|
||||||
factory Message.fromJson(Map<String, dynamic> json) => Message(
|
factory Message.fromJson(Map<String, dynamic> json) => Message(
|
||||||
id: json["id"],
|
id: json['id'],
|
||||||
createdAt: DateTime.parse(json["created_at"]),
|
createdAt: DateTime.parse(json['created_at']),
|
||||||
updatedAt: DateTime.parse(json["updated_at"]),
|
updatedAt: DateTime.parse(json['updated_at']),
|
||||||
deletedAt: json["deleted_at"],
|
deletedAt: json['deleted_at'],
|
||||||
content: json["content"],
|
content: json['content'],
|
||||||
metadata: json["metadata"],
|
metadata: json['metadata'],
|
||||||
type: json["type"],
|
type: json['type'],
|
||||||
attachments: List<Attachment>.from(
|
attachments: List<Attachment>.from(
|
||||||
json["attachments"]?.map((x) => Attachment.fromJson(x)) ??
|
json['attachments']?.map((x) => Attachment.fromJson(x)) ??
|
||||||
List.empty()),
|
List.empty()),
|
||||||
channel: Channel.fromJson(json["channel"]),
|
channel: Channel.fromJson(json['channel']),
|
||||||
sender: Sender.fromJson(json["sender"]),
|
sender: Sender.fromJson(json['sender']),
|
||||||
replyId: json["reply_id"],
|
replyId: json['reply_id'],
|
||||||
replyTo: json["reply_to"] != null
|
replyTo: json['reply_to'] != null
|
||||||
? Message.fromJson(json["reply_to"])
|
? Message.fromJson(json['reply_to'])
|
||||||
: null,
|
: null,
|
||||||
channelId: json["channel_id"],
|
channelId: json['channel_id'],
|
||||||
senderId: json["sender_id"],
|
senderId: json['sender_id'],
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
"id": id,
|
'id': id,
|
||||||
"created_at": createdAt.toIso8601String(),
|
'created_at': createdAt.toIso8601String(),
|
||||||
"updated_at": updatedAt.toIso8601String(),
|
'updated_at': updatedAt.toIso8601String(),
|
||||||
"deleted_at": deletedAt,
|
'deleted_at': deletedAt,
|
||||||
"content": content,
|
'content': content,
|
||||||
"metadata": metadata,
|
'metadata': metadata,
|
||||||
"type": type,
|
'type': type,
|
||||||
"attachments": List<dynamic>.from(
|
'attachments': List<dynamic>.from(
|
||||||
attachments?.map((x) => x.toJson()) ?? List.empty()),
|
attachments?.map((x) => x.toJson()) ?? List.empty()),
|
||||||
"channel": channel?.toJson(),
|
'channel': channel?.toJson(),
|
||||||
"sender": sender.toJson(),
|
'sender': sender.toJson(),
|
||||||
"reply_id": replyId,
|
'reply_id': replyId,
|
||||||
"reply_to": replyTo?.toJson(),
|
'reply_to': replyTo?.toJson(),
|
||||||
"channel_id": channelId,
|
'channel_id': channelId,
|
||||||
"sender_id": senderId,
|
'sender_id': senderId,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,24 +97,24 @@ class Sender {
|
|||||||
});
|
});
|
||||||
|
|
||||||
factory Sender.fromJson(Map<String, dynamic> json) => Sender(
|
factory Sender.fromJson(Map<String, dynamic> json) => Sender(
|
||||||
id: json["id"],
|
id: json['id'],
|
||||||
createdAt: DateTime.parse(json["created_at"]),
|
createdAt: DateTime.parse(json['created_at']),
|
||||||
updatedAt: DateTime.parse(json["updated_at"]),
|
updatedAt: DateTime.parse(json['updated_at']),
|
||||||
deletedAt: json["deleted_at"],
|
deletedAt: json['deleted_at'],
|
||||||
account: Account.fromJson(json["account"]),
|
account: Account.fromJson(json['account']),
|
||||||
channelId: json["channel_id"],
|
channelId: json['channel_id'],
|
||||||
accountId: json["account_id"],
|
accountId: json['account_id'],
|
||||||
notify: json["notify"],
|
notify: json['notify'],
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
"id": id,
|
'id': id,
|
||||||
"created_at": createdAt.toIso8601String(),
|
'created_at': createdAt.toIso8601String(),
|
||||||
"updated_at": updatedAt.toIso8601String(),
|
'updated_at': updatedAt.toIso8601String(),
|
||||||
"deleted_at": deletedAt,
|
'deleted_at': deletedAt,
|
||||||
"account": account.toJson(),
|
'account': account.toJson(),
|
||||||
"channel_id": channelId,
|
'channel_id': channelId,
|
||||||
"account_id": accountId,
|
'account_id': accountId,
|
||||||
"notify": notify,
|
'notify': notify,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -28,37 +28,37 @@ class Notification {
|
|||||||
});
|
});
|
||||||
|
|
||||||
factory Notification.fromJson(Map<String, dynamic> json) => Notification(
|
factory Notification.fromJson(Map<String, dynamic> json) => Notification(
|
||||||
id: json["id"],
|
id: json['id'],
|
||||||
createdAt: DateTime.parse(json["created_at"]),
|
createdAt: DateTime.parse(json['created_at']),
|
||||||
updatedAt: DateTime.parse(json["updated_at"]),
|
updatedAt: DateTime.parse(json['updated_at']),
|
||||||
deletedAt: json["deleted_at"],
|
deletedAt: json['deleted_at'],
|
||||||
subject: json["subject"],
|
subject: json['subject'],
|
||||||
content: json["content"],
|
content: json['content'],
|
||||||
links: json["links"] != null
|
links: json['links'] != null
|
||||||
? List<Link>.from(json["links"].map((x) => Link.fromJson(x)))
|
? List<Link>.from(json['links'].map((x) => Link.fromJson(x)))
|
||||||
: List.empty(),
|
: List.empty(),
|
||||||
isImportant: json["is_important"],
|
isImportant: json['is_important'],
|
||||||
isRealtime: json["is_realtime"],
|
isRealtime: json['is_realtime'],
|
||||||
readAt: json["read_at"],
|
readAt: json['read_at'],
|
||||||
senderId: json["sender_id"],
|
senderId: json['sender_id'],
|
||||||
recipientId: json["recipient_id"],
|
recipientId: json['recipient_id'],
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
"id": id,
|
'id': id,
|
||||||
"created_at": createdAt.toIso8601String(),
|
'created_at': createdAt.toIso8601String(),
|
||||||
"updated_at": updatedAt.toIso8601String(),
|
'updated_at': updatedAt.toIso8601String(),
|
||||||
"deleted_at": deletedAt,
|
'deleted_at': deletedAt,
|
||||||
"subject": subject,
|
'subject': subject,
|
||||||
"content": content,
|
'content': content,
|
||||||
"links": links != null
|
'links': links != null
|
||||||
? List<dynamic>.from(links!.map((x) => x.toJson()))
|
? List<dynamic>.from(links!.map((x) => x.toJson()))
|
||||||
: List.empty(),
|
: List.empty(),
|
||||||
"is_important": isImportant,
|
'is_important': isImportant,
|
||||||
"is_realtime": isRealtime,
|
'is_realtime': isRealtime,
|
||||||
"read_at": readAt,
|
'read_at': readAt,
|
||||||
"sender_id": senderId,
|
'sender_id': senderId,
|
||||||
"recipient_id": recipientId,
|
'recipient_id': recipientId,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,12 +72,12 @@ class Link {
|
|||||||
});
|
});
|
||||||
|
|
||||||
factory Link.fromJson(Map<String, dynamic> json) => Link(
|
factory Link.fromJson(Map<String, dynamic> json) => Link(
|
||||||
label: json["label"],
|
label: json['label'],
|
||||||
url: json["url"],
|
url: json['url'],
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
"label": label,
|
'label': label,
|
||||||
"url": url,
|
'url': url,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -10,14 +10,14 @@ class NetworkPackage {
|
|||||||
});
|
});
|
||||||
|
|
||||||
factory NetworkPackage.fromJson(Map<String, dynamic> json) => NetworkPackage(
|
factory NetworkPackage.fromJson(Map<String, dynamic> json) => NetworkPackage(
|
||||||
method: json["w"],
|
method: json['w'],
|
||||||
message: json["m"],
|
message: json['m'],
|
||||||
payload: json["p"],
|
payload: json['p'],
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
"w": method,
|
'w': method,
|
||||||
"m": message,
|
'm': message,
|
||||||
"p": payload,
|
'p': payload,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -8,10 +8,10 @@ class PaginationResult {
|
|||||||
});
|
});
|
||||||
|
|
||||||
factory PaginationResult.fromJson(Map<String, dynamic> json) =>
|
factory PaginationResult.fromJson(Map<String, dynamic> json) =>
|
||||||
PaginationResult(count: json["count"], data: json["data"]);
|
PaginationResult(count: json['count'], data: json['data']);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
"count": count,
|
'count': count,
|
||||||
"data": data,
|
'data': data,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,8 @@ class Post {
|
|||||||
List<Attachment>? attachments;
|
List<Attachment>? attachments;
|
||||||
Map<String, dynamic>? reactionList;
|
Map<String, dynamic>? reactionList;
|
||||||
|
|
||||||
|
String get dataset => '${modelType}s';
|
||||||
|
|
||||||
Post({
|
Post({
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.createdAt,
|
required this.createdAt,
|
||||||
@ -38,46 +40,46 @@ class Post {
|
|||||||
});
|
});
|
||||||
|
|
||||||
factory Post.fromJson(Map<String, dynamic> json) => Post(
|
factory Post.fromJson(Map<String, dynamic> json) => Post(
|
||||||
id: json["id"],
|
id: json['id'],
|
||||||
createdAt: DateTime.parse(json["created_at"]),
|
createdAt: DateTime.parse(json['created_at']),
|
||||||
updatedAt: DateTime.parse(json["updated_at"]),
|
updatedAt: DateTime.parse(json['updated_at']),
|
||||||
deletedAt: json["deleted_at"],
|
deletedAt: json['deleted_at'],
|
||||||
alias: json["alias"],
|
alias: json['alias'],
|
||||||
title: json["title"],
|
title: json['title'],
|
||||||
description: json["description"],
|
description: json['description'],
|
||||||
content: json["content"],
|
content: json['content'],
|
||||||
modelType: json["model_type"],
|
modelType: json['model_type'],
|
||||||
commentCount: json["comment_count"],
|
commentCount: json['comment_count'],
|
||||||
reactionCount: json["reaction_count"],
|
reactionCount: json['reaction_count'],
|
||||||
authorId: json["author_id"],
|
authorId: json['author_id'],
|
||||||
realmId: json["realm_id"],
|
realmId: json['realm_id'],
|
||||||
author: Author.fromJson(json["author"]),
|
author: Author.fromJson(json['author']),
|
||||||
attachments: json["attachments"] != null
|
attachments: json['attachments'] != null
|
||||||
? List<Attachment>.from(
|
? List<Attachment>.from(
|
||||||
json["attachments"].map((x) => Attachment.fromJson(x)))
|
json['attachments'].map((x) => Attachment.fromJson(x)))
|
||||||
: List.empty(),
|
: List.empty(),
|
||||||
reactionList: json["reaction_list"],
|
reactionList: json['reaction_list'],
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
"id": id,
|
'id': id,
|
||||||
"created_at": createdAt.toIso8601String(),
|
'created_at': createdAt.toIso8601String(),
|
||||||
"updated_at": updatedAt.toIso8601String(),
|
'updated_at': updatedAt.toIso8601String(),
|
||||||
"deleted_at": deletedAt,
|
'deleted_at': deletedAt,
|
||||||
"alias": alias,
|
'alias': alias,
|
||||||
"title": title,
|
'title': title,
|
||||||
"description": description,
|
'description': description,
|
||||||
"content": content,
|
'content': content,
|
||||||
"model_type": modelType,
|
'model_type': modelType,
|
||||||
"comment_count": commentCount,
|
'comment_count': commentCount,
|
||||||
"reaction_count": reactionCount,
|
'reaction_count': reactionCount,
|
||||||
"author_id": authorId,
|
'author_id': authorId,
|
||||||
"realm_id": realmId,
|
'realm_id': realmId,
|
||||||
"author": author.toJson(),
|
'author': author.toJson(),
|
||||||
"attachments": attachments == null
|
'attachments': attachments == null
|
||||||
? List.empty()
|
? List.empty()
|
||||||
: List<dynamic>.from(attachments!.map((x) => x.toJson())),
|
: List<dynamic>.from(attachments!.map((x) => x.toJson())),
|
||||||
"reaction_list": reactionList,
|
'reaction_list': reactionList,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,38 +119,38 @@ class Attachment {
|
|||||||
});
|
});
|
||||||
|
|
||||||
factory Attachment.fromJson(Map<String, dynamic> json) => Attachment(
|
factory Attachment.fromJson(Map<String, dynamic> json) => Attachment(
|
||||||
id: json["id"],
|
id: json['id'],
|
||||||
createdAt: DateTime.parse(json["created_at"]),
|
createdAt: DateTime.parse(json['created_at']),
|
||||||
updatedAt: DateTime.parse(json["updated_at"]),
|
updatedAt: DateTime.parse(json['updated_at']),
|
||||||
deletedAt: json["deleted_at"],
|
deletedAt: json['deleted_at'],
|
||||||
fileId: json["file_id"],
|
fileId: json['file_id'],
|
||||||
filesize: json["filesize"],
|
filesize: json['filesize'],
|
||||||
filename: json["filename"],
|
filename: json['filename'],
|
||||||
mimetype: json["mimetype"],
|
mimetype: json['mimetype'],
|
||||||
type: json["type"],
|
type: json['type'],
|
||||||
externalUrl: json["external_url"],
|
externalUrl: json['external_url'],
|
||||||
author: Author.fromJson(json["author"]),
|
author: Author.fromJson(json['author']),
|
||||||
articleId: json["article_id"],
|
articleId: json['article_id'],
|
||||||
momentId: json["moment_id"],
|
momentId: json['moment_id'],
|
||||||
commentId: json["comment_id"],
|
commentId: json['comment_id'],
|
||||||
authorId: json["author_id"],
|
authorId: json['author_id'],
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
"id": id,
|
'id': id,
|
||||||
"created_at": createdAt.toIso8601String(),
|
'created_at': createdAt.toIso8601String(),
|
||||||
"updated_at": updatedAt.toIso8601String(),
|
'updated_at': updatedAt.toIso8601String(),
|
||||||
"deleted_at": deletedAt,
|
'deleted_at': deletedAt,
|
||||||
"file_id": fileId,
|
'file_id': fileId,
|
||||||
"filesize": filesize,
|
'filesize': filesize,
|
||||||
"filename": filename,
|
'filename': filename,
|
||||||
"mimetype": mimetype,
|
'mimetype': mimetype,
|
||||||
"type": type,
|
'type': type,
|
||||||
"external_url": externalUrl,
|
'external_url': externalUrl,
|
||||||
"author": author.toJson(),
|
'author': author.toJson(),
|
||||||
"article_id": articleId,
|
'article_id': articleId,
|
||||||
"moment_id": momentId,
|
'moment_id': momentId,
|
||||||
"comment_id": commentId,
|
'comment_id': commentId,
|
||||||
"author_id": authorId,
|
'author_id': authorId,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -14,12 +14,12 @@ class AuthProvider extends ChangeNotifier {
|
|||||||
final userinfoEndpoint = getRequestUri('passport', '/api/users/me');
|
final userinfoEndpoint = getRequestUri('passport', '/api/users/me');
|
||||||
final redirectUrl = Uri.parse('solian://auth');
|
final redirectUrl = Uri.parse('solian://auth');
|
||||||
|
|
||||||
static const clientId = "solian";
|
static const clientId = 'solian';
|
||||||
static const clientSecret = "_F4%q2Eea3";
|
static const clientSecret = '_F4%q2Eea3';
|
||||||
|
|
||||||
static const storage = FlutterSecureStorage();
|
static const storage = FlutterSecureStorage();
|
||||||
static const storageKey = "identity";
|
static const storageKey = 'identity';
|
||||||
static const profileKey = "profiles";
|
static const profileKey = 'profiles';
|
||||||
|
|
||||||
/// Before use this variable to make request
|
/// Before use this variable to make request
|
||||||
/// **MAKE SURE YOU HAVE CALL THE isAuthorized() METHOD**
|
/// **MAKE SURE YOU HAVE CALL THE isAuthorized() METHOD**
|
||||||
@ -57,7 +57,7 @@ class AuthProvider extends ChangeNotifier {
|
|||||||
password,
|
password,
|
||||||
identifier: clientId,
|
identifier: clientId,
|
||||||
secret: clientSecret,
|
secret: clientSecret,
|
||||||
scopes: ["openid"],
|
scopes: ['openid'],
|
||||||
basicAuth: false,
|
basicAuth: false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -115,6 +115,6 @@ class AuthProvider extends ChangeNotifier {
|
|||||||
|
|
||||||
Future<dynamic> getProfiles() async {
|
Future<dynamic> getProfiles() async {
|
||||||
const storage = FlutterSecureStorage();
|
const storage = FlutterSecureStorage();
|
||||||
return jsonDecode(await storage.read(key: profileKey) ?? "{}");
|
return jsonDecode(await storage.read(key: profileKey) ?? '{}');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import 'dart:async';
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:http/http.dart';
|
||||||
import 'package:livekit_client/livekit_client.dart';
|
import 'package:livekit_client/livekit_client.dart';
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
@ -17,9 +18,11 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
|||||||
|
|
||||||
class ChatProvider extends ChangeNotifier {
|
class ChatProvider extends ChangeNotifier {
|
||||||
bool isOpened = false;
|
bool isOpened = false;
|
||||||
bool isShown = false;
|
bool isCallShown = false;
|
||||||
|
|
||||||
ChatCallInstance? call;
|
Call? ongoingCall;
|
||||||
|
Channel? focusChannel;
|
||||||
|
ChatCallInstance? currentCall;
|
||||||
|
|
||||||
Future<WebSocketChannel?> connect(AuthProvider auth) async {
|
Future<WebSocketChannel?> connect(AuthProvider auth) async {
|
||||||
if (auth.client == null) await auth.loadClient();
|
if (auth.client == null) await auth.loadClient();
|
||||||
@ -43,17 +46,51 @@ class ChatProvider extends ChangeNotifier {
|
|||||||
return channel;
|
return channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool handleCall(Call call, Channel channel,
|
Future<Channel> fetchChannel(String alias) async {
|
||||||
{Function? onUpdate, Function? onDispose}) {
|
final Client client = Client();
|
||||||
if (this.call != null) return false;
|
|
||||||
|
|
||||||
this.call = ChatCallInstance(
|
var uri = getRequestUri('messaging', '/api/channels/$alias');
|
||||||
|
var res = await client.get(uri);
|
||||||
|
if (res.statusCode == 200) {
|
||||||
|
final result = jsonDecode(utf8.decode(res.bodyBytes));
|
||||||
|
focusChannel = Channel.fromJson(result);
|
||||||
|
notifyListeners();
|
||||||
|
return focusChannel!;
|
||||||
|
} else {
|
||||||
|
var message = utf8.decode(res.bodyBytes);
|
||||||
|
throw Exception(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Call?> fetchOngoingCall(String alias) async {
|
||||||
|
final Client client = Client();
|
||||||
|
|
||||||
|
var uri = getRequestUri('messaging', '/api/channels/$alias/calls/ongoing');
|
||||||
|
var res = await client.get(uri);
|
||||||
|
if (res.statusCode == 200) {
|
||||||
|
final result = jsonDecode(utf8.decode(res.bodyBytes));
|
||||||
|
ongoingCall = Call.fromJson(result);
|
||||||
|
notifyListeners();
|
||||||
|
return ongoingCall;
|
||||||
|
} else if (res.statusCode != 404) {
|
||||||
|
var message = utf8.decode(res.bodyBytes);
|
||||||
|
throw Exception(message);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool handleCallJoin(Call call, Channel channel,
|
||||||
|
{Function? onUpdate, Function? onDispose}) {
|
||||||
|
if (currentCall != null) return false;
|
||||||
|
|
||||||
|
currentCall = ChatCallInstance(
|
||||||
onUpdate: () {
|
onUpdate: () {
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
if (onUpdate != null) onUpdate();
|
if (onUpdate != null) onUpdate();
|
||||||
},
|
},
|
||||||
onDispose: () {
|
onDispose: () {
|
||||||
this.call = null;
|
currentCall = null;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
if (onDispose != null) onDispose();
|
if (onDispose != null) onDispose();
|
||||||
},
|
},
|
||||||
@ -64,8 +101,13 @@ class ChatProvider extends ChangeNotifier {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setShown(bool state) {
|
void setOngoingCall(Call? item) {
|
||||||
isShown = state;
|
ongoingCall = item;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCallShown(bool state) {
|
||||||
|
isCallShown = state;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -118,8 +160,9 @@ class ChatCallInstance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> checkPermissions() async {
|
Future<void> checkPermissions() async {
|
||||||
if (lkPlatformIs(PlatformType.macOS) || lkPlatformIs(PlatformType.linux))
|
if (lkPlatformIs(PlatformType.macOS) || lkPlatformIs(PlatformType.linux)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await Permission.camera.request();
|
await Permission.camera.request();
|
||||||
await Permission.microphone.request();
|
await Permission.microphone.request();
|
||||||
@ -133,7 +176,7 @@ class ChatCallInstance {
|
|||||||
final auth = context.read<AuthProvider>();
|
final auth = context.read<AuthProvider>();
|
||||||
if (!await auth.isAuthorized()) {
|
if (!await auth.isAuthorized()) {
|
||||||
onDispose();
|
onDispose();
|
||||||
throw Exception("unauthorized");
|
throw Exception('unauthorized');
|
||||||
}
|
}
|
||||||
|
|
||||||
var uri = getRequestUri(
|
var uri = getRequestUri(
|
||||||
|
@ -29,7 +29,7 @@ class NotifyProvider extends ChangeNotifier {
|
|||||||
const androidSettings = AndroidInitializationSettings('app_icon');
|
const androidSettings = AndroidInitializationSettings('app_icon');
|
||||||
const darwinSettings = DarwinInitializationSettings(
|
const darwinSettings = DarwinInitializationSettings(
|
||||||
notificationCategories: [
|
notificationCategories: [
|
||||||
DarwinNotificationCategory("general"),
|
DarwinNotificationCategory('general'),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
const linuxSettings =
|
const linuxSettings =
|
||||||
@ -46,8 +46,9 @@ class NotifyProvider extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> requestPermissions() async {
|
Future<void> requestPermissions() async {
|
||||||
if (lkPlatformIs(PlatformType.macOS) || lkPlatformIs(PlatformType.linux))
|
if (lkPlatformIs(PlatformType.macOS) || lkPlatformIs(PlatformType.linux)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
await Permission.notification.request();
|
await Permission.notification.request();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ class NameCard extends StatelessWidget {
|
|||||||
Future<Widget> renderAvatar(BuildContext context) async {
|
Future<Widget> renderAvatar(BuildContext context) async {
|
||||||
final auth = context.read<AuthProvider>();
|
final auth = context.read<AuthProvider>();
|
||||||
final profiles = await auth.getProfiles();
|
final profiles = await auth.getProfiles();
|
||||||
return AccountAvatar(source: profiles["picture"], direct: true);
|
return AccountAvatar(source: profiles['picture'], direct: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Column> renderLabel(BuildContext context) async {
|
Future<Column> renderLabel(BuildContext context) async {
|
||||||
@ -107,13 +107,13 @@ class NameCard extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
profiles["nick"],
|
profiles['nick'],
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(profiles["email"])
|
Text(profiles['email'])
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -157,8 +157,9 @@ class _FriendScreenState extends State<FriendScreen> {
|
|||||||
DismissDirection getDismissDirection(Friendship relation) {
|
DismissDirection getDismissDirection(Friendship relation) {
|
||||||
if (relation.status == 2) return DismissDirection.endToStart;
|
if (relation.status == 2) return DismissDirection.endToStart;
|
||||||
if (relation.status == 1) return DismissDirection.startToEnd;
|
if (relation.status == 1) return DismissDirection.startToEnd;
|
||||||
if (relation.status == 0 && relation.relatedId != _selfId)
|
if (relation.status == 0 && relation.relatedId != _selfId) {
|
||||||
return DismissDirection.startToEnd;
|
return DismissDirection.startToEnd;
|
||||||
|
}
|
||||||
return DismissDirection.horizontal;
|
return DismissDirection.horizontal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
|
||||||
import 'package:webview_flutter/webview_flutter.dart';
|
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
|
||||||
|
|
||||||
class AuthorizationScreen extends StatelessWidget {
|
|
||||||
final Uri authorizationUrl;
|
|
||||||
|
|
||||||
const AuthorizationScreen(this.authorizationUrl, {super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
title: Text(AppLocalizations.of(context)!.signIn),
|
|
||||||
),
|
|
||||||
body: Stack(children: [
|
|
||||||
WebViewWidget(
|
|
||||||
controller: WebViewController()
|
|
||||||
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
|
||||||
..setBackgroundColor(Colors.white)
|
|
||||||
..setNavigationDelegate(NavigationDelegate(
|
|
||||||
onNavigationRequest: (NavigationRequest request) {
|
|
||||||
if (request.url.startsWith('solian')) {
|
|
||||||
Navigator.of(context).pop(request.url);
|
|
||||||
WebViewCookieManager().clearCookies();
|
|
||||||
return NavigationDecision.prevent;
|
|
||||||
} else if (request.url.contains("sign-up")) {
|
|
||||||
launchUrl(Uri.parse(request.url));
|
|
||||||
return NavigationDecision.prevent;
|
|
||||||
}
|
|
||||||
return NavigationDecision.navigate;
|
|
||||||
},
|
|
||||||
))
|
|
||||||
..loadRequest(authorizationUrl)
|
|
||||||
..clearCache(),
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -24,14 +24,14 @@ class _ChatCallState extends State<ChatCall> {
|
|||||||
|
|
||||||
late ChatProvider _chat;
|
late ChatProvider _chat;
|
||||||
|
|
||||||
ChatCallInstance get _call => _chat.call!;
|
ChatCallInstance get _call => _chat.currentCall!;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
_chat.setShown(true);
|
_chat.setCallShown(true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,13 +40,13 @@ class _ChatCallState extends State<ChatCall> {
|
|||||||
_chat = context.watch<ChatProvider>();
|
_chat = context.watch<ChatProvider>();
|
||||||
if (!_isHandled) {
|
if (!_isHandled) {
|
||||||
_isHandled = true;
|
_isHandled = true;
|
||||||
if (_chat.handleCall(widget.call, widget.call.channel)) {
|
if (_chat.handleCallJoin(widget.call, widget.call.channel)) {
|
||||||
_chat.call?.init();
|
_chat.currentCall?.init();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget content;
|
Widget content;
|
||||||
if (_chat.call == null) {
|
if (_chat.currentCall == null) {
|
||||||
content = const Center(
|
content = const Center(
|
||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator(),
|
||||||
);
|
);
|
||||||
@ -136,7 +136,7 @@ class _ChatCallState extends State<ChatCall> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void deactivate() {
|
void deactivate() {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) => _chat.setShown(false));
|
WidgetsBinding.instance.addPostFrameCallback((_) => _chat.setCallShown(false));
|
||||||
super.deactivate();
|
super.deactivate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ class _ChannelEditorScreenState extends State<ChannelEditorScreen> {
|
|||||||
? getRequestUri('messaging', '/api/channels')
|
? getRequestUri('messaging', '/api/channels')
|
||||||
: getRequestUri('messaging', '/api/channels/${widget.editing!.id}');
|
: getRequestUri('messaging', '/api/channels/${widget.editing!.id}');
|
||||||
|
|
||||||
final req = Request(widget.editing == null ? "POST" : "PUT", uri);
|
final req = Request(widget.editing == null ? 'POST' : 'PUT', uri);
|
||||||
req.headers['Content-Type'] = 'application/json';
|
req.headers['Content-Type'] = 'application/json';
|
||||||
req.body = jsonEncode(<String, dynamic>{
|
req.body = jsonEncode(<String, dynamic>{
|
||||||
'alias': _aliasController.value.text.toLowerCase(),
|
'alias': _aliasController.value.text.toLowerCase(),
|
||||||
|
@ -4,69 +4,52 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_animate/flutter_animate.dart';
|
import 'package:flutter_animate/flutter_animate.dart';
|
||||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:solian/models/call.dart';
|
|
||||||
import 'package:solian/models/channel.dart';
|
|
||||||
import 'package:solian/models/message.dart';
|
import 'package:solian/models/message.dart';
|
||||||
import 'package:solian/models/pagination.dart';
|
import 'package:solian/models/pagination.dart';
|
||||||
import 'package:solian/providers/auth.dart';
|
import 'package:solian/providers/auth.dart';
|
||||||
|
import 'package:solian/providers/chat.dart';
|
||||||
import 'package:solian/router.dart';
|
import 'package:solian/router.dart';
|
||||||
import 'package:solian/utils/service_url.dart';
|
import 'package:solian/utils/service_url.dart';
|
||||||
import 'package:solian/widgets/chat/channel_action.dart';
|
|
||||||
import 'package:solian/widgets/chat/maintainer.dart';
|
import 'package:solian/widgets/chat/maintainer.dart';
|
||||||
import 'package:solian/widgets/chat/message.dart';
|
import 'package:solian/widgets/chat/message.dart';
|
||||||
import 'package:solian/widgets/chat/message_action.dart';
|
import 'package:solian/widgets/chat/message_action.dart';
|
||||||
import 'package:solian/widgets/chat/message_editor.dart';
|
import 'package:solian/widgets/chat/message_editor.dart';
|
||||||
import 'package:solian/widgets/exts.dart';
|
|
||||||
import 'package:solian/widgets/indent_wrapper.dart';
|
import 'package:solian/widgets/indent_wrapper.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
|
|
||||||
class ChatScreen extends StatefulWidget {
|
class ChatScreen extends StatelessWidget {
|
||||||
final String alias;
|
final String alias;
|
||||||
|
|
||||||
const ChatScreen({super.key, required this.alias});
|
const ChatScreen({super.key, required this.alias});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ChatScreen> createState() => _ChatScreenState();
|
Widget build(BuildContext context) {
|
||||||
|
return IndentWrapper(
|
||||||
|
title: AppLocalizations.of(context)!.post,
|
||||||
|
noSafeArea: true,
|
||||||
|
hideDrawer: true,
|
||||||
|
child: ChatScreenWidget(
|
||||||
|
alias: alias,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ChatScreenState extends State<ChatScreen> {
|
class ChatScreenWidget extends StatefulWidget {
|
||||||
Call? _ongoingCall;
|
final String alias;
|
||||||
Channel? _channelMeta;
|
|
||||||
|
const ChatScreenWidget({super.key, required this.alias});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ChatScreenWidget> createState() => _ChatScreenWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ChatScreenWidgetState extends State<ChatScreenWidget> {
|
||||||
|
bool _isReady = false;
|
||||||
|
|
||||||
final PagingController<int, Message> _pagingController = PagingController(firstPageKey: 0);
|
final PagingController<int, Message> _pagingController = PagingController(firstPageKey: 0);
|
||||||
|
|
||||||
final http.Client _client = http.Client();
|
late final ChatProvider _chat;
|
||||||
|
|
||||||
Future<Channel> fetchMetadata() async {
|
|
||||||
var uri = getRequestUri('messaging', '/api/channels/${widget.alias}');
|
|
||||||
var res = await _client.get(uri);
|
|
||||||
if (res.statusCode == 200) {
|
|
||||||
final result = jsonDecode(utf8.decode(res.bodyBytes));
|
|
||||||
setState(() => _channelMeta = Channel.fromJson(result));
|
|
||||||
return _channelMeta!;
|
|
||||||
} else {
|
|
||||||
var message = utf8.decode(res.bodyBytes);
|
|
||||||
context.showErrorDialog(message);
|
|
||||||
throw Exception(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Call?> fetchCall() async {
|
|
||||||
var uri = getRequestUri('messaging', '/api/channels/${widget.alias}/calls/ongoing');
|
|
||||||
var res = await _client.get(uri);
|
|
||||||
if (res.statusCode == 200) {
|
|
||||||
final result = jsonDecode(utf8.decode(res.bodyBytes));
|
|
||||||
setState(() => _ongoingCall = Call.fromJson(result));
|
|
||||||
return _ongoingCall;
|
|
||||||
} else if (res.statusCode != 404) {
|
|
||||||
var message = utf8.decode(res.bodyBytes);
|
|
||||||
context.showErrorDialog(message);
|
|
||||||
throw Exception(message);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> fetchMessages(int pageKey, BuildContext context) async {
|
Future<void> fetchMessages(int pageKey, BuildContext context) async {
|
||||||
final auth = context.read<AuthProvider>();
|
final auth = context.read<AuthProvider>();
|
||||||
@ -142,11 +125,6 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
Future.delayed(Duration.zero, () {
|
|
||||||
fetchMetadata();
|
|
||||||
fetchCall();
|
|
||||||
});
|
|
||||||
|
|
||||||
_pagingController.addPageRequestListener((pageKey) => fetchMessages(pageKey, context));
|
_pagingController.addPageRequestListener((pageKey) => fetchMessages(pageKey, context));
|
||||||
|
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -180,6 +158,11 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!_isReady) {
|
||||||
|
_isReady = true;
|
||||||
|
_chat = context.watch<ChatProvider>();
|
||||||
|
}
|
||||||
|
|
||||||
final callBanner = MaterialBanner(
|
final callBanner = MaterialBanner(
|
||||||
padding: const EdgeInsets.only(top: 4, bottom: 4, left: 20),
|
padding: const EdgeInsets.only(top: 4, bottom: 4, left: 20),
|
||||||
leading: const Icon(Icons.call_received),
|
leading: const Icon(Icons.call_received),
|
||||||
@ -192,7 +175,7 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
onPressed: () {
|
onPressed: () {
|
||||||
router.pushNamed(
|
router.pushNamed(
|
||||||
'chat.channel.call',
|
'chat.channel.call',
|
||||||
extra: _ongoingCall,
|
extra: _chat.ongoingCall,
|
||||||
pathParameters: {'channel': widget.alias},
|
pathParameters: {'channel': widget.alias},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -200,69 +183,52 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
return IndentWrapper(
|
return FutureBuilder(
|
||||||
hideDrawer: true,
|
future: _chat.fetchChannel(widget.alias),
|
||||||
title: _channelMeta?.name ?? 'Loading...',
|
builder: (context, snapshot) {
|
||||||
appBarActions: _channelMeta != null
|
if (!snapshot.hasData || snapshot.data == null) {
|
||||||
? [
|
return const Center(child: CircularProgressIndicator());
|
||||||
ChannelCallAction(
|
}
|
||||||
call: _ongoingCall,
|
|
||||||
channel: _channelMeta!,
|
|
||||||
onUpdate: () => fetchMetadata(),
|
|
||||||
),
|
|
||||||
ChannelManageAction(
|
|
||||||
channel: _channelMeta!,
|
|
||||||
onUpdate: () => fetchMetadata(),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
: [],
|
|
||||||
child: FutureBuilder(
|
|
||||||
future: fetchMetadata(),
|
|
||||||
builder: (context, snapshot) {
|
|
||||||
if (!snapshot.hasData || snapshot.data == null) {
|
|
||||||
return const Center(child: CircularProgressIndicator());
|
|
||||||
}
|
|
||||||
|
|
||||||
return ChatMaintainer(
|
return ChatMaintainer(
|
||||||
channel: snapshot.data!,
|
channel: snapshot.data!,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
Column(
|
Column(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: PagedListView<int, Message>(
|
child: PagedListView<int, Message>(
|
||||||
reverse: true,
|
reverse: true,
|
||||||
pagingController: _pagingController,
|
pagingController: _pagingController,
|
||||||
builderDelegate: PagedChildBuilderDelegate<Message>(
|
builderDelegate: PagedChildBuilderDelegate<Message>(
|
||||||
animateTransitions: true,
|
animateTransitions: true,
|
||||||
transitionDuration: 500.ms,
|
transitionDuration: 500.ms,
|
||||||
itemBuilder: chatHistoryBuilder,
|
itemBuilder: chatHistoryBuilder,
|
||||||
noItemsFoundIndicatorBuilder: (_) => Container(),
|
noItemsFoundIndicatorBuilder: (_) => Container(),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
ChatMessageEditor(
|
),
|
||||||
channel: widget.alias,
|
ChatMessageEditor(
|
||||||
editing: _editingItem,
|
channel: widget.alias,
|
||||||
replying: _replyingItem,
|
editing: _editingItem,
|
||||||
onReset: () => setState(() {
|
replying: _replyingItem,
|
||||||
_editingItem = null;
|
onReset: () => setState(() {
|
||||||
_replyingItem = null;
|
_editingItem = null;
|
||||||
}),
|
_replyingItem = null;
|
||||||
),
|
}),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
_ongoingCall != null ? callBanner.animate().slideY() : Container(),
|
),
|
||||||
],
|
_chat.ongoingCall != null ? callBanner.animate().slideY() : Container(),
|
||||||
),
|
],
|
||||||
onInsertMessage: (message) => addMessage(message),
|
),
|
||||||
onUpdateMessage: (message) => updateMessage(message),
|
onInsertMessage: (message) => addMessage(message),
|
||||||
onDeleteMessage: (message) => deleteMessage(message),
|
onUpdateMessage: (message) => updateMessage(message),
|
||||||
onCallStarted: (call) => setState(() => _ongoingCall = call),
|
onDeleteMessage: (message) => deleteMessage(message),
|
||||||
onCallEnded: () => setState(() => _ongoingCall = null),
|
onCallStarted: (call) => _chat.setOngoingCall(call),
|
||||||
);
|
onCallEnded: () => _chat.setOngoingCall(null),
|
||||||
},
|
);
|
||||||
),
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,10 @@ import 'package:provider/provider.dart';
|
|||||||
import 'package:solian/models/channel.dart';
|
import 'package:solian/models/channel.dart';
|
||||||
import 'package:solian/providers/auth.dart';
|
import 'package:solian/providers/auth.dart';
|
||||||
import 'package:solian/router.dart';
|
import 'package:solian/router.dart';
|
||||||
|
import 'package:solian/screens/chat/chat.dart';
|
||||||
import 'package:solian/utils/service_url.dart';
|
import 'package:solian/utils/service_url.dart';
|
||||||
import 'package:solian/widgets/chat/chat_new.dart';
|
import 'package:solian/widgets/chat/chat_new.dart';
|
||||||
|
import 'package:solian/widgets/empty.dart';
|
||||||
import 'package:solian/widgets/exts.dart';
|
import 'package:solian/widgets/exts.dart';
|
||||||
import 'package:solian/widgets/indent_wrapper.dart';
|
import 'package:solian/widgets/indent_wrapper.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
@ -21,6 +23,68 @@ class ChatIndexScreen extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _ChatIndexScreenState extends State<ChatIndexScreen> {
|
class _ChatIndexScreenState extends State<ChatIndexScreen> {
|
||||||
|
Channel? _selectedChannel;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final screenWidth = MediaQuery.of(context).size.width;
|
||||||
|
final isLargeScreen = screenWidth >= 600;
|
||||||
|
|
||||||
|
return IndentWrapper(
|
||||||
|
title: AppLocalizations.of(context)!.chat,
|
||||||
|
appBarActions: const [NotificationButton()],
|
||||||
|
fixedAppBarColor: isLargeScreen,
|
||||||
|
child: isLargeScreen
|
||||||
|
? Row(
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
flex: 2,
|
||||||
|
child: ChatIndexScreenWidget(
|
||||||
|
onSelect: (item) {
|
||||||
|
setState(() => _selectedChannel = item);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const VerticalDivider(thickness: 0.3, width: 0.3),
|
||||||
|
Flexible(
|
||||||
|
flex: 4,
|
||||||
|
child: _selectedChannel == null
|
||||||
|
? const SelectionEmptyWidget()
|
||||||
|
: ChatScreenWidget(
|
||||||
|
key: Key('c${_selectedChannel!.id}'),
|
||||||
|
alias: _selectedChannel!.alias,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: ChatIndexScreenWidget(
|
||||||
|
onSelect: (item) async {
|
||||||
|
final result = await router.pushNamed(
|
||||||
|
'chat.channel',
|
||||||
|
pathParameters: {
|
||||||
|
'channel': item.alias,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
switch (result) {
|
||||||
|
case 'refresh':
|
||||||
|
// fetchChannels();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ChatIndexScreenWidget extends StatefulWidget {
|
||||||
|
final Function(Channel item) onSelect;
|
||||||
|
|
||||||
|
const ChatIndexScreenWidget({super.key, required this.onSelect});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ChatIndexScreenWidget> createState() => _ChatIndexScreenWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ChatIndexScreenWidgetState extends State<ChatIndexScreenWidget> {
|
||||||
List<Channel> _channels = List.empty();
|
List<Channel> _channels = List.empty();
|
||||||
|
|
||||||
Future<void> fetchChannels() async {
|
Future<void> fetchChannels() async {
|
||||||
@ -61,9 +125,7 @@ class _ChatIndexScreenState extends State<ChatIndexScreen> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final auth = context.read<AuthProvider>();
|
final auth = context.read<AuthProvider>();
|
||||||
|
|
||||||
return IndentWrapper(
|
return Scaffold(
|
||||||
title: AppLocalizations.of(context)!.chat,
|
|
||||||
appBarActions: const [NotificationButton()],
|
|
||||||
floatingActionButton: FutureBuilder(
|
floatingActionButton: FutureBuilder(
|
||||||
future: auth.isAuthorized(),
|
future: auth.isAuthorized(),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
@ -78,43 +140,33 @@ class _ChatIndexScreenState extends State<ChatIndexScreen> {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
child: FutureBuilder(
|
body: FutureBuilder(
|
||||||
future: auth.isAuthorized(),
|
future: auth.isAuthorized(),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (!snapshot.hasData || !snapshot.data!) {
|
if (!snapshot.hasData || !snapshot.data!) {
|
||||||
return const SignInRequiredScreen();
|
return const SignInRequiredScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
return RefreshIndicator(
|
return RefreshIndicator(
|
||||||
onRefresh: () => fetchChannels(),
|
onRefresh: () => fetchChannels(),
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
itemCount: _channels.length,
|
itemCount: _channels.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final element = _channels[index];
|
final element = _channels[index];
|
||||||
return ListTile(
|
return ListTile(
|
||||||
leading: const CircleAvatar(
|
leading: const CircleAvatar(
|
||||||
backgroundColor: Colors.indigo,
|
backgroundColor: Colors.indigo,
|
||||||
child: Icon(Icons.tag, color: Colors.white),
|
child: Icon(Icons.tag, color: Colors.white),
|
||||||
),
|
),
|
||||||
title: Text(element.name),
|
title: Text(element.name),
|
||||||
subtitle: Text(element.description),
|
subtitle: Text(element.description),
|
||||||
onTap: () async {
|
onTap: () => widget.onSelect(element),
|
||||||
final result = await router.pushNamed(
|
);
|
||||||
'chat.channel',
|
},
|
||||||
pathParameters: {
|
),
|
||||||
'channel': element.alias,
|
);
|
||||||
},
|
},
|
||||||
);
|
),
|
||||||
switch (result) {
|
|
||||||
case 'refresh':
|
|
||||||
fetchChannels();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,10 +6,12 @@ import 'package:solian/models/pagination.dart';
|
|||||||
import 'package:solian/models/post.dart';
|
import 'package:solian/models/post.dart';
|
||||||
import 'package:solian/providers/auth.dart';
|
import 'package:solian/providers/auth.dart';
|
||||||
import 'package:solian/router.dart';
|
import 'package:solian/router.dart';
|
||||||
|
import 'package:solian/screens/posts/screen.dart';
|
||||||
import 'package:solian/utils/service_url.dart';
|
import 'package:solian/utils/service_url.dart';
|
||||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'package:solian/widgets/empty.dart';
|
||||||
import 'package:solian/widgets/indent_wrapper.dart';
|
import 'package:solian/widgets/indent_wrapper.dart';
|
||||||
import 'package:solian/widgets/notification_notifier.dart';
|
import 'package:solian/widgets/notification_notifier.dart';
|
||||||
import 'package:solian/widgets/posts/item.dart';
|
import 'package:solian/widgets/posts/item.dart';
|
||||||
@ -22,8 +24,68 @@ class ExploreScreen extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _ExploreScreenState extends State<ExploreScreen> {
|
class _ExploreScreenState extends State<ExploreScreen> {
|
||||||
final PagingController<int, Post> _pagingController =
|
Post? _selectedPost;
|
||||||
PagingController(firstPageKey: 0);
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final screenWidth = MediaQuery.of(context).size.width;
|
||||||
|
final isLargeScreen = screenWidth >= 600;
|
||||||
|
|
||||||
|
return IndentWrapper(
|
||||||
|
noSafeArea: true,
|
||||||
|
fixedAppBarColor: isLargeScreen,
|
||||||
|
appBarActions: const [NotificationButton()],
|
||||||
|
title: AppLocalizations.of(context)!.explore,
|
||||||
|
child: isLargeScreen
|
||||||
|
? Row(
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
flex: 2,
|
||||||
|
child: ExploreScreenWidget(
|
||||||
|
onSelect: (item) {
|
||||||
|
setState(() => _selectedPost = item);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const VerticalDivider(thickness: 0.3, width: 0.3),
|
||||||
|
Flexible(
|
||||||
|
flex: 4,
|
||||||
|
child: _selectedPost == null
|
||||||
|
? const SelectionEmptyWidget()
|
||||||
|
: PostScreenWidget(
|
||||||
|
key: Key('p${_selectedPost!.id}'),
|
||||||
|
dataset: _selectedPost!.dataset,
|
||||||
|
alias: _selectedPost!.alias,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: ExploreScreenWidget(
|
||||||
|
onSelect: (item) {
|
||||||
|
router.pushNamed(
|
||||||
|
'posts.screen',
|
||||||
|
pathParameters: {
|
||||||
|
'alias': item.alias,
|
||||||
|
'dataset': item.dataset,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ExploreScreenWidget extends StatefulWidget {
|
||||||
|
final Function(Post item) onSelect;
|
||||||
|
|
||||||
|
const ExploreScreenWidget({super.key, required this.onSelect});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ExploreScreenWidget> createState() => _ExploreScreenWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ExploreScreenWidgetState extends State<ExploreScreenWidget> {
|
||||||
|
final PagingController<int, Post> _pagingController = PagingController(firstPageKey: 0);
|
||||||
|
|
||||||
final http.Client _client = http.Client();
|
final http.Client _client = http.Client();
|
||||||
|
|
||||||
@ -31,15 +93,12 @@ class _ExploreScreenState extends State<ExploreScreen> {
|
|||||||
final offset = pageKey;
|
final offset = pageKey;
|
||||||
const take = 5;
|
const take = 5;
|
||||||
|
|
||||||
var uri =
|
var uri = getRequestUri('interactive', '/api/feed?take=$take&offset=$offset');
|
||||||
getRequestUri('interactive', '/api/feed?take=$take&offset=$offset');
|
|
||||||
|
|
||||||
var res = await _client.get(uri);
|
var res = await _client.get(uri);
|
||||||
if (res.statusCode == 200) {
|
if (res.statusCode == 200) {
|
||||||
final result =
|
final result = PaginationResult.fromJson(jsonDecode(utf8.decode(res.bodyBytes)));
|
||||||
PaginationResult.fromJson(jsonDecode(utf8.decode(res.bodyBytes)));
|
final items = result.data?.map((x) => Post.fromJson(x)).toList() ?? List.empty();
|
||||||
final items =
|
|
||||||
result.data?.map((x) => Post.fromJson(x)).toList() ?? List.empty();
|
|
||||||
final isLastPage = (result.count - pageKey) < take;
|
final isLastPage = (result.count - pageKey) < take;
|
||||||
if (isLastPage || result.data == null) {
|
if (isLastPage || result.data == null) {
|
||||||
_pagingController.appendLastPage(items);
|
_pagingController.appendLastPage(items);
|
||||||
@ -63,8 +122,7 @@ class _ExploreScreenState extends State<ExploreScreen> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final auth = context.read<AuthProvider>();
|
final auth = context.read<AuthProvider>();
|
||||||
|
|
||||||
return IndentWrapper(
|
return Scaffold(
|
||||||
noSafeArea: true,
|
|
||||||
floatingActionButton: FutureBuilder(
|
floatingActionButton: FutureBuilder(
|
||||||
future: auth.isAuthorized(),
|
future: auth.isAuthorized(),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
@ -72,7 +130,7 @@ class _ExploreScreenState extends State<ExploreScreen> {
|
|||||||
return FloatingActionButton(
|
return FloatingActionButton(
|
||||||
child: const Icon(Icons.edit),
|
child: const Icon(Icons.edit),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
final did = await router.pushNamed("posts.moments.editor");
|
final did = await router.pushNamed('posts.moments.editor');
|
||||||
if (did == true) _pagingController.refresh();
|
if (did == true) _pagingController.refresh();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -81,9 +139,7 @@ class _ExploreScreenState extends State<ExploreScreen> {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
appBarActions: const [NotificationButton()],
|
body: RefreshIndicator(
|
||||||
title: AppLocalizations.of(context)!.explore,
|
|
||||||
child: RefreshIndicator(
|
|
||||||
onRefresh: () => Future.sync(
|
onRefresh: () => Future.sync(
|
||||||
() => _pagingController.refresh(),
|
() => _pagingController.refresh(),
|
||||||
),
|
),
|
||||||
@ -93,15 +149,7 @@ class _ExploreScreenState extends State<ExploreScreen> {
|
|||||||
itemBuilder: (context, item, index) => PostItem(
|
itemBuilder: (context, item, index) => PostItem(
|
||||||
item: item,
|
item: item,
|
||||||
onUpdate: () => _pagingController.refresh(),
|
onUpdate: () => _pagingController.refresh(),
|
||||||
onTap: () {
|
onTap: () => widget.onSelect(item),
|
||||||
router.pushNamed(
|
|
||||||
'posts.screen',
|
|
||||||
pathParameters: {
|
|
||||||
'alias': item.alias,
|
|
||||||
'dataset': '${item.modelType}s',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -97,7 +97,7 @@ class NotificationItem extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
left: 16, right: 16, top: 34, bottom: 12),
|
left: 16, right: 16, top: 34, bottom: 12),
|
||||||
child: Text(
|
child: Text(
|
||||||
"Links",
|
'Links',
|
||||||
style: Theme.of(context).textTheme.headlineSmall,
|
style: Theme.of(context).textTheme.headlineSmall,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -151,7 +151,7 @@ class NotificationItem extends StatelessWidget {
|
|||||||
text: item.subject,
|
text: item.subject,
|
||||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
const TextSpan(text: " is marked as read")
|
const TextSpan(text: ' is marked as read')
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -65,7 +65,7 @@ class _CommentEditorScreenState extends State<CommentEditorScreen> {
|
|||||||
? getRequestUri('interactive', '/api/p/$relatedDataset/$alias/comments')
|
? getRequestUri('interactive', '/api/p/$relatedDataset/$alias/comments')
|
||||||
: getRequestUri('interactive', '/api/p/comments/${widget.editing!.id}');
|
: getRequestUri('interactive', '/api/p/comments/${widget.editing!.id}');
|
||||||
|
|
||||||
final req = Request(widget.editing == null ? "POST" : "PUT", uri);
|
final req = Request(widget.editing == null ? 'POST' : 'PUT', uri);
|
||||||
req.headers['Content-Type'] = 'application/json';
|
req.headers['Content-Type'] = 'application/json';
|
||||||
req.body = jsonEncode(<String, dynamic>{
|
req.body = jsonEncode(<String, dynamic>{
|
||||||
'alias': _alias,
|
'alias': _alias,
|
||||||
@ -140,12 +140,12 @@ class _CommentEditorScreenState extends State<CommentEditorScreen> {
|
|||||||
if (snapshot.hasData) {
|
if (snapshot.hasData) {
|
||||||
var userinfo = snapshot.data;
|
var userinfo = snapshot.data;
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(userinfo["nick"]),
|
title: Text(userinfo['nick']),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
AppLocalizations.of(context)!.postIdentityNotify,
|
AppLocalizations.of(context)!.postIdentityNotify,
|
||||||
),
|
),
|
||||||
leading: AccountAvatar(
|
leading: AccountAvatar(
|
||||||
source: userinfo["picture"],
|
source: userinfo['picture'],
|
||||||
direct: true,
|
direct: true,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -55,7 +55,7 @@ class _MomentEditorScreenState extends State<MomentEditorScreen> {
|
|||||||
? getRequestUri('interactive', '/api/p/moments')
|
? getRequestUri('interactive', '/api/p/moments')
|
||||||
: getRequestUri('interactive', '/api/p/moments/${widget.editing!.id}');
|
: getRequestUri('interactive', '/api/p/moments/${widget.editing!.id}');
|
||||||
|
|
||||||
final req = Request(widget.editing == null ? "POST" : "PUT", uri);
|
final req = Request(widget.editing == null ? 'POST' : 'PUT', uri);
|
||||||
req.headers['Content-Type'] = 'application/json';
|
req.headers['Content-Type'] = 'application/json';
|
||||||
req.body = jsonEncode(<String, dynamic>{
|
req.body = jsonEncode(<String, dynamic>{
|
||||||
'alias': _alias,
|
'alias': _alias,
|
||||||
@ -130,12 +130,12 @@ class _MomentEditorScreenState extends State<MomentEditorScreen> {
|
|||||||
if (snapshot.hasData) {
|
if (snapshot.hasData) {
|
||||||
var userinfo = snapshot.data;
|
var userinfo = snapshot.data;
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(userinfo["nick"]),
|
title: Text(userinfo['nick']),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
AppLocalizations.of(context)!.postIdentityNotify,
|
AppLocalizations.of(context)!.postIdentityNotify,
|
||||||
),
|
),
|
||||||
leading: AccountAvatar(
|
leading: AccountAvatar(
|
||||||
source: userinfo["picture"],
|
source: userinfo['picture'],
|
||||||
direct: true,
|
direct: true,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -11,25 +11,43 @@ import 'package:solian/widgets/posts/comment_list.dart';
|
|||||||
import 'package:solian/widgets/posts/item.dart';
|
import 'package:solian/widgets/posts/item.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
class PostScreen extends StatefulWidget {
|
class PostScreen extends StatelessWidget {
|
||||||
final String dataset;
|
final String dataset;
|
||||||
final String alias;
|
final String alias;
|
||||||
|
|
||||||
const PostScreen({super.key, required this.alias, required this.dataset});
|
const PostScreen({super.key, required this.alias, required this.dataset});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<PostScreen> createState() => _PostScreenState();
|
Widget build(BuildContext context) {
|
||||||
|
return IndentWrapper(
|
||||||
|
title: AppLocalizations.of(context)!.post,
|
||||||
|
noSafeArea: true,
|
||||||
|
hideDrawer: true,
|
||||||
|
child: PostScreenWidget(
|
||||||
|
dataset: dataset,
|
||||||
|
alias: alias,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _PostScreenState extends State<PostScreen> {
|
class PostScreenWidget extends StatefulWidget {
|
||||||
|
final String dataset;
|
||||||
|
final String alias;
|
||||||
|
|
||||||
|
const PostScreenWidget({super.key, required this.dataset, required this.alias});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<PostScreenWidget> createState() => _PostScreenWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PostScreenWidgetState extends State<PostScreenWidget> {
|
||||||
final _client = http.Client();
|
final _client = http.Client();
|
||||||
|
|
||||||
final PagingController<int, Post> _commentPagingController =
|
final PagingController<int, Post> _commentPagingController = PagingController(firstPageKey: 0);
|
||||||
PagingController(firstPageKey: 0);
|
|
||||||
|
|
||||||
Future<Post?> fetchPost(BuildContext context) async {
|
Future<Post?> fetchPost(BuildContext context) async {
|
||||||
final uri = getRequestUri(
|
final uri = getRequestUri('interactive', '/api/p/${widget.dataset}/${widget.alias}');
|
||||||
'interactive', '/api/p/${widget.dataset}/${widget.alias}');
|
|
||||||
final res = await _client.get(uri);
|
final res = await _client.get(uri);
|
||||||
if (res.statusCode != 200) {
|
if (res.statusCode != 200) {
|
||||||
final err = utf8.decode(res.bodyBytes);
|
final err = utf8.decode(res.bodyBytes);
|
||||||
@ -42,43 +60,38 @@ class _PostScreenState extends State<PostScreen> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return IndentWrapper(
|
return FutureBuilder(
|
||||||
noSafeArea: true,
|
future: fetchPost(context),
|
||||||
hideDrawer: true,
|
builder: (context, snapshot) {
|
||||||
title: AppLocalizations.of(context)!.post,
|
if (snapshot.hasData && snapshot.data != null) {
|
||||||
child: FutureBuilder(
|
return CustomScrollView(
|
||||||
future: fetchPost(context),
|
slivers: [
|
||||||
builder: (context, snapshot) {
|
SliverToBoxAdapter(
|
||||||
if (snapshot.hasData && snapshot.data != null) {
|
child: PostItem(
|
||||||
return CustomScrollView(
|
item: snapshot.data!,
|
||||||
slivers: [
|
brief: false,
|
||||||
SliverToBoxAdapter(
|
ripple: false,
|
||||||
child: PostItem(
|
|
||||||
item: snapshot.data!,
|
|
||||||
brief: false,
|
|
||||||
ripple: false,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
SliverToBoxAdapter(
|
),
|
||||||
child: CommentListHeader(
|
SliverToBoxAdapter(
|
||||||
related: snapshot.data!,
|
child: CommentListHeader(
|
||||||
paging: _commentPagingController,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
CommentList(
|
|
||||||
related: snapshot.data!,
|
related: snapshot.data!,
|
||||||
dataset: widget.dataset,
|
|
||||||
paging: _commentPagingController,
|
paging: _commentPagingController,
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
);
|
CommentList(
|
||||||
} else {
|
related: snapshot.data!,
|
||||||
return const Center(
|
dataset: widget.dataset,
|
||||||
child: CircularProgressIndicator(),
|
paging: _commentPagingController,
|
||||||
);
|
),
|
||||||
}
|
],
|
||||||
},
|
);
|
||||||
),
|
} else {
|
||||||
|
return const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ class CallOverlay extends StatelessWidget {
|
|||||||
|
|
||||||
final chat = context.watch<ChatProvider>();
|
final chat = context.watch<ChatProvider>();
|
||||||
|
|
||||||
if (chat.isShown || chat.call == null) {
|
if (chat.isCallShown || chat.currentCall == null) {
|
||||||
return Container();
|
return Container();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,8 +54,8 @@ class CallOverlay extends StatelessWidget {
|
|||||||
onTap: () {
|
onTap: () {
|
||||||
router.pushNamed(
|
router.pushNamed(
|
||||||
'chat.channel.call',
|
'chat.channel.call',
|
||||||
extra: chat.call!.info,
|
extra: chat.currentCall!.info,
|
||||||
pathParameters: {'channel': chat.call!.channel.alias},
|
pathParameters: {'channel': chat.currentCall!.channel.alias},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -73,9 +73,9 @@ class _ControlsWidgetState extends State<ControlsWidget> {
|
|||||||
if (await context.showDisconnectDialog() != true) return;
|
if (await context.showDisconnectDialog() != true) return;
|
||||||
|
|
||||||
final chat = context.read<ChatProvider>();
|
final chat = context.read<ChatProvider>();
|
||||||
if (chat.call != null) {
|
if (chat.currentCall != null) {
|
||||||
chat.call!.deactivate();
|
chat.currentCall!.deactivate();
|
||||||
chat.call!.dispose();
|
chat.currentCall!.dispose();
|
||||||
router.pop();
|
router.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,23 +55,19 @@ class _ChatMaintainerState extends State<ChatMaintainer> {
|
|||||||
switch (result.method) {
|
switch (result.method) {
|
||||||
case 'messages.new':
|
case 'messages.new':
|
||||||
final payload = Message.fromJson(result.payload!);
|
final payload = Message.fromJson(result.payload!);
|
||||||
if (payload.channelId == widget.channel.id)
|
if (payload.channelId == widget.channel.id) widget.onInsertMessage(payload);
|
||||||
widget.onInsertMessage(payload);
|
|
||||||
break;
|
break;
|
||||||
case 'messages.update':
|
case 'messages.update':
|
||||||
final payload = Message.fromJson(result.payload!);
|
final payload = Message.fromJson(result.payload!);
|
||||||
if (payload.channelId == widget.channel.id)
|
if (payload.channelId == widget.channel.id) widget.onUpdateMessage(payload);
|
||||||
widget.onUpdateMessage(payload);
|
|
||||||
break;
|
break;
|
||||||
case 'messages.burnt':
|
case 'messages.burnt':
|
||||||
final payload = Message.fromJson(result.payload!);
|
final payload = Message.fromJson(result.payload!);
|
||||||
if (payload.channelId == widget.channel.id)
|
if (payload.channelId == widget.channel.id) widget.onDeleteMessage(payload);
|
||||||
widget.onDeleteMessage(payload);
|
|
||||||
break;
|
break;
|
||||||
case 'calls.new':
|
case 'calls.new':
|
||||||
final payload = Call.fromJson(result.payload!);
|
final payload = Call.fromJson(result.payload!);
|
||||||
if (payload.channelId == widget.channel.id)
|
if (payload.channelId == widget.channel.id) widget.onCallStarted(payload);
|
||||||
widget.onCallStarted(payload);
|
|
||||||
break;
|
break;
|
||||||
case 'calls.end':
|
case 'calls.end':
|
||||||
final payload = Call.fromJson(result.payload!);
|
final payload = Call.fromJson(result.payload!);
|
||||||
|
@ -56,7 +56,7 @@ class _ChatMessageEditorState extends State<ChatMessageEditor> {
|
|||||||
? getRequestUri('messaging', '/api/channels/${widget.channel}/messages')
|
? getRequestUri('messaging', '/api/channels/${widget.channel}/messages')
|
||||||
: getRequestUri('messaging', '/api/channels/${widget.channel}/messages/${widget.editing!.id}');
|
: getRequestUri('messaging', '/api/channels/${widget.channel}/messages/${widget.editing!.id}');
|
||||||
|
|
||||||
final req = Request(widget.editing == null ? "POST" : "PUT", uri);
|
final req = Request(widget.editing == null ? 'POST' : 'PUT', uri);
|
||||||
req.headers['Content-Type'] = 'application/json';
|
req.headers['Content-Type'] = 'application/json';
|
||||||
req.body = jsonEncode(<String, dynamic>{
|
req.body = jsonEncode(<String, dynamic>{
|
||||||
'content': _textController.value.text,
|
'content': _textController.value.text,
|
||||||
@ -163,7 +163,6 @@ class _ChatMessageEditorState extends State<ChatMessageEditor> {
|
|||||||
focusNode: _focusNode,
|
focusNode: _focusNode,
|
||||||
controller: _textController,
|
controller: _textController,
|
||||||
maxLines: null,
|
maxLines: null,
|
||||||
autofocus: true,
|
|
||||||
autocorrect: true,
|
autocorrect: true,
|
||||||
keyboardType: TextInputType.text,
|
keyboardType: TextInputType.text,
|
||||||
decoration: InputDecoration.collapsed(
|
decoration: InputDecoration.collapsed(
|
||||||
|
@ -22,7 +22,11 @@ class LayoutWrapper extends StatelessWidget {
|
|||||||
final content = child ?? Container();
|
final content = child ?? Container();
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(title: Text(title), actions: appBarActions),
|
appBar: AppBar(
|
||||||
|
title: Text(title),
|
||||||
|
actions: appBarActions,
|
||||||
|
centerTitle: false,
|
||||||
|
),
|
||||||
floatingActionButton: floatingActionButton,
|
floatingActionButton: floatingActionButton,
|
||||||
drawer: const SolianNavigationDrawer(),
|
drawer: const SolianNavigationDrawer(),
|
||||||
body: noSafeArea ? content : SafeArea(child: content),
|
body: noSafeArea ? content : SafeArea(child: content),
|
||||||
|
23
lib/widgets/empty.dart
Normal file
23
lib/widgets/empty.dart
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
|
class SelectionEmptyWidget extends StatelessWidget {
|
||||||
|
const SelectionEmptyWidget({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Image.asset('assets/logo.png', width: 64, height: 64),
|
||||||
|
const SizedBox(height: 2),
|
||||||
|
Text(
|
||||||
|
AppLocalizations.of(context)!.appName,
|
||||||
|
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w900),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -9,8 +9,8 @@ extension SolianCommonExtensions on BuildContext {
|
|||||||
return message
|
return message
|
||||||
.split(' ')
|
.split(' ')
|
||||||
.map((element) =>
|
.map((element) =>
|
||||||
"${element[0].toUpperCase()}${element.substring(1).toLowerCase()}")
|
'${element[0].toUpperCase()}${element.substring(1).toLowerCase()}')
|
||||||
.join(" ");
|
.join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
return showDialog<void>(
|
return showDialog<void>(
|
||||||
|
@ -5,6 +5,7 @@ import 'package:solian/widgets/navigation_drawer.dart';
|
|||||||
|
|
||||||
class IndentWrapper extends LayoutWrapper {
|
class IndentWrapper extends LayoutWrapper {
|
||||||
final bool hideDrawer;
|
final bool hideDrawer;
|
||||||
|
final bool fixedAppBarColor;
|
||||||
|
|
||||||
const IndentWrapper({
|
const IndentWrapper({
|
||||||
super.key,
|
super.key,
|
||||||
@ -13,6 +14,7 @@ class IndentWrapper extends LayoutWrapper {
|
|||||||
super.floatingActionButton,
|
super.floatingActionButton,
|
||||||
super.appBarActions,
|
super.appBarActions,
|
||||||
this.hideDrawer = false,
|
this.hideDrawer = false,
|
||||||
|
this.fixedAppBarColor = false,
|
||||||
super.noSafeArea = false,
|
super.noSafeArea = false,
|
||||||
}) : super();
|
}) : super();
|
||||||
|
|
||||||
@ -30,6 +32,8 @@ class IndentWrapper extends LayoutWrapper {
|
|||||||
: null,
|
: null,
|
||||||
title: Text(title),
|
title: Text(title),
|
||||||
actions: appBarActions,
|
actions: appBarActions,
|
||||||
|
centerTitle: false,
|
||||||
|
elevation: fixedAppBarColor ? 4 : null,
|
||||||
),
|
),
|
||||||
floatingActionButton: floatingActionButton,
|
floatingActionButton: floatingActionButton,
|
||||||
drawer: const SolianNavigationDrawer(),
|
drawer: const SolianNavigationDrawer(),
|
||||||
|
@ -39,21 +39,21 @@ class _SolianNavigationDrawerState extends State<SolianNavigationDrawer> {
|
|||||||
icon: const Icon(Icons.explore),
|
icon: const Icon(Icons.explore),
|
||||||
label: Text(AppLocalizations.of(context)!.explore),
|
label: Text(AppLocalizations.of(context)!.explore),
|
||||||
),
|
),
|
||||||
"explore",
|
'explore',
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
NavigationDrawerDestination(
|
NavigationDrawerDestination(
|
||||||
icon: const Icon(Icons.send),
|
icon: const Icon(Icons.send),
|
||||||
label: Text(AppLocalizations.of(context)!.chat),
|
label: Text(AppLocalizations.of(context)!.chat),
|
||||||
),
|
),
|
||||||
"chat",
|
'chat',
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
NavigationDrawerDestination(
|
NavigationDrawerDestination(
|
||||||
icon: const Icon(Icons.account_circle),
|
icon: const Icon(Icons.account_circle),
|
||||||
label: Text(AppLocalizations.of(context)!.account),
|
label: Text(AppLocalizations.of(context)!.account),
|
||||||
),
|
),
|
||||||
"account",
|
'account',
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -69,7 +69,7 @@ class _SolianNavigationDrawerState extends State<SolianNavigationDrawer> {
|
|||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Image.asset("assets/logo.png", width: 26, height: 26),
|
Image.asset('assets/logo.png', width: 26, height: 26),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Text(
|
Text(
|
||||||
AppLocalizations.of(context)!.appName,
|
AppLocalizations.of(context)!.appName,
|
||||||
|
@ -85,7 +85,7 @@ class _NotificationButtonState extends State<NotificationButton> {
|
|||||||
child: IconButton(
|
child: IconButton(
|
||||||
icon: const Icon(Icons.notifications),
|
icon: const Icon(Icons.notifications),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
router.pushNamed("notification");
|
router.pushNamed('notification');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -112,7 +112,7 @@ class _AttachmentEditorState extends State<AttachmentEditor> {
|
|||||||
var res = await auth.client!.send(req);
|
var res = await auth.client!.send(req);
|
||||||
if (res.statusCode == 200) {
|
if (res.statusCode == 200) {
|
||||||
var result = Attachment.fromJson(
|
var result = Attachment.fromJson(
|
||||||
jsonDecode(utf8.decode(await res.stream.toBytes()))["info"],
|
jsonDecode(utf8.decode(await res.stream.toBytes()))['info'],
|
||||||
);
|
);
|
||||||
setState(() => _attachments.add(result));
|
setState(() => _attachments.add(result));
|
||||||
widget.onUpdate(_attachments);
|
widget.onUpdate(_attachments);
|
||||||
@ -252,7 +252,7 @@ class _AttachmentEditorState extends State<AttachmentEditor> {
|
|||||||
style: Theme.of(context).textTheme.titleMedium,
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
"${getFileType(element)} · ${formatBytes(element.filesize)}",
|
'${getFileType(element)} · ${formatBytes(element.filesize)}',
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -121,7 +121,7 @@ class CommentListHeader extends StatelessWidget {
|
|||||||
return TextButton(
|
return TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
final did = await router.pushNamed(
|
final did = await router.pushNamed(
|
||||||
"posts.comments.editor",
|
'posts.comments.editor',
|
||||||
extra: CommentPostArguments(related: related),
|
extra: CommentPostArguments(related: related),
|
||||||
);
|
);
|
||||||
if (did == true) paging.refresh();
|
if (did == true) paging.refresh();
|
||||||
|
@ -55,7 +55,7 @@ class ArticleContent extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
imageBuilder: (url, _, __) {
|
imageBuilder: (url, _, __) {
|
||||||
Uri uri;
|
Uri uri;
|
||||||
if (url.toString().startsWith("/api/attachments")) {
|
if (url.toString().startsWith('/api/attachments')) {
|
||||||
uri = getRequestUri('interactive', url.toString());
|
uri = getRequestUri('interactive', url.toString());
|
||||||
} else {
|
} else {
|
||||||
uri = url;
|
uri = url;
|
||||||
|
@ -30,7 +30,7 @@ class _AttachmentItemState extends State<AttachmentItem> {
|
|||||||
|
|
||||||
late final _videoPlayer = Player(
|
late final _videoPlayer = Player(
|
||||||
configuration: PlayerConfiguration(
|
configuration: PlayerConfiguration(
|
||||||
title: "Attachment #${getTag()}",
|
title: 'Attachment #${getTag()}',
|
||||||
logLevel: MPVLogLevel.error,
|
logLevel: MPVLogLevel.error,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -13,8 +13,8 @@ import 'package:timeago/timeago.dart' as timeago;
|
|||||||
|
|
||||||
class PostItem extends StatefulWidget {
|
class PostItem extends StatefulWidget {
|
||||||
final Post item;
|
final Post item;
|
||||||
final bool? brief;
|
final bool brief;
|
||||||
final bool? ripple;
|
final bool ripple;
|
||||||
final Function? onUpdate;
|
final Function? onUpdate;
|
||||||
final Function? onDelete;
|
final Function? onDelete;
|
||||||
final Function? onTap;
|
final Function? onTap;
|
||||||
@ -22,8 +22,8 @@ class PostItem extends StatefulWidget {
|
|||||||
const PostItem({
|
const PostItem({
|
||||||
super.key,
|
super.key,
|
||||||
required this.item,
|
required this.item,
|
||||||
this.brief,
|
this.brief = true,
|
||||||
this.ripple,
|
this.ripple = true,
|
||||||
this.onUpdate,
|
this.onUpdate,
|
||||||
this.onDelete,
|
this.onDelete,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
@ -79,9 +79,9 @@ class _PostItemState extends State<PostItem> {
|
|||||||
Widget renderContent() {
|
Widget renderContent() {
|
||||||
switch (widget.item.modelType) {
|
switch (widget.item.modelType) {
|
||||||
case 'article':
|
case 'article':
|
||||||
return ArticleContent(item: widget.item, brief: widget.brief ?? true);
|
return ArticleContent(item: widget.item, brief: widget.brief);
|
||||||
default:
|
default:
|
||||||
return MomentContent(item: widget.item, brief: widget.brief ?? true);
|
return MomentContent(item: widget.item, brief: widget.brief);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,7 +163,7 @@ class _PostItemState extends State<PostItem> {
|
|||||||
|
|
||||||
Widget content;
|
Widget content;
|
||||||
|
|
||||||
if (widget.brief ?? true) {
|
if (widget.brief) {
|
||||||
content = Padding(
|
content = Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -199,7 +199,7 @@ class _PostItemState extends State<PostItem> {
|
|||||||
content = Column(
|
content = Column(
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(left: 12, right: 12, top: 16),
|
padding: const EdgeInsets.only(left: 20, right: 20, top: 16),
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@ -230,17 +230,17 @@ class _PostItemState extends State<PostItem> {
|
|||||||
child: Divider(thickness: 0.3),
|
child: Divider(thickness: 0.3),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
|
||||||
child: renderContent(),
|
child: renderContent(),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: renderAttachments(),
|
child: renderAttachments(),
|
||||||
),
|
),
|
||||||
ClipRRect(
|
ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 2),
|
||||||
child: renderReactions(),
|
child: renderReactions(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -248,9 +248,7 @@ class _PostItemState extends State<PostItem> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final ripple = widget.ripple ?? true;
|
if (widget.ripple) {
|
||||||
|
|
||||||
if (ripple) {
|
|
||||||
return InkWell(
|
return InkWell(
|
||||||
child: content,
|
child: content,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
@ -134,8 +134,8 @@ class _ReactionActionPopupState extends State<ReactionActionPopup> {
|
|||||||
child: ListTile(
|
child: ListTile(
|
||||||
title: Text(info.value.icon),
|
title: Text(info.value.icon),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
":${info.key}:",
|
':${info.key}:',
|
||||||
style: const TextStyle(fontFamily: "monospace"),
|
style: const TextStyle(fontFamily: 'monospace'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user