forked from Nexus/nexus
Compare commits
8 commits
e1d7a30a06
...
309d0df581
| Author | SHA1 | Date | |
|---|---|---|---|
|
309d0df581 |
|||
|
3310d9b907 |
|||
|
8c047827de |
|||
|
d513e466fd |
|||
|
84a729aea8 |
|||
|
23e0aa3c4e |
|||
|
f0a26b58d1 |
|||
|
78b5abea7d |
10 changed files with 134 additions and 75 deletions
|
|
@ -108,8 +108,8 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend.
|
|||
- [x] Events
|
||||
- [ ] Rooms
|
||||
- [x] Member list
|
||||
- [x] Sort by power level
|
||||
- [ ] Colors based off of power level
|
||||
- [ ] Sort by power level
|
||||
- [ ] Notifications using UnifiedPush ([#35](https://git.federated.nexus/Nexus/nexus/issues/35))
|
||||
- [ ] Group calls using [MSC4195](https://github.com/matrix-org/matrix-spec-proposals/pull/4195)
|
||||
- [ ] Invites
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 466 KiB After Width: | Height: | Size: 616 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 462 KiB After Width: | Height: | Size: 616 KiB |
|
|
@ -2,5 +2,5 @@ import "package:color_hash/color_hash.dart";
|
|||
import "package:flutter/material.dart";
|
||||
|
||||
extension ToColor on String {
|
||||
Color get colorHash => ColorHash(this, lightness: .7, saturation: .7).color;
|
||||
Color get colorHash => ColorHash(this, lightness: .5, saturation: .7).color;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import "package:flutter/material.dart";
|
||||
import "package:nexus/widgets/divider_widget.dart";
|
||||
|
||||
class DividerText extends StatelessWidget {
|
||||
final String text;
|
||||
|
|
@ -6,24 +7,6 @@ class DividerText extends StatelessWidget {
|
|||
const DividerText(this.text, {super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => LayoutBuilder(
|
||||
builder: (context, constraints) => Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 16,
|
||||
child: Divider(color: Theme.of(context).colorScheme.onSurface),
|
||||
),
|
||||
ConstrainedBox(
|
||||
constraints: .new(maxWidth: constraints.maxWidth - 32),
|
||||
child: Padding(
|
||||
padding: const .all(8),
|
||||
child: Text(text, style: Theme.of(context).textTheme.labelLarge),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Divider(color: Theme.of(context).colorScheme.onSurface),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
Widget build(BuildContext context) =>
|
||||
DividerWidget(Text(text, style: Theme.of(context).textTheme.labelLarge));
|
||||
}
|
||||
|
|
|
|||
25
lib/widgets/divider_widget.dart
Normal file
25
lib/widgets/divider_widget.dart
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import "package:flutter/material.dart";
|
||||
|
||||
class DividerWidget extends StatelessWidget {
|
||||
final Widget widget;
|
||||
const DividerWidget(this.widget, {super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => LayoutBuilder(
|
||||
builder: (_, constraints) => Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 16,
|
||||
child: Divider(color: Theme.of(context).colorScheme.onSurface),
|
||||
),
|
||||
ConstrainedBox(
|
||||
constraints: .new(maxWidth: constraints.maxWidth - 32),
|
||||
child: Padding(padding: const .all(8), child: widget),
|
||||
),
|
||||
Expanded(
|
||||
child: Divider(color: Theme.of(context).colorScheme.onSurface),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -21,11 +21,12 @@ class ErrorDialog extends ConsumerWidget {
|
|||
onPressed: () => ref.invalidate(provider!),
|
||||
child: const Text("Try Again"),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () =>
|
||||
Navigator.of(context).popUntil((route) => route.isFirst),
|
||||
child: const Text("Go Back"),
|
||||
),
|
||||
if (Navigator.of(context).canPop())
|
||||
TextButton(
|
||||
onPressed: () =>
|
||||
Navigator.of(context).popUntil((route) => route.isFirst),
|
||||
child: const Text("Go Back"),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,36 +18,37 @@ class MessageImage extends ConsumerWidget {
|
|||
url.toString(),
|
||||
child: ClipRRect(
|
||||
borderRadius: .all(.circular(8)),
|
||||
child: Image(
|
||||
image: CachedNetworkImage(
|
||||
url.toString(),
|
||||
ref.watch(CrossCacheController.provider),
|
||||
headers: ref.headers,
|
||||
),
|
||||
width: info?.width,
|
||||
loadingBuilder: (_, child, loadingProgress) => loadingProgress == null
|
||||
? child
|
||||
: switch (info?.blurHash) {
|
||||
final blurHash? =>
|
||||
info?.width == null || info?.height == null
|
||||
? SizedBox(
|
||||
width: 200,
|
||||
height: 200,
|
||||
child: BlurHash(hash: blurHash),
|
||||
)
|
||||
: AspectRatio(
|
||||
aspectRatio: info!.width! / info!.height!,
|
||||
child: SizedBox(
|
||||
child: AspectRatio(
|
||||
aspectRatio: info!.width! / info!.height!,
|
||||
child: Image(
|
||||
image: CachedNetworkImage(
|
||||
url.toString(),
|
||||
ref.watch(CrossCacheController.provider),
|
||||
headers: ref.headers,
|
||||
),
|
||||
width: info?.width,
|
||||
fit: BoxFit.fitWidth,
|
||||
loadingBuilder: (_, child, loadingProgress) => loadingProgress == null
|
||||
? child
|
||||
: switch (info?.blurHash) {
|
||||
final blurHash? =>
|
||||
info?.width == null || info?.height == null
|
||||
? SizedBox(
|
||||
width: 200,
|
||||
height: 200,
|
||||
child: BlurHash(hash: blurHash),
|
||||
)
|
||||
: SizedBox(
|
||||
width: info!.width,
|
||||
child: BlurHash(hash: blurHash),
|
||||
),
|
||||
),
|
||||
_ => Loading(),
|
||||
},
|
||||
errorBuilder: (context, error, stackTrace) => Center(
|
||||
child: Text(
|
||||
"Image Failed to Load",
|
||||
style: .new(color: Theme.of(context).colorScheme.error),
|
||||
_ => Loading(),
|
||||
},
|
||||
errorBuilder: (context, error, stackTrace) => Center(
|
||||
child: Text(
|
||||
"Image Failed to Load",
|
||||
style: .new(color: Theme.of(context).colorScheme.error),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import "package:nexus/models/content/server_acl.dart";
|
|||
import "package:nexus/models/content/sticker.dart";
|
||||
import "package:nexus/models/content/topic.dart";
|
||||
import "package:nexus/models/event.dart";
|
||||
import "package:nexus/widgets/error_dialog.dart";
|
||||
import "package:nexus/widgets/lazy_loading/message_displayname.dart";
|
||||
import "package:nexus/widgets/renderers/message.dart";
|
||||
import "package:nexus/widgets/reaction_row.dart";
|
||||
|
|
@ -49,9 +50,13 @@ class EventRenderer extends ConsumerWidget {
|
|||
final child = event.redactedBy != null || event.relationType == "m.replace"
|
||||
? null
|
||||
: switch (event.content) {
|
||||
Content(:final parseError?) => SelectableText(
|
||||
"An error occurred while parsing this event:\n$parseError\n${parseError.stackTrace}",
|
||||
style: errorStyle,
|
||||
Content(:final parseError?) => Row(
|
||||
children: [
|
||||
ErrorDialog(
|
||||
"An error occurred while parsing event ${event.eventId}:\n$parseError",
|
||||
parseError.stackTrace,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
MessageContent() ||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import "package:nexus/controllers/key_controller.dart";
|
|||
import "package:nexus/controllers/spaces_controller.dart";
|
||||
import "package:nexus/models/room.dart";
|
||||
import "package:nexus/widgets/avatar_or_hash.dart";
|
||||
import "package:nexus/widgets/divider_widget.dart";
|
||||
import "package:nexus/widgets/join_dialog.dart";
|
||||
import "package:nexus/widgets/room_menu.dart";
|
||||
|
||||
|
|
@ -38,9 +39,11 @@ class Sidebar extends HookConsumerWidget {
|
|||
spaces.firstWhereOrNull((space) => space.id == selectedSpaceId) ??
|
||||
spaces.first;
|
||||
|
||||
final indexOfSelectedRoom = selectedSpace.children.indexWhere(
|
||||
(room) => room.metadata?.id == selectedRoomId,
|
||||
);
|
||||
final indexOfSelectedRoom = selectedSpace.children
|
||||
.addAll(
|
||||
selectedSpace.subSpaces.map((element) => element.children).flattened,
|
||||
)
|
||||
.indexWhere((room) => room.metadata?.id == selectedRoomId);
|
||||
final selectedRoomIndex = indexOfSelectedRoom == -1
|
||||
? null
|
||||
: indexOfSelectedRoom;
|
||||
|
|
@ -60,7 +63,6 @@ class Sidebar extends HookConsumerWidget {
|
|||
fallback: selectedSpaceId == "dms"
|
||||
? null
|
||||
: Icon(Icons.numbers),
|
||||
// space.client.headers,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
@ -98,17 +100,32 @@ class Sidebar extends HookConsumerWidget {
|
|||
destinations: spaces
|
||||
.map(
|
||||
(space) => NavigationRailM3EDestination(
|
||||
badgeCount: switch (space.children.fold(
|
||||
0,
|
||||
(previousValue, room) =>
|
||||
previousValue +
|
||||
(room.metadata?.unreadNotifications ?? 0),
|
||||
)) {
|
||||
badgeCount: switch (space.children
|
||||
.addAll(
|
||||
space.subSpaces
|
||||
.map((element) => element.children)
|
||||
.flattened,
|
||||
)
|
||||
.fold(
|
||||
0,
|
||||
(previousValue, room) =>
|
||||
previousValue +
|
||||
(room.metadata?.unreadNotifications ?? 0),
|
||||
)) {
|
||||
0 =>
|
||||
space.children.any(
|
||||
(room) =>
|
||||
room.metadata?.unreadMessages != 0,
|
||||
)
|
||||
space.children
|
||||
.addAll(
|
||||
space.subSpaces
|
||||
.map(
|
||||
(element) => element.children,
|
||||
)
|
||||
.flattened,
|
||||
)
|
||||
.any(
|
||||
(room) =>
|
||||
room.metadata?.unreadMessages !=
|
||||
0,
|
||||
)
|
||||
? 0
|
||||
: null,
|
||||
int badgeCount => badgeCount,
|
||||
|
|
@ -213,20 +230,47 @@ class Sidebar extends HookConsumerWidget {
|
|||
selectedIndex: selectedRoomIndex ?? 0,
|
||||
sections: [
|
||||
.new(
|
||||
header: selectedSpace.room == null ? null : Text("Rooms"),
|
||||
header: selectedSpace.room == null
|
||||
? null
|
||||
: DividerWidget(Text("Rooms")),
|
||||
destinations: roomsToDestinations(selectedSpace.children),
|
||||
),
|
||||
for (final subSpace in selectedSpace.subSpaces)
|
||||
.new(
|
||||
header: Text(
|
||||
subSpace.room.metadata?.name ?? "Unnamed Space",
|
||||
header: DividerWidget(
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
spacing: 8,
|
||||
children: [
|
||||
if (subSpace.room.metadata?.avatar != null)
|
||||
AvatarOrHash(
|
||||
subSpace.room.metadata?.avatar,
|
||||
subSpace.room.metadata?.name ??
|
||||
"Unnamed Room",
|
||||
height: 16,
|
||||
),
|
||||
Flexible(
|
||||
child: Text(
|
||||
subSpace.room.metadata?.name ??
|
||||
"Unnamed Space",
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
destinations: roomsToDestinations(subSpace.children),
|
||||
),
|
||||
],
|
||||
onDestinationSelected: (value) {
|
||||
final children = selectedSpace.children.addAll(
|
||||
selectedSpace.subSpaces
|
||||
.map((element) => element.children)
|
||||
.flattened,
|
||||
);
|
||||
selectedRoomIdNotifier.set(
|
||||
selectedSpace.children[value].metadata?.id,
|
||||
children[value].metadata?.id, //
|
||||
);
|
||||
if (!isDesktop) Navigator.of(context).pop();
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue