mirror of
https://gitlab.com/famedly/fluffychat.git
synced 2024-11-16 17:09:31 +01:00
design: Improve new design
This commit is contained in:
parent
82fe657e68
commit
692cb48aaf
@ -12,6 +12,7 @@ class ContentBanner extends StatelessWidget {
|
|||||||
final bool loading;
|
final bool loading;
|
||||||
final Function onEdit;
|
final Function onEdit;
|
||||||
final Client client;
|
final Client client;
|
||||||
|
final double opacity;
|
||||||
|
|
||||||
const ContentBanner(this.mxContent,
|
const ContentBanner(this.mxContent,
|
||||||
{this.height = 400,
|
{this.height = 400,
|
||||||
@ -19,6 +20,7 @@ class ContentBanner extends StatelessWidget {
|
|||||||
this.loading = false,
|
this.loading = false,
|
||||||
this.onEdit,
|
this.onEdit,
|
||||||
this.client,
|
this.client,
|
||||||
|
this.opacity = 0.75,
|
||||||
Key key})
|
Key key})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@ -35,7 +37,7 @@ class ContentBanner extends StatelessWidget {
|
|||||||
animated: true,
|
animated: true,
|
||||||
);
|
);
|
||||||
return Container(
|
return Container(
|
||||||
height: 300,
|
height: height,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context).secondaryHeaderColor,
|
color: Theme.of(context).secondaryHeaderColor,
|
||||||
@ -48,7 +50,7 @@ class ContentBanner extends StatelessWidget {
|
|||||||
top: 0,
|
top: 0,
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
child: Opacity(
|
child: Opacity(
|
||||||
opacity: 0.75,
|
opacity: opacity,
|
||||||
child: !loading
|
child: !loading
|
||||||
? mxContent != null
|
? mxContent != null
|
||||||
? CachedNetworkImage(
|
? CachedNetworkImage(
|
||||||
|
@ -16,7 +16,6 @@ import 'package:fluffychat/views/login.dart';
|
|||||||
import 'package:fluffychat/views/new_group.dart';
|
import 'package:fluffychat/views/new_group.dart';
|
||||||
import 'package:fluffychat/views/new_private_chat.dart';
|
import 'package:fluffychat/views/new_private_chat.dart';
|
||||||
import 'package:fluffychat/views/set_status_view.dart';
|
import 'package:fluffychat/views/set_status_view.dart';
|
||||||
import 'package:fluffychat/views/settings.dart';
|
|
||||||
import 'package:fluffychat/views/settings_3pid.dart';
|
import 'package:fluffychat/views/settings_3pid.dart';
|
||||||
import 'package:fluffychat/views/settings_devices.dart';
|
import 'package:fluffychat/views/settings_devices.dart';
|
||||||
import 'package:fluffychat/views/settings_emotes.dart';
|
import 'package:fluffychat/views/settings_emotes.dart';
|
||||||
@ -149,17 +148,17 @@ class FluffyRoutes {
|
|||||||
switch (action) {
|
switch (action) {
|
||||||
case '3pid':
|
case '3pid':
|
||||||
return ViewData(
|
return ViewData(
|
||||||
leftView: (_) => Settings(),
|
leftView: (_) => HomeView(),
|
||||||
mainView: (_) => Settings3Pid(),
|
mainView: (_) => Settings3Pid(),
|
||||||
);
|
);
|
||||||
case 'devices':
|
case 'devices':
|
||||||
return ViewData(
|
return ViewData(
|
||||||
leftView: (_) => Settings(),
|
leftView: (_) => HomeView(),
|
||||||
mainView: (_) => DevicesSettings(),
|
mainView: (_) => DevicesSettings(),
|
||||||
);
|
);
|
||||||
case 'emotes':
|
case 'emotes':
|
||||||
return ViewData(
|
return ViewData(
|
||||||
leftView: (_) => Settings(),
|
leftView: (_) => HomeView(),
|
||||||
mainView: (_) => EmotesSettings(
|
mainView: (_) => EmotesSettings(
|
||||||
room: ((settings.arguments ?? {}) as Map)['room'],
|
room: ((settings.arguments ?? {}) as Map)['room'],
|
||||||
stateKey: ((settings.arguments ?? {}) as Map)['stateKey'],
|
stateKey: ((settings.arguments ?? {}) as Map)['stateKey'],
|
||||||
@ -167,25 +166,25 @@ class FluffyRoutes {
|
|||||||
);
|
);
|
||||||
case 'ignore':
|
case 'ignore':
|
||||||
return ViewData(
|
return ViewData(
|
||||||
leftView: (_) => Settings(),
|
leftView: (_) => HomeView(),
|
||||||
mainView: (_) => SettingsIgnoreList(
|
mainView: (_) => SettingsIgnoreList(
|
||||||
initialUserId: settings.arguments,
|
initialUserId: settings.arguments,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
case 'notifications':
|
case 'notifications':
|
||||||
return ViewData(
|
return ViewData(
|
||||||
leftView: (_) => Settings(),
|
leftView: (_) => HomeView(),
|
||||||
mainView: (_) => SettingsNotifications(),
|
mainView: (_) => SettingsNotifications(),
|
||||||
);
|
);
|
||||||
case 'style':
|
case 'style':
|
||||||
return ViewData(
|
return ViewData(
|
||||||
leftView: (_) => Settings(),
|
leftView: (_) => HomeView(),
|
||||||
mainView: (_) => SettingsStyle(),
|
mainView: (_) => SettingsStyle(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ViewData(
|
return ViewData(
|
||||||
mainView: (_) => Settings(),
|
mainView: (_) => HomeView(),
|
||||||
emptyView: (_) => EmptyPage(),
|
emptyView: (_) => EmptyPage(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1025,8 +1025,13 @@
|
|||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
"newPrivateChat": "New private chat",
|
"noStatusesFound": "No statuses found so far.",
|
||||||
"@newPrivateChat": {
|
"@noStatusesFound": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"newChat": "New chat",
|
||||||
|
"@newChat": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
@ -1352,6 +1357,16 @@
|
|||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
|
"contacts": "Contacts",
|
||||||
|
"@contacts": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"all": "All",
|
||||||
|
"@all": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
"discover": "Discover",
|
"discover": "Discover",
|
||||||
"@discover": {
|
"@discover": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -571,14 +571,14 @@ class _ChatState extends State<Chat> {
|
|||||||
: Row(
|
: Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Icon(Icons.edit_outlined,
|
Icon(Icons.edit_outlined,
|
||||||
color: Theme.of(context).primaryColor,
|
color: Theme.of(context).accentColor,
|
||||||
size: 13),
|
size: 13),
|
||||||
SizedBox(width: 4),
|
SizedBox(width: 4),
|
||||||
Text(
|
Text(
|
||||||
room.getLocalizedTypingText(context),
|
room.getLocalizedTypingText(context),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context).primaryColor,
|
color: Theme.of(context).accentColor,
|
||||||
fontStyle: FontStyle.italic,
|
fontStyle: FontStyle.italic,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
),
|
),
|
||||||
|
@ -4,6 +4,7 @@ import 'dart:io';
|
|||||||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||||
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
|
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
|
||||||
import 'package:famedlysdk/famedlysdk.dart';
|
import 'package:famedlysdk/famedlysdk.dart';
|
||||||
|
import 'package:fluffychat/utils/fluffy_share.dart';
|
||||||
import 'package:fluffychat/views/home_view_parts/discover.dart';
|
import 'package:fluffychat/views/home_view_parts/discover.dart';
|
||||||
import 'package:fluffychat/views/share_view.dart';
|
import 'package:fluffychat/views/share_view.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
@ -11,12 +12,12 @@ import 'package:fluffychat/app_config.dart';
|
|||||||
import 'package:fluffychat/utils/platform_infos.dart';
|
import 'package:fluffychat/utils/platform_infos.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:preload_page_view/preload_page_view.dart';
|
|
||||||
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
|
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
|
||||||
import '../components/matrix.dart';
|
import '../components/matrix.dart';
|
||||||
import '../utils/matrix_file_extension.dart';
|
import '../utils/matrix_file_extension.dart';
|
||||||
import '../utils/url_launcher.dart';
|
import '../utils/url_launcher.dart';
|
||||||
import 'home_view_parts/chat_list.dart';
|
import 'home_view_parts/chat_list.dart';
|
||||||
|
import 'home_view_parts/settings.dart';
|
||||||
import 'home_view_parts/status_list.dart';
|
import 'home_view_parts/status_list.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
|
|
||||||
@ -31,13 +32,18 @@ class HomeView extends StatefulWidget {
|
|||||||
_HomeViewState createState() => _HomeViewState();
|
_HomeViewState createState() => _HomeViewState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _HomeViewState extends State<HomeView> {
|
class _HomeViewState extends State<HomeView> with TickerProviderStateMixin {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
_initReceiveSharingIntent();
|
_initReceiveSharingIntent();
|
||||||
|
_pageController = TabController(length: 4, vsync: this, initialIndex: 1);
|
||||||
|
_pageController.addListener(_updateCurrentIndex);
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _updateCurrentIndex() =>
|
||||||
|
setState(() => currentIndex = _pageController.index);
|
||||||
|
|
||||||
int currentIndex = 1;
|
int currentIndex = 1;
|
||||||
|
|
||||||
StreamSubscription _intentDataStreamSubscription;
|
StreamSubscription _intentDataStreamSubscription;
|
||||||
@ -48,8 +54,7 @@ class _HomeViewState extends State<HomeView> {
|
|||||||
|
|
||||||
AppBar appBar;
|
AppBar appBar;
|
||||||
|
|
||||||
final PreloadPageController _pageController =
|
TabController _pageController;
|
||||||
PreloadPageController(initialPage: 1);
|
|
||||||
|
|
||||||
void _onShare(Map<String, dynamic> content) {
|
void _onShare(Map<String, dynamic> content) {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
@ -114,6 +119,7 @@ class _HomeViewState extends State<HomeView> {
|
|||||||
void dispose() {
|
void dispose() {
|
||||||
_intentDataStreamSubscription?.cancel();
|
_intentDataStreamSubscription?.cancel();
|
||||||
_intentFileStreamSubscription?.cancel();
|
_intentFileStreamSubscription?.cancel();
|
||||||
|
_pageController.removeListener(_updateCurrentIndex);
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,9 +176,6 @@ class _HomeViewState extends State<HomeView> {
|
|||||||
fabIcon = Icons.add_outlined;
|
fabIcon = Icons.add_outlined;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
fabIcon = Icons.group_add_outlined;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
fabIcon = Icons.domain_outlined;
|
fabIcon = Icons.domain_outlined;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -182,33 +185,56 @@ class _HomeViewState extends State<HomeView> {
|
|||||||
AppBar(
|
AppBar(
|
||||||
centerTitle: false,
|
centerTitle: false,
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
PopupMenuButton(
|
||||||
icon: Icon(Icons.account_circle_outlined),
|
onSelected: (action) {
|
||||||
onPressed: () =>
|
switch (action) {
|
||||||
AdaptivePageLayout.of(context).pushNamed('/settings'),
|
case 'invite':
|
||||||
|
FluffyShare.share(
|
||||||
|
L10n.of(context).inviteText(
|
||||||
|
Matrix.of(context).client.userID,
|
||||||
|
'https://matrix.to/#/${Matrix.of(context).client.userID}'),
|
||||||
|
context);
|
||||||
|
break;
|
||||||
|
case 'archive':
|
||||||
|
AdaptivePageLayout.of(context).pushNamed('/archive');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
itemBuilder: (_) => [
|
||||||
|
PopupMenuItem(
|
||||||
|
value: 'invite',
|
||||||
|
child: Text(L10n.of(context).inviteContact),
|
||||||
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
value: 'archive',
|
||||||
|
child: Text(L10n.of(context).archive),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
title: Text(AppConfig.applicationName)),
|
title: Text(AppConfig.applicationName)),
|
||||||
body: PreloadPageView(
|
body: TabBarView(
|
||||||
controller: _pageController,
|
controller: _pageController,
|
||||||
onPageChanged: (i) => setState(() => currentIndex = i),
|
|
||||||
children: [
|
children: [
|
||||||
StatusList(key: Key('StatusList')),
|
StatusList(key: Key('StatusList')),
|
||||||
ChatList(
|
ChatList(
|
||||||
type: ChatListType.messages,
|
|
||||||
onCustomAppBar: (appBar) => setState(() => this.appBar = appBar),
|
|
||||||
),
|
|
||||||
ChatList(
|
|
||||||
type: ChatListType.groups,
|
|
||||||
onCustomAppBar: (appBar) => setState(() => this.appBar = appBar),
|
onCustomAppBar: (appBar) => setState(() => this.appBar = appBar),
|
||||||
),
|
),
|
||||||
Discover(server: _server),
|
Discover(server: _server),
|
||||||
|
Settings(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: fabIcon == null
|
||||||
child: Icon(fabIcon),
|
? null
|
||||||
onPressed: _onFabTab,
|
: FloatingActionButton(
|
||||||
),
|
child: Icon(fabIcon),
|
||||||
|
onPressed: _onFabTab,
|
||||||
|
foregroundColor:
|
||||||
|
currentIndex == 2 ? Theme.of(context).accentColor : null,
|
||||||
|
backgroundColor: currentIndex == 2
|
||||||
|
? Theme.of(context).scaffoldBackgroundColor
|
||||||
|
: null,
|
||||||
|
),
|
||||||
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
|
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
|
||||||
bottomNavigationBar: BottomNavigationBar(
|
bottomNavigationBar: BottomNavigationBar(
|
||||||
unselectedItemColor: Theme.of(context).textTheme.bodyText1.color,
|
unselectedItemColor: Theme.of(context).textTheme.bodyText1.color,
|
||||||
@ -219,11 +245,7 @@ class _HomeViewState extends State<HomeView> {
|
|||||||
elevation: 20,
|
elevation: 20,
|
||||||
backgroundColor: Theme.of(context).appBarTheme.color,
|
backgroundColor: Theme.of(context).appBarTheme.color,
|
||||||
onTap: (i) {
|
onTap: (i) {
|
||||||
_pageController.animateToPage(
|
_pageController.animateTo(i);
|
||||||
i,
|
|
||||||
duration: Duration(milliseconds: 200),
|
|
||||||
curve: Curves.bounceOut,
|
|
||||||
);
|
|
||||||
setState(() => currentIndex = i);
|
setState(() => currentIndex = i);
|
||||||
},
|
},
|
||||||
items: [
|
items: [
|
||||||
@ -235,14 +257,14 @@ class _HomeViewState extends State<HomeView> {
|
|||||||
label: L10n.of(context).messages,
|
label: L10n.of(context).messages,
|
||||||
icon: Icon(CupertinoIcons.chat_bubble_2),
|
icon: Icon(CupertinoIcons.chat_bubble_2),
|
||||||
),
|
),
|
||||||
BottomNavigationBarItem(
|
|
||||||
label: L10n.of(context).groups,
|
|
||||||
icon: Icon(Icons.people_outline),
|
|
||||||
),
|
|
||||||
BottomNavigationBarItem(
|
BottomNavigationBarItem(
|
||||||
label: L10n.of(context).discover,
|
label: L10n.of(context).discover,
|
||||||
icon: Icon(CupertinoIcons.search_circle),
|
icon: Icon(CupertinoIcons.search_circle),
|
||||||
),
|
),
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
label: L10n.of(context).settings,
|
||||||
|
icon: Icon(Icons.settings_outlined),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -8,19 +8,15 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
||||||
|
|
||||||
enum ChatListType { messages, groups, all }
|
|
||||||
|
|
||||||
enum SelectMode { normal, select }
|
enum SelectMode { normal, select }
|
||||||
|
|
||||||
class ChatList extends StatefulWidget {
|
class ChatList extends StatefulWidget {
|
||||||
final String activeChat;
|
final String activeChat;
|
||||||
final ChatListType type;
|
|
||||||
final void Function(AppBar appBar) onCustomAppBar;
|
final void Function(AppBar appBar) onCustomAppBar;
|
||||||
|
|
||||||
const ChatList({
|
const ChatList({
|
||||||
Key key,
|
Key key,
|
||||||
this.activeChat,
|
this.activeChat,
|
||||||
@required this.type,
|
|
||||||
this.onCustomAppBar,
|
this.onCustomAppBar,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
@override
|
@override
|
||||||
@ -175,11 +171,6 @@ class _ChatListState extends State<ChatList> {
|
|||||||
(searchMode &&
|
(searchMode &&
|
||||||
!room.displayname.toLowerCase().contains(
|
!room.displayname.toLowerCase().contains(
|
||||||
searchController.text.toLowerCase() ?? '')));
|
searchController.text.toLowerCase() ?? '')));
|
||||||
if (widget.type == ChatListType.messages) {
|
|
||||||
rooms.removeWhere((room) => !room.isDirectChat);
|
|
||||||
} else if (widget.type == ChatListType.groups) {
|
|
||||||
rooms.removeWhere((room) => room.isDirectChat);
|
|
||||||
}
|
|
||||||
if (rooms.isEmpty && (!searchMode)) {
|
if (rooms.isEmpty && (!searchMode)) {
|
||||||
return Column(
|
return Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
@ -196,6 +187,7 @@ class _ChatListState extends State<ChatList> {
|
|||||||
searchMode
|
searchMode
|
||||||
? L10n.of(context).noRoomsFound
|
? L10n.of(context).noRoomsFound
|
||||||
: L10n.of(context).startYourFirstChat,
|
: L10n.of(context).startYourFirstChat,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.grey,
|
color: Colors.grey,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
|
@ -19,11 +19,11 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
|||||||
import 'package:image_picker/image_picker.dart';
|
import 'package:image_picker/image_picker.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
import '../components/content_banner.dart';
|
import '../../components/content_banner.dart';
|
||||||
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
||||||
import '../components/matrix.dart';
|
import '../../components/matrix.dart';
|
||||||
import '../app_config.dart';
|
import '../../app_config.dart';
|
||||||
import '../config/setting_keys.dart';
|
import '../../config/setting_keys.dart';
|
||||||
|
|
||||||
class Settings extends StatefulWidget {
|
class Settings extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
@ -320,221 +320,197 @@ class _SettingsState extends State<Settings> {
|
|||||||
return c;
|
return c;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return Scaffold(
|
return ListView(
|
||||||
body: NestedScrollView(
|
children: <Widget>[
|
||||||
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) =>
|
ContentBanner(
|
||||||
<Widget>[
|
profile?.avatarUrl,
|
||||||
SliverAppBar(
|
height: 200,
|
||||||
leading: BackButton(),
|
opacity: 1,
|
||||||
expandedHeight: 300.0,
|
defaultIcon: Icons.account_circle_outlined,
|
||||||
floating: true,
|
loading: profile == null,
|
||||||
pinned: true,
|
onEdit: () => setAvatarAction(context),
|
||||||
backgroundColor: Theme.of(context).appBarTheme.color,
|
),
|
||||||
flexibleSpace: FlexibleSpaceBar(
|
ListTile(
|
||||||
title: Text(
|
title: Text(
|
||||||
L10n.of(context).settings,
|
L10n.of(context).notifications,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context)
|
color: Theme.of(context).primaryColor,
|
||||||
.appBarTheme
|
fontWeight: FontWeight.bold,
|
||||||
.textTheme
|
),
|
||||||
.headline6
|
),
|
||||||
.color),
|
),
|
||||||
),
|
ListTile(
|
||||||
background: ContentBanner(
|
trailing: Icon(Icons.notifications_outlined),
|
||||||
profile?.avatarUrl,
|
title: Text(L10n.of(context).notifications),
|
||||||
height: 300,
|
onTap: () => AdaptivePageLayout.of(context)
|
||||||
defaultIcon: Icons.account_circle_outlined,
|
.pushNamed('/settings/notifications'),
|
||||||
loading: profile == null,
|
),
|
||||||
onEdit: () => setAvatarAction(context),
|
Divider(thickness: 1),
|
||||||
|
ListTile(
|
||||||
|
title: Text(
|
||||||
|
L10n.of(context).chat,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).primaryColor,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Text(L10n.of(context).changeTheme),
|
||||||
|
onTap: () =>
|
||||||
|
AdaptivePageLayout.of(context).pushNamed('/settings/style'),
|
||||||
|
trailing: Icon(Icons.style_outlined),
|
||||||
|
),
|
||||||
|
SettingsSwitchListTile(
|
||||||
|
title: L10n.of(context).renderRichContent,
|
||||||
|
onChanged: (b) => AppConfig.renderHtml = b,
|
||||||
|
storeKey: SettingKeys.renderHtml,
|
||||||
|
defaultValue: AppConfig.renderHtml,
|
||||||
|
),
|
||||||
|
SettingsSwitchListTile(
|
||||||
|
title: L10n.of(context).hideRedactedEvents,
|
||||||
|
onChanged: (b) => AppConfig.hideRedactedEvents = b,
|
||||||
|
storeKey: SettingKeys.hideRedactedEvents,
|
||||||
|
defaultValue: AppConfig.hideRedactedEvents,
|
||||||
|
),
|
||||||
|
SettingsSwitchListTile(
|
||||||
|
title: L10n.of(context).hideUnknownEvents,
|
||||||
|
onChanged: (b) => AppConfig.hideUnknownEvents = b,
|
||||||
|
storeKey: SettingKeys.hideUnknownEvents,
|
||||||
|
defaultValue: AppConfig.hideUnknownEvents,
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Text(L10n.of(context).emoteSettings),
|
||||||
|
onTap: () =>
|
||||||
|
AdaptivePageLayout.of(context).pushNamed('/settings/emotes'),
|
||||||
|
trailing: Icon(Icons.insert_emoticon_outlined),
|
||||||
|
),
|
||||||
|
Divider(thickness: 1),
|
||||||
|
ListTile(
|
||||||
|
title: Text(
|
||||||
|
L10n.of(context).account,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).primaryColor,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
trailing: Icon(Icons.edit_outlined),
|
||||||
|
title: Text(L10n.of(context).editDisplayname),
|
||||||
|
subtitle: Text(profile?.displayname ?? client.userID.localpart),
|
||||||
|
onTap: () => setDisplaynameAction(context),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
trailing: Icon(Icons.phone_outlined),
|
||||||
|
title: Text(L10n.of(context).editJitsiInstance),
|
||||||
|
subtitle: Text(AppConfig.jitsiInstance),
|
||||||
|
onTap: () => setJitsiInstanceAction(context),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
trailing: Icon(Icons.devices_other_outlined),
|
||||||
|
title: Text(L10n.of(context).devices),
|
||||||
|
onTap: () =>
|
||||||
|
AdaptivePageLayout.of(context).pushNamed('/settings/devices'),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
trailing: Icon(Icons.block_outlined),
|
||||||
|
title: Text(L10n.of(context).ignoredUsers),
|
||||||
|
onTap: () =>
|
||||||
|
AdaptivePageLayout.of(context).pushNamed('/settings/ignore'),
|
||||||
|
),
|
||||||
|
SentrySwitchListTile(),
|
||||||
|
Divider(thickness: 1),
|
||||||
|
ListTile(
|
||||||
|
trailing: Icon(Icons.security_outlined),
|
||||||
|
title: Text(
|
||||||
|
L10n.of(context).changePassword,
|
||||||
|
),
|
||||||
|
onTap: () => _changePasswordAccountAction(context),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
trailing: Icon(Icons.email_outlined),
|
||||||
|
title: Text(L10n.of(context).passwordRecovery),
|
||||||
|
onTap: () =>
|
||||||
|
AdaptivePageLayout.of(context).pushNamed('/settings/3pid'),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
trailing: Icon(Icons.exit_to_app_outlined),
|
||||||
|
title: Text(L10n.of(context).logout),
|
||||||
|
onTap: () => logoutAction(context),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
trailing: Icon(Icons.delete_forever_outlined),
|
||||||
|
title: Text(
|
||||||
|
L10n.of(context).deleteAccount,
|
||||||
|
style: TextStyle(color: Colors.red),
|
||||||
|
),
|
||||||
|
onTap: () => _deleteAccountAction(context),
|
||||||
|
),
|
||||||
|
if (client.encryption != null) ...{
|
||||||
|
Divider(thickness: 1),
|
||||||
|
ListTile(
|
||||||
|
title: Text(
|
||||||
|
L10n.of(context).security,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).primaryColor,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
if (PlatformInfos.isMobile)
|
||||||
body: ListView(
|
|
||||||
children: <Widget>[
|
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(
|
trailing: Icon(Icons.lock_outlined),
|
||||||
L10n.of(context).notifications,
|
title: Text(L10n.of(context).appLock),
|
||||||
style: TextStyle(
|
onTap: () => _setAppLockAction(context),
|
||||||
color: Theme.of(context).primaryColor,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
trailing: Icon(Icons.notifications_outlined),
|
title: Text(L10n.of(context).yourPublicKey),
|
||||||
title: Text(L10n.of(context).notifications),
|
onTap: () => showOkAlertDialog(
|
||||||
onTap: () => AdaptivePageLayout.of(context)
|
context: context,
|
||||||
.pushNamed('/settings/notifications'),
|
title: L10n.of(context).yourPublicKey,
|
||||||
|
message: client.fingerprintKey.beautified,
|
||||||
),
|
),
|
||||||
Divider(thickness: 1),
|
trailing: Icon(Icons.vpn_key_outlined),
|
||||||
ListTile(
|
),
|
||||||
title: Text(
|
ListTile(
|
||||||
L10n.of(context).chat,
|
title: Text(L10n.of(context).cachedKeys),
|
||||||
style: TextStyle(
|
trailing: Icon(Icons.wb_cloudy_outlined),
|
||||||
color: Theme.of(context).primaryColor,
|
subtitle: Text(
|
||||||
fontWeight: FontWeight.bold,
|
'${client.encryption.keyManager.enabled ? L10n.of(context).onlineKeyBackupEnabled : L10n.of(context).onlineKeyBackupDisabled}\n${client.encryption.crossSigning.enabled ? L10n.of(context).crossSigningEnabled : L10n.of(context).crossSigningDisabled}'),
|
||||||
),
|
onTap: () => BootstrapDialog(
|
||||||
),
|
l10n: L10n.of(context),
|
||||||
|
client: Matrix.of(context).client,
|
||||||
|
).show(context),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
Divider(thickness: 1),
|
||||||
|
ListTile(
|
||||||
|
title: Text(
|
||||||
|
L10n.of(context).about,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).primaryColor,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
ListTile(
|
),
|
||||||
title: Text(L10n.of(context).changeTheme),
|
onTap: () => AdaptivePageLayout.of(context).pushNamed('/logs'),
|
||||||
onTap: () =>
|
|
||||||
AdaptivePageLayout.of(context).pushNamed('/settings/style'),
|
|
||||||
trailing: Icon(Icons.style_outlined),
|
|
||||||
),
|
|
||||||
SettingsSwitchListTile(
|
|
||||||
title: L10n.of(context).renderRichContent,
|
|
||||||
onChanged: (b) => AppConfig.renderHtml = b,
|
|
||||||
storeKey: SettingKeys.renderHtml,
|
|
||||||
defaultValue: AppConfig.renderHtml,
|
|
||||||
),
|
|
||||||
SettingsSwitchListTile(
|
|
||||||
title: L10n.of(context).hideRedactedEvents,
|
|
||||||
onChanged: (b) => AppConfig.hideRedactedEvents = b,
|
|
||||||
storeKey: SettingKeys.hideRedactedEvents,
|
|
||||||
defaultValue: AppConfig.hideRedactedEvents,
|
|
||||||
),
|
|
||||||
SettingsSwitchListTile(
|
|
||||||
title: L10n.of(context).hideUnknownEvents,
|
|
||||||
onChanged: (b) => AppConfig.hideUnknownEvents = b,
|
|
||||||
storeKey: SettingKeys.hideUnknownEvents,
|
|
||||||
defaultValue: AppConfig.hideUnknownEvents,
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
title: Text(L10n.of(context).emoteSettings),
|
|
||||||
onTap: () =>
|
|
||||||
AdaptivePageLayout.of(context).pushNamed('/settings/emotes'),
|
|
||||||
trailing: Icon(Icons.insert_emoticon_outlined),
|
|
||||||
),
|
|
||||||
Divider(thickness: 1),
|
|
||||||
ListTile(
|
|
||||||
title: Text(
|
|
||||||
L10n.of(context).account,
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(context).primaryColor,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
trailing: Icon(Icons.edit_outlined),
|
|
||||||
title: Text(L10n.of(context).editDisplayname),
|
|
||||||
subtitle: Text(profile?.displayname ?? client.userID.localpart),
|
|
||||||
onTap: () => setDisplaynameAction(context),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
trailing: Icon(Icons.phone_outlined),
|
|
||||||
title: Text(L10n.of(context).editJitsiInstance),
|
|
||||||
subtitle: Text(AppConfig.jitsiInstance),
|
|
||||||
onTap: () => setJitsiInstanceAction(context),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
trailing: Icon(Icons.devices_other_outlined),
|
|
||||||
title: Text(L10n.of(context).devices),
|
|
||||||
onTap: () =>
|
|
||||||
AdaptivePageLayout.of(context).pushNamed('/settings/devices'),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
trailing: Icon(Icons.block_outlined),
|
|
||||||
title: Text(L10n.of(context).ignoredUsers),
|
|
||||||
onTap: () =>
|
|
||||||
AdaptivePageLayout.of(context).pushNamed('/settings/ignore'),
|
|
||||||
),
|
|
||||||
SentrySwitchListTile(),
|
|
||||||
Divider(thickness: 1),
|
|
||||||
ListTile(
|
|
||||||
trailing: Icon(Icons.security_outlined),
|
|
||||||
title: Text(
|
|
||||||
L10n.of(context).changePassword,
|
|
||||||
),
|
|
||||||
onTap: () => _changePasswordAccountAction(context),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
trailing: Icon(Icons.email_outlined),
|
|
||||||
title: Text(L10n.of(context).passwordRecovery),
|
|
||||||
onTap: () =>
|
|
||||||
AdaptivePageLayout.of(context).pushNamed('/settings/3pid'),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
trailing: Icon(Icons.exit_to_app_outlined),
|
|
||||||
title: Text(L10n.of(context).logout),
|
|
||||||
onTap: () => logoutAction(context),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
trailing: Icon(Icons.delete_forever_outlined),
|
|
||||||
title: Text(
|
|
||||||
L10n.of(context).deleteAccount,
|
|
||||||
style: TextStyle(color: Colors.red),
|
|
||||||
),
|
|
||||||
onTap: () => _deleteAccountAction(context),
|
|
||||||
),
|
|
||||||
if (client.encryption != null) ...{
|
|
||||||
Divider(thickness: 1),
|
|
||||||
ListTile(
|
|
||||||
title: Text(
|
|
||||||
L10n.of(context).security,
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(context).primaryColor,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (PlatformInfos.isMobile)
|
|
||||||
ListTile(
|
|
||||||
trailing: Icon(Icons.lock_outlined),
|
|
||||||
title: Text(L10n.of(context).appLock),
|
|
||||||
onTap: () => _setAppLockAction(context),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
title: Text(L10n.of(context).yourPublicKey),
|
|
||||||
onTap: () => showOkAlertDialog(
|
|
||||||
context: context,
|
|
||||||
title: L10n.of(context).yourPublicKey,
|
|
||||||
message: client.fingerprintKey.beautified,
|
|
||||||
),
|
|
||||||
trailing: Icon(Icons.vpn_key_outlined),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
title: Text(L10n.of(context).cachedKeys),
|
|
||||||
trailing: Icon(Icons.wb_cloudy_outlined),
|
|
||||||
subtitle: Text(
|
|
||||||
'${client.encryption.keyManager.enabled ? L10n.of(context).onlineKeyBackupEnabled : L10n.of(context).onlineKeyBackupDisabled}\n${client.encryption.crossSigning.enabled ? L10n.of(context).crossSigningEnabled : L10n.of(context).crossSigningDisabled}'),
|
|
||||||
onTap: () => BootstrapDialog(
|
|
||||||
l10n: L10n.of(context),
|
|
||||||
client: Matrix.of(context).client,
|
|
||||||
).show(context),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
Divider(thickness: 1),
|
|
||||||
ListTile(
|
|
||||||
title: Text(
|
|
||||||
L10n.of(context).about,
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(context).primaryColor,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onTap: () => AdaptivePageLayout.of(context).pushNamed('/logs'),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
trailing: Icon(Icons.help_outlined),
|
|
||||||
title: Text(L10n.of(context).help),
|
|
||||||
onTap: () => launch(AppConfig.supportUrl),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
trailing: Icon(Icons.privacy_tip_outlined),
|
|
||||||
title: Text(L10n.of(context).privacy),
|
|
||||||
onTap: () => launch(AppConfig.privacyUrl),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
trailing: Icon(Icons.link_outlined),
|
|
||||||
title: Text(L10n.of(context).about),
|
|
||||||
onTap: () => PlatformInfos.showDialog(context),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
ListTile(
|
||||||
|
trailing: Icon(Icons.help_outlined),
|
||||||
|
title: Text(L10n.of(context).help),
|
||||||
|
onTap: () => launch(AppConfig.supportUrl),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
trailing: Icon(Icons.privacy_tip_outlined),
|
||||||
|
title: Text(L10n.of(context).privacy),
|
||||||
|
onTap: () => launch(AppConfig.privacyUrl),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
trailing: Icon(Icons.link_outlined),
|
||||||
|
title: Text(L10n.of(context).about),
|
||||||
|
onTap: () => PlatformInfos.showDialog(context),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,6 +2,7 @@ import 'package:fluffychat/components/list_items/status_list_tile.dart';
|
|||||||
import 'package:fluffychat/components/matrix.dart';
|
import 'package:fluffychat/components/matrix.dart';
|
||||||
import 'package:fluffychat/utils/status.dart';
|
import 'package:fluffychat/utils/status.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
|
|
||||||
class StatusList extends StatefulWidget {
|
class StatusList extends StatefulWidget {
|
||||||
const StatusList({Key key}) : super(key: key);
|
const StatusList({Key key}) : super(key: key);
|
||||||
@ -22,7 +23,7 @@ class _StatusListState extends State<StatusList> {
|
|||||||
elevation: _onlyContacts ? 7 : null,
|
elevation: _onlyContacts ? 7 : null,
|
||||||
color: !_onlyContacts ? null : Theme.of(context).primaryColor,
|
color: !_onlyContacts ? null : Theme.of(context).primaryColor,
|
||||||
child: Text(
|
child: Text(
|
||||||
'Contacts',
|
L10n.of(context).contacts,
|
||||||
style: TextStyle(color: _onlyContacts ? Colors.white : null),
|
style: TextStyle(color: _onlyContacts ? Colors.white : null),
|
||||||
),
|
),
|
||||||
onPressed: () => setState(() => _onlyContacts = true),
|
onPressed: () => setState(() => _onlyContacts = true),
|
||||||
@ -31,7 +32,7 @@ class _StatusListState extends State<StatusList> {
|
|||||||
elevation: !_onlyContacts ? 7 : null,
|
elevation: !_onlyContacts ? 7 : null,
|
||||||
color: _onlyContacts ? null : Theme.of(context).primaryColor,
|
color: _onlyContacts ? null : Theme.of(context).primaryColor,
|
||||||
child: Text(
|
child: Text(
|
||||||
'All',
|
L10n.of(context).all,
|
||||||
style: TextStyle(color: !_onlyContacts ? Colors.white : null),
|
style: TextStyle(color: !_onlyContacts ? Colors.white : null),
|
||||||
),
|
),
|
||||||
onPressed: () => setState(() => _onlyContacts = false),
|
onPressed: () => setState(() => _onlyContacts = false),
|
||||||
@ -53,6 +54,16 @@ class _StatusListState extends State<StatusList> {
|
|||||||
statuses.removeWhere(
|
statuses.removeWhere(
|
||||||
(p) => client.getDirectChatFromUserId(p.senderId) == null);
|
(p) => client.getDirectChatFromUserId(p.senderId) == null);
|
||||||
}
|
}
|
||||||
|
if (statuses.isEmpty) {
|
||||||
|
return Container(
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Text(
|
||||||
|
L10n.of(context).noStatusesFound,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
return ListView.separated(
|
return ListView.separated(
|
||||||
physics: NeverScrollableScrollPhysics(),
|
physics: NeverScrollableScrollPhysics(),
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
|
@ -87,8 +87,18 @@ class _NewPrivateChatState extends State<NewPrivateChat> {
|
|||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
leading: BackButton(),
|
leading: BackButton(),
|
||||||
title: Text(L10n.of(context).newPrivateChat),
|
title: Text(L10n.of(context).newChat),
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
|
actions: [
|
||||||
|
FlatButton(
|
||||||
|
child: Text(
|
||||||
|
L10n.of(context).createNewGroup,
|
||||||
|
style: TextStyle(color: Theme.of(context).accentColor),
|
||||||
|
),
|
||||||
|
onPressed: () => AdaptivePageLayout.of(context)
|
||||||
|
.pushNamedAndRemoveUntilIsFirst('/newgroup'),
|
||||||
|
)
|
||||||
|
],
|
||||||
),
|
),
|
||||||
body: Column(
|
body: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
|
@ -19,9 +19,7 @@ class ShareView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
title: Text(L10n.of(context).share),
|
title: Text(L10n.of(context).share),
|
||||||
),
|
),
|
||||||
body: ChatList(
|
body: ChatList(),
|
||||||
type: ChatListType.all,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -872,13 +872,6 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.0-nullsafety.2"
|
version: "1.5.0-nullsafety.2"
|
||||||
preload_page_view:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: preload_page_view
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "0.1.4"
|
|
||||||
process:
|
process:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -31,7 +31,6 @@ dependencies:
|
|||||||
adaptive_page_layout: ^0.1.6
|
adaptive_page_layout: ^0.1.6
|
||||||
provider: ^4.3.3
|
provider: ^4.3.3
|
||||||
adaptive_theme: ^1.1.0
|
adaptive_theme: ^1.1.0
|
||||||
preload_page_view: ^0.1.4
|
|
||||||
# desktop_notifications: ^0.0.0-dev.4 // Currently blocked by: https://github.com/canonical/desktop_notifications.dart/issues/5
|
# desktop_notifications: ^0.0.0-dev.4 // Currently blocked by: https://github.com/canonical/desktop_notifications.dart/issues/5
|
||||||
matrix_link_text: ^0.3.2
|
matrix_link_text: ^0.3.2
|
||||||
path_provider: ^1.6.27
|
path_provider: ^1.6.27
|
||||||
|
Loading…
Reference in New Issue
Block a user