Merge branch 'feature/new-ui' into feature/image-detail

This commit is contained in:
Salvatore Giordano
2020-11-26 17:17:24 +01:00
committed by GitHub
134 changed files with 3100 additions and 2585 deletions
File diff suppressed because one or more lines are too long
+19 -13
View File
@@ -108,8 +108,6 @@ PODS:
- GoogleUtilities/Logger
- image_picker (0.0.1):
- Flutter
- media_gallery (0.0.1):
- Flutter
- nanopb (1.30906.0):
- nanopb/decode (= 1.30906.0)
- nanopb/encode (= 1.30906.0)
@@ -119,11 +117,13 @@ PODS:
- Flutter
- "permission_handler (5.0.1+1)":
- Flutter
- photo_manager (0.0.1):
- Flutter
- PromisesObjC (1.2.11)
- Protobuf (3.13.0)
- SDWebImage (5.9.4):
- SDWebImage/Core (= 5.9.4)
- SDWebImage/Core (5.9.4)
- SDWebImage (5.10.0):
- SDWebImage/Core (= 5.10.0)
- SDWebImage/Core (5.10.0)
- shared_preferences (0.0.1):
- Flutter
- sqflite (0.0.2):
@@ -148,11 +148,13 @@ PODS:
- sqlite3/perf-threadsafe
- sqlite3/rtree
- Starscream (4.0.4)
- StreamChatClient (2.4.1):
- StreamChatClient (2.4.2):
- Starscream (~> 4.0)
- SwiftyGif (5.3.0)
- url_launcher (0.0.1):
- Flutter
- video_compress (0.3.0):
- Flutter
- video_player (0.0.1):
- Flutter
- wakelock (0.0.1):
@@ -169,14 +171,15 @@ DEPENDENCIES:
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
- image_picker (from `.symlinks/plugins/image_picker/ios`)
- media_gallery (from `.symlinks/plugins/media_gallery/ios`)
- path_provider (from `.symlinks/plugins/path_provider/ios`)
- permission_handler (from `.symlinks/plugins/permission_handler/ios`)
- photo_manager (from `.symlinks/plugins/photo_manager/ios`)
- shared_preferences (from `.symlinks/plugins/shared_preferences/ios`)
- sqflite (from `.symlinks/plugins/sqflite/ios`)
- sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/ios`)
- StreamChatClient
- url_launcher (from `.symlinks/plugins/url_launcher/ios`)
- video_compress (from `.symlinks/plugins/video_compress/ios`)
- video_player (from `.symlinks/plugins/video_player/ios`)
- wakelock (from `.symlinks/plugins/wakelock/ios`)
@@ -223,12 +226,12 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/flutter_secure_storage/ios"
image_picker:
:path: ".symlinks/plugins/image_picker/ios"
media_gallery:
:path: ".symlinks/plugins/media_gallery/ios"
path_provider:
:path: ".symlinks/plugins/path_provider/ios"
permission_handler:
:path: ".symlinks/plugins/permission_handler/ios"
photo_manager:
:path: ".symlinks/plugins/photo_manager/ios"
shared_preferences:
:path: ".symlinks/plugins/shared_preferences/ios"
sqflite:
@@ -237,6 +240,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/sqlite3_flutter_libs/ios"
url_launcher:
:path: ".symlinks/plugins/url_launcher/ios"
video_compress:
:path: ".symlinks/plugins/video_compress/ios"
video_player:
:path: ".symlinks/plugins/video_player/ios"
wakelock:
@@ -264,24 +269,25 @@ SPEC CHECKSUMS:
GoogleDataTransport: f56af7caa4ed338dc8e138a5d7c5973e66440833
GoogleUtilities: 7f2f5a07f888cdb145101d6042bc4422f57e70b3
image_picker: 9c3312491f862b28d21ecd8fdf0ee14e601b3f09
media_gallery: 834a04455a476b975c280aa1f24ed2853dfe36de
nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc
path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c
permission_handler: eac8e15b4a1a3fba55b761d19f3f4e6b005d15b6
photo_manager: f7c619c2cc8c2adb8d85c63363babac477de9c67
PromisesObjC: 8c196f5a328c2cba3e74624585467a557dcb482f
Protobuf: 3dac39b34a08151c6d949560efe3f86134a3f748
SDWebImage: b69257f4ab14e9b6a2ef53e910fdf914d8f757c1
SDWebImage: 9169792e9eec3e45bba2a0c02f74bf8bd922d1ee
shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
sqlite3: 8f7d2078ae27778699a622a94b853285793422a2
sqlite3_flutter_libs: 5651f8ff48e3b44d910863c4ea5916085b1b245f
Starscream: 5178aed56b316f13fa3bc55694e583d35dd414d9
StreamChatClient: d061cf52babcd9df930aee9cf864ab0379427eff
StreamChatClient: fc6419fa7ceda9c048188bb7c8e9f07755ced05f
SwiftyGif: e466e86c660d343357ab944a819a101c4127cb40
url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef
video_compress: fce97e4fb1dfd88175aa07d2ffc8a2f297f87fbe
video_player: 9cc823b1d9da7e8427ee591e8438bfbcde500e6e
wakelock: bfc7955c418d0db797614075aabbc58a39ab5107
PODFILE CHECKSUM: eb001256612a59f8f9e4d083ad8b9671e69dd184
COCOAPODS: 1.10.0.rc.1
COCOAPODS: 1.10.0
+4 -2
View File
@@ -327,14 +327,15 @@
"${BUILT_PRODUCTS_DIR}/flutter_local_notifications/flutter_local_notifications.framework",
"${BUILT_PRODUCTS_DIR}/flutter_secure_storage/flutter_secure_storage.framework",
"${BUILT_PRODUCTS_DIR}/image_picker/image_picker.framework",
"${BUILT_PRODUCTS_DIR}/media_gallery/media_gallery.framework",
"${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework",
"${BUILT_PRODUCTS_DIR}/path_provider/path_provider.framework",
"${BUILT_PRODUCTS_DIR}/photo_manager/photo_manager.framework",
"${BUILT_PRODUCTS_DIR}/shared_preferences/shared_preferences.framework",
"${BUILT_PRODUCTS_DIR}/sqflite/sqflite.framework",
"${BUILT_PRODUCTS_DIR}/sqlite3/sqlite3.framework",
"${BUILT_PRODUCTS_DIR}/sqlite3_flutter_libs/sqlite3_flutter_libs.framework",
"${BUILT_PRODUCTS_DIR}/url_launcher/url_launcher.framework",
"${BUILT_PRODUCTS_DIR}/video_compress/video_compress.framework",
"${BUILT_PRODUCTS_DIR}/video_player/video_player.framework",
"${BUILT_PRODUCTS_DIR}/wakelock/wakelock.framework",
);
@@ -358,14 +359,15 @@
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_local_notifications.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_secure_storage.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/image_picker.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/media_gallery.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/photo_manager.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqflite.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqlite3.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqlite3_flutter_libs.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/video_compress.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/video_player.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/wakelock.framework",
);
+17 -2
View File
@@ -7,6 +7,8 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'main.dart';
import 'notifications_service.dart';
import 'choose_user_page.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
class AdvancedOptionsPage extends StatefulWidget {
@override
@@ -44,8 +46,7 @@ class _AdvancedOptionsPageState extends State<AdvancedOptionsPage> {
),
),
leading: IconButton(
icon: Icon(
StreamIcons.left,
icon: StreamSvgIcon.left(
color: Colors.black,
),
onPressed: () {
@@ -286,6 +287,20 @@ class _AdvancedOptionsPageState extends State<AdvancedOptionsPage> {
if (!kIsWeb) {
initNotifications(client);
}
final secureStorage = FlutterSecureStorage();
secureStorage.write(
key: kStreamApiKey,
value: apiKey,
);
secureStorage.write(
key: kStreamUserId,
value: userId,
);
secureStorage.write(
key: kStreamToken,
value: userToken,
);
} catch (e) {
var errorText = 'Error connecting, retry';
if (e is Map) {
+14 -19
View File
@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/stream_icons.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
typedef ChipBuilder<T> = Widget Function(BuildContext context, T chip);
typedef OnChipAdded<T> = void Function(T chip);
@@ -43,7 +43,6 @@ class ChipInputTextFieldState<T> extends State<ChipsInputTextField<T>> {
_chips.remove(item);
if (_chips.isEmpty) resumeItemAddition();
});
if (widget.focusNode != null) widget.focusNode.requestFocus();
if (widget.onChipRemoved != null) widget.onChipRemoved(item);
}
@@ -51,25 +50,20 @@ class ChipInputTextFieldState<T> extends State<ChipsInputTextField<T>> {
if (!_pauseItemAddition) {
setState(() => _pauseItemAddition = true);
}
widget.focusNode?.unfocus();
}
void resumeItemAddition() {
if (_pauseItemAddition) {
setState(() => _pauseItemAddition = false);
}
widget.focusNode?.requestFocus();
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: _pauseItemAddition
? () {
setState(() {
_pauseItemAddition = false;
widget.focusNode?.requestFocus();
});
}
: null,
onTap: _pauseItemAddition ? resumeItemAddition : null,
child: Material(
elevation: 1,
color: Colors.white,
@@ -130,15 +124,16 @@ class ChipInputTextFieldState<T> extends State<ChipsInputTextField<T>> {
Align(
alignment: Alignment.bottomCenter,
child: IconButton(
icon: Icon(
_chips.isEmpty
? StreamIcons.user
: StreamIcons.user_add,
color: Colors.black.withOpacity(0.5),
size: 24,
),
onPressed:
!_pauseItemAddition ? null : resumeItemAddition,
icon: _chips.isEmpty
? StreamSvgIcon.user(
color: Colors.black.withOpacity(0.5),
size: 24,
)
: StreamSvgIcon.userAdd(
color: Colors.black.withOpacity(0.5),
size: 24,
),
onPressed: resumeItemAddition,
alignment: Alignment.topRight,
visualDensity: VisualDensity.compact,
padding: const EdgeInsets.all(0),
+8 -20
View File
@@ -8,6 +8,7 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'notifications_service.dart';
import 'routes/routes.dart';
const kStreamApiKey = 'STREAM_API_KEY';
const kStreamUserId = 'STREAM_USER_ID';
@@ -132,7 +133,7 @@ class ChooseUserPage extends StatelessWidget {
final secureStorage = FlutterSecureStorage();
final client = StreamChat.of(context).client;
client.apiKey = kDefaultStreamApiKey;
await client.setUser(
user,
token,
@@ -154,18 +155,11 @@ class ChooseUserPage extends StatelessWidget {
if (!kIsWeb) {
initNotifications(client);
}
Navigator.pop(context);
await Navigator.pushReplacement(
Navigator.pushNamedAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) {
return StreamChat(
client: client,
child: ChannelListPage(),
);
},
),
Routes.CHANNEL_LIST,
ModalRoute.withName(Routes.CHANNEL_LIST),
arguments: client,
);
},
leading: UserAvatar(
@@ -188,16 +182,10 @@ class ChooseUserPage extends StatelessWidget {
}),
ListTile(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AdvancedOptionsPage(),
),
);
Navigator.pushNamed(context, Routes.ADVANCED_OPTIONS);
},
leading: CircleAvatar(
child: Icon(
StreamIcons.settings,
child: StreamSvgIcon.settings(
color: Colors.black,
),
backgroundColor:
+152 -146
View File
@@ -3,7 +3,7 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:uuid/uuid.dart';
import 'main.dart';
import 'neumorphic_button.dart';
import 'routes/routes.dart';
class GroupChatDetailsScreen extends StatefulWidget {
final List<User> selectedUsers;
@@ -53,167 +53,173 @@ class _GroupChatDetailsScreenState extends State<GroupChatDetailsScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color.fromRGBO(252, 252, 252, 1),
appBar: AppBar(
elevation: 1,
backgroundColor: Colors.white,
leading: const StreamBackButton(),
title: Text(
'Name of Group Chat',
style: TextStyle(
color: Colors.black,
fontSize: 16,
return WillPopScope(
onWillPop: () async {
Navigator.pop(context, _selectedUsers);
return false;
},
child: Scaffold(
backgroundColor: Color.fromRGBO(252, 252, 252, 1),
appBar: AppBar(
elevation: 1,
backgroundColor: Colors.white,
leading: const StreamBackButton(),
title: Text(
'Name of Group Chat',
style: TextStyle(
color: Colors.black,
fontSize: 16,
),
),
),
centerTitle: true,
bottom: PreferredSize(
preferredSize: Size.fromHeight(kToolbarHeight),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 18, horizontal: 16),
child: Row(
children: [
Text(
'NAME',
style: TextStyle(
fontSize: 12,
color: Colors.black.withOpacity(0.5),
),
),
SizedBox(width: 16),
Expanded(
child: TextField(
controller: _groupNameController,
decoration: InputDecoration(
isDense: true,
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
errorBorder: InputBorder.none,
disabledBorder: InputBorder.none,
contentPadding: const EdgeInsets.all(0),
hintText: 'Choose a group chat name',
hintStyle: TextStyle(
fontSize: 14, color: Colors.black.withOpacity(.5)),
centerTitle: true,
bottom: PreferredSize(
preferredSize: Size.fromHeight(kToolbarHeight),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 18, horizontal: 16),
child: Row(
children: [
Text(
'NAME',
style: TextStyle(
fontSize: 12,
color: Colors.black.withOpacity(0.5),
),
),
),
],
SizedBox(width: 16),
Expanded(
child: TextField(
controller: _groupNameController,
decoration: InputDecoration(
isDense: true,
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
errorBorder: InputBorder.none,
disabledBorder: InputBorder.none,
contentPadding: const EdgeInsets.all(0),
hintText: 'Choose a group chat name',
hintStyle: TextStyle(
fontSize: 14, color: Colors.black.withOpacity(.5)),
),
),
),
],
),
),
),
),
actions: [
NeumorphicButton(
child: IconButton(
padding: const EdgeInsets.all(0),
icon: Icon(StreamIcons.check),
color: Color(0xFF006CFF),
onPressed: _isGroupNameEmpty
? null
: () async {
final groupName = _groupNameController.text;
final client = StreamChat.of(context).client;
final channel = client
.channel('messaging', id: Uuid().v4(), extraData: {
'members': [
client.state.user.id,
..._selectedUsers.map((e) => e.id),
],
'name': groupName,
});
await channel.watch();
Navigator.of(context)
..pop()
..pushReplacement(
MaterialPageRoute(
builder: (context) {
return StreamChannel(
child: ChannelPage(),
channel: channel,
);
},
),
actions: [
StreamNeumorphicButton(
child: IconButton(
padding: const EdgeInsets.all(0),
icon: StreamSvgIcon.check(
size: 24,
color: _isGroupNameEmpty ? Colors.grey : Color(0xFF006CFF),
),
onPressed: _isGroupNameEmpty
? null
: () async {
final groupName = _groupNameController.text;
final client = StreamChat.of(context).client;
final channel = client
.channel('messaging', id: Uuid().v4(), extraData: {
'members': [
client.state.user.id,
..._selectedUsers.map((e) => e.id),
],
'name': groupName,
});
await channel.watch();
Navigator.pushNamedAndRemoveUntil(
context,
Routes.CHANNEL_PAGE,
ModalRoute.withName(Routes.CHANNEL_LIST),
arguments: channel,
);
},
),
),
],
),
body: Column(
children: [
Container(
width: double.maxFinite,
color: Colors.grey.shade50,
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 8,
horizontal: 8,
},
),
child: Text(
'$_totalUsers ${_totalUsers > 1 ? 'Members' : 'Member'}',
style: TextStyle(
fontWeight: FontWeight.w500,
),
],
),
body: Column(
children: [
Container(
width: double.maxFinite,
color: Colors.grey.shade50,
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 8,
horizontal: 8,
),
child: Text(
'$_totalUsers ${_totalUsers > 1 ? 'Members' : 'Member'}',
style: TextStyle(
fontWeight: FontWeight.w500,
),
),
),
),
),
Expanded(
child: ListView.separated(
itemCount: _selectedUsers.length + 1,
separatorBuilder: (_, __) => Container(
height: 1,
color: Theme.of(context).brightness == Brightness.dark
? Colors.white.withOpacity(0.1)
: Colors.black.withOpacity(0.1),
),
itemBuilder: (_, index) {
if (index == _selectedUsers.length) {
return Container(
Expanded(
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onPanDown: (_) => FocusScope.of(context).unfocus(),
child: ListView.separated(
itemCount: _selectedUsers.length + 1,
separatorBuilder: (_, __) => Container(
height: 1,
color: Theme.of(context).brightness == Brightness.dark
? Colors.white.withOpacity(0.1)
: Colors.black.withOpacity(0.1),
);
}
final user = _selectedUsers[index];
return ListTile(
key: ObjectKey(user),
leading: UserAvatar(
user: user,
constraints: BoxConstraints.tightFor(
width: 40,
height: 40,
),
),
title: Text(
user.name,
style: TextStyle(fontWeight: FontWeight.bold),
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 8,
),
trailing: IconButton(
icon: Icon(
Icons.clear_rounded,
color: Colors.black,
),
padding: const EdgeInsets.all(0),
splashRadius: 24,
onPressed: () {
setState(() {
_selectedUsers.remove(user);
});
if (_selectedUsers.isEmpty) {
Navigator.pop(context);
}
},
),
);
},
itemBuilder: (_, index) {
if (index == _selectedUsers.length) {
return Container(
height: 1,
color: Theme.of(context).brightness == Brightness.dark
? Colors.white.withOpacity(0.1)
: Colors.black.withOpacity(0.1),
);
}
final user = _selectedUsers[index];
return ListTile(
key: ObjectKey(user),
leading: UserAvatar(
user: user,
constraints: BoxConstraints.tightFor(
width: 40,
height: 40,
),
),
title: Text(
user.name,
style: TextStyle(fontWeight: FontWeight.bold),
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 8,
),
trailing: IconButton(
icon: Icon(
Icons.clear_rounded,
color: Colors.black,
),
padding: const EdgeInsets.all(0),
splashRadius: 24,
onPressed: () {
setState(() {
_selectedUsers.remove(user);
});
if (_selectedUsers.isEmpty) {
Navigator.pop(context, _selectedUsers);
}
},
),
);
},
),
),
),
),
],
],
),
),
);
}
+54 -47
View File
@@ -10,6 +10,8 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'notifications_service.dart';
import 'routes/app_routes.dart';
import 'routes/routes.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
@@ -47,19 +49,19 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
//TODO change to system once dark theme is implemented
themeMode: ThemeMode.light,
builder: (context, widget) {
return StreamChat(
child: widget,
client: client,
);
},
home: client.state.user == null ? ChooseUserPage() : ChannelListPage(),
return StreamChat(
client: client,
child: MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
//TODO change to system once dark theme is implemented
themeMode: ThemeMode.light,
onGenerateRoute: AppRoutes.generateRoute,
initialRoute: client.state.user == null
? Routes.CHOOSE_USER
: Routes.CHANNEL_LIST,
),
);
}
}
@@ -69,21 +71,17 @@ class ChannelListPage extends StatelessWidget {
Widget build(BuildContext context) {
final user = StreamChat.of(context).user;
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
title: Text(
'Stream Chat',
style: TextStyle(color: Colors.black),
),
appBar: ChannelListHeader(
onNewChatButtonTap: () {
Navigator.pushNamed(context, Routes.NEW_CHAT);
},
),
drawer: _buildDrawer(context, user),
drawerEdgeDragWidth: 50,
body: ChannelsBloc(
child: ChannelListView(
onStartChatPressed: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
return NewChatScreen();
}));
Navigator.pushNamed(context, Routes.NEW_CHAT);
},
swipeToAction: true,
filter: {
@@ -141,13 +139,14 @@ class ChannelListPage extends StatelessWidget {
),
),
ListTile(
leading: Icon(StreamIcons.edit),
leading: StreamSvgIcon.penWrite(
color: Colors.black.withOpacity(.5),
),
onTap: () {
Navigator.of(context)
..pop()
..push(MaterialPageRoute(builder: (context) {
return NewChatScreen();
}));
Navigator.popAndPushNamed(
context,
Routes.NEW_CHAT,
);
},
title: Text(
'New direct message',
@@ -157,13 +156,14 @@ class ChannelListPage extends StatelessWidget {
),
),
ListTile(
leading: Icon(StreamIcons.group),
leading: StreamSvgIcon.contacts(
color: Colors.black.withOpacity(.5),
),
onTap: () {
Navigator.of(context)
..pop()
..push(MaterialPageRoute(builder: (context) {
return NewGroupChatScreen();
}));
Navigator.popAndPushNamed(
context,
Routes.NEW_GROUP_CHAT,
);
},
title: Text(
'New group',
@@ -182,14 +182,14 @@ class ChannelListPage extends StatelessWidget {
final secureStorage = FlutterSecureStorage();
await secureStorage.deleteAll();
Navigator.pop(context);
await Navigator.pushReplacement(
Navigator.pushReplacementNamed(
context,
MaterialPageRoute(
builder: (context) => ChooseUserPage(),
),
Routes.CHOOSE_USER,
);
},
leading: Icon(StreamIcons.user),
leading: StreamSvgIcon.user(
color: Colors.black.withOpacity(.5),
),
title: Text(
'Sign out',
style: TextStyle(
@@ -215,7 +215,9 @@ class ChannelPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: ChannelHeader(),
appBar: ChannelHeader(
showTypingIndicator: false,
),
body: Column(
children: <Widget>[
Expanded(
@@ -228,14 +230,19 @@ class ChannelPage extends StatelessWidget {
);
},
),
Positioned.fill(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8.0,
vertical: 4,
),
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Container(
alignment: Alignment.centerLeft,
color: Color(0xffFCFCFC).withOpacity(.9),
child: TypingIndicator(
alignment: Alignment.bottomRight,
alignment: Alignment.centerLeft,
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 4,
),
),
),
),
+74 -75
View File
@@ -5,8 +5,8 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'chips_input_text_field.dart';
import 'main.dart';
import 'neumorphic_button.dart';
import 'new_group_chat_screen.dart';
import 'routes/routes.dart';
class NewChatScreen extends StatefulWidget {
@override
@@ -164,8 +164,7 @@ class _NewChatScreenState extends State<NewChatScreen> {
),
),
Positioned(
child: Icon(
StreamIcons.close,
child: StreamSvgIcon.close(
color: Colors.white,
),
),
@@ -184,19 +183,21 @@ class _NewChatScreenState extends State<NewChatScreen> {
Container(
child: InkWell(
onTap: () {
Navigator.push(
Navigator.pushNamed(
context,
MaterialPageRoute(builder: (_) => NewGroupChatScreen()),
Routes.NEW_GROUP_CHAT,
);
},
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Row(
children: [
NeumorphicButton(
child: Icon(
StreamIcons.group,
color: Color(0xFF006CFF),
StreamNeumorphicButton(
child: Center(
child: StreamSvgIcon.contacts(
color: Color(0xFF006CFF),
size: 24,
),
),
),
SizedBox(width: 8),
@@ -243,68 +244,71 @@ class _NewChatScreenState extends State<NewChatScreen> {
),
Expanded(
child: _showUserList
? UsersBloc(
child: UserListView(
selectedUsers: _selectedUsers,
groupAlphabetically: _isSearchActive ? false : true,
onUserTap: (user, _) {
_controller.clear();
if (!_selectedUsers.contains(user)) {
_chipInputTextFieldState
..addItem(user)
..pauseItemAddition();
} else {
_chipInputTextFieldState.removeItem(user);
}
},
pagination: PaginationParams(
limit: 25,
),
filter: {
if (_userNameQuery.isNotEmpty)
'name': {
r'$autocomplete': _userNameQuery,
},
'id': {
r'$ne': StreamChat.of(context).user.id,
? GestureDetector(
behavior: HitTestBehavior.opaque,
onPanDown: (_) => FocusScope.of(context).unfocus(),
child: UsersBloc(
child: UserListView(
selectedUsers: _selectedUsers,
groupAlphabetically: _isSearchActive ? false : true,
onUserTap: (user, _) {
_controller.clear();
if (!_selectedUsers.contains(user)) {
_chipInputTextFieldState
..addItem(user)
..pauseItemAddition();
} else {
_chipInputTextFieldState.removeItem(user);
}
},
},
sort: [
SortOption(
'name',
direction: 1,
pagination: PaginationParams(
limit: 25,
),
],
emptyBuilder: (_) {
return LayoutBuilder(
builder: (context, viewportConstraints) {
return SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: viewportConstraints.maxHeight,
),
child: Center(
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(24),
child: Icon(
StreamIcons.search,
size: 96,
color: Colors.grey,
filter: {
if (_userNameQuery.isNotEmpty)
'name': {
r'$autocomplete': _userNameQuery,
},
'id': {
r'$ne': StreamChat.of(context).user.id,
},
},
sort: [
SortOption(
'name',
direction: 1,
),
],
emptyBuilder: (_) {
return LayoutBuilder(
builder: (context, viewportConstraints) {
return SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: viewportConstraints.maxHeight,
),
child: Center(
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(24),
child: StreamSvgIcon.search(
size: 96,
color: Colors.grey,
),
),
),
Text(
'No user matches these keywords...'),
],
Text(
'No user matches these keywords...'),
],
),
),
),
),
);
},
);
},
);
},
);
},
),
),
)
: MessageListView(),
@@ -331,16 +335,11 @@ class _NewChatScreenState extends State<NewChatScreen> {
channel.update({
'draft': false,
});
Navigator.pushReplacement(
Navigator.pushNamedAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) {
return StreamChannel(
child: ChannelPage(),
channel: channel,
);
},
),
Routes.CHANNEL_PAGE,
ModalRoute.withName(Routes.CHANNEL_LIST),
arguments: channel,
);
}
}
+180 -157
View File
@@ -4,6 +4,8 @@ import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'group_chat_details_screen.dart';
import 'search_text_field.dart';
import 'routes/routes.dart';
class NewGroupChatScreen extends StatefulWidget {
@override
@@ -24,11 +26,12 @@ class _NewGroupChatScreenState extends State<NewGroupChatScreen> {
void _userNameListener() {
if (_debounce?.isActive ?? false) _debounce.cancel();
_debounce = Timer(const Duration(milliseconds: 350), () {
if (mounted)
if (mounted) {
setState(() {
_userNameQuery = _controller.text;
_isSearchActive = _userNameQuery.isNotEmpty;
});
}
});
}
@@ -48,174 +51,167 @@ class _NewGroupChatScreenState extends State<NewGroupChatScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color.fromRGBO(252, 252, 252, 1),
appBar: AppBar(
elevation: 1,
backgroundColor: Colors.white,
leading: const StreamBackButton(),
title: Text(
'Add Group Members',
style: TextStyle(
color: Colors.black,
fontSize: 16,
),
),
centerTitle: true,
actions: [
if (_selectedUsers.isNotEmpty)
IconButton(
icon: Icon(
StreamIcons.arrow_right,
color: Color(0xFF006CFF),
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => GroupChatDetailsScreen(
selectedUsers: _selectedUsers.toList(growable: false),
),
),
);
},
)
],
),
body: UsersBloc(
child: Column(
children: [
Container(
height: 36,
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(
color: Colors.grey.shade300,
),
borderRadius: BorderRadius.circular(24),
),
margin: const EdgeInsets.symmetric(
vertical: 8,
horizontal: 8,
),
child: TextField(
controller: _controller,
decoration: InputDecoration(
prefixIcon: Icon(
StreamIcons.search,
return SafeArea(
child: Scaffold(
backgroundColor: Color.fromRGBO(252, 252, 252, 1),
body: NestedScrollView(
floatHeaderSlivers: true,
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
pinned: true,
forceElevated: true,
elevation: 1,
backgroundColor: Colors.white,
leading: const StreamBackButton(),
title: Text(
'Add Group Members',
style: TextStyle(
color: Colors.black,
size: 24,
),
hintText: 'Search',
hintStyle: TextStyle(
color: Colors.black.withOpacity(0.5),
fontSize: 14,
),
contentPadding: const EdgeInsets.all(0),
border: OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.circular(24),
fontSize: 16,
),
),
centerTitle: true,
actions: [
if (_selectedUsers.isNotEmpty)
IconButton(
icon: StreamSvgIcon.arrow_right(
color: Color(0xFF006CFF),
),
onPressed: () async {
final updatedList = await Navigator.pushNamed(
context,
Routes.NEW_GROUP_CHAT_DETAILS,
arguments: _selectedUsers.toList(growable: false),
);
if (updatedList != null) {
setState(() {
_selectedUsers
..clear()
..addAll(updatedList);
});
}
},
)
],
),
),
if (_selectedUsers.isNotEmpty)
Container(
height: 104,
child: ListView.separated(
scrollDirection: Axis.horizontal,
itemCount: _selectedUsers.length,
padding: const EdgeInsets.all(8),
separatorBuilder: (_, __) => SizedBox(width: 16),
itemBuilder: (_, index) {
final user = _selectedUsers.elementAt(index);
return Column(
children: [
Stack(
SliverToBoxAdapter(
child: SearchTextField(
controller: _controller,
),
),
if (_selectedUsers.isNotEmpty)
SliverToBoxAdapter(
child: Container(
height: 104,
child: ListView.separated(
scrollDirection: Axis.horizontal,
itemCount: _selectedUsers.length,
padding: const EdgeInsets.all(8),
separatorBuilder: (_, __) => SizedBox(width: 16),
itemBuilder: (_, index) {
final user = _selectedUsers.elementAt(index);
return Column(
children: [
UserAvatar(
onlineIndicatorAlignment: Alignment(0.9, 0.9),
user: user,
showOnlineStatus: true,
borderRadius: BorderRadius.circular(32),
constraints: BoxConstraints.tightFor(
height: 64,
width: 64,
),
),
Positioned(
top: -4,
right: -4,
child: GestureDetector(
onTap: () {
if (_selectedUsers.contains(user)) {
setState(() => _selectedUsers.remove(user));
}
},
child: Container(
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
border: Border.all(
color: Colors.grey.shade100,
),
),
child: Padding(
padding: const EdgeInsets.all(0.0),
child: Icon(
StreamIcons.close,
size: 24,
),
Stack(
children: [
UserAvatar(
onlineIndicatorAlignment: Alignment(0.9, 0.9),
user: user,
showOnlineStatus: true,
borderRadius: BorderRadius.circular(32),
constraints: BoxConstraints.tightFor(
height: 64,
width: 64,
),
),
Positioned(
top: -4,
right: -4,
child: GestureDetector(
onTap: () {
if (_selectedUsers.contains(user)) {
setState(
() => _selectedUsers.remove(user));
}
},
child: Container(
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
border: Border.all(
color: Colors.grey.shade100,
),
),
child: Padding(
padding: const EdgeInsets.all(0.0),
child: StreamSvgIcon.close(
color: Colors.black,
size: 24,
),
),
),
),
)
],
),
SizedBox(height: 4),
Text(
user.name.split(' ')[0],
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 12,
),
)
),
],
);
},
),
),
),
SliverPersistentHeader(
pinned: true,
delegate: _HeaderDelegate(
height: 30,
child: Container(
width: double.maxFinite,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [
Colors.black.withOpacity(0.02),
Colors.white.withOpacity(0.05),
],
stops: [0, 1],
),
),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 8,
horizontal: 8,
),
child: Text(
_isSearchActive
? 'Matches for \"$_userNameQuery\"'
: 'On the platform',
style: TextStyle(
color: Colors.black.withOpacity(0.5),
),
SizedBox(height: 4),
Text(
user.name.split(' ')[0],
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 12,
),
),
],
);
},
),
),
Container(
width: double.maxFinite,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [
Colors.black.withOpacity(0.02),
Colors.white.withOpacity(0.05),
],
stops: [0, 1],
),
),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 8,
horizontal: 8,
),
child: Text(
_isSearchActive
? 'Matches for \"$_userNameQuery\"'
: 'On the platform',
style: TextStyle(
color: Colors.black.withOpacity(0.5),
),
),
),
),
),
),
Expanded(
];
},
body: GestureDetector(
behavior: HitTestBehavior.opaque,
onPanDown: (_) => FocusScope.of(context).unfocus(),
child: UsersBloc(
child: UserListView(
selectedUsers: _selectedUsers,
pullToRefresh: false,
groupAlphabetically: _isSearchActive ? false : true,
onUserTap: (user, _) {
if (!_selectedUsers.contains(user)) {
@@ -260,8 +256,7 @@ class _NewGroupChatScreenState extends State<NewGroupChatScreen> {
children: [
Padding(
padding: const EdgeInsets.all(24),
child: Icon(
StreamIcons.search,
child: StreamSvgIcon.search(
size: 96,
color: Colors.grey,
),
@@ -277,9 +272,37 @@ class _NewGroupChatScreenState extends State<NewGroupChatScreen> {
},
),
),
],
),
),
),
);
}
}
class _HeaderDelegate extends SliverPersistentHeaderDelegate {
final Widget child;
final double height;
const _HeaderDelegate({
@required this.child,
@required this.height,
});
@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
return Container(
color: Colors.white,
child: child,
);
}
@override
double get maxExtent => height;
@override
double get minExtent => height;
@override
bool shouldRebuild(_HeaderDelegate oldDelegate) => true;
}
+64
View File
@@ -0,0 +1,64 @@
import 'routes.dart';
import 'package:flutter/material.dart';
import '../choose_user_page.dart';
import '../advanced_options_page.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import '../main.dart';
import '../group_chat_details_screen.dart';
import '../new_group_chat_screen.dart';
import '../new_chat_screen.dart';
class AppRoutes {
/// Add entry for new route here
static Route<dynamic> generateRoute(RouteSettings settings) {
final args = settings.arguments;
switch (settings.name) {
case Routes.CHANNEL_LIST:
return MaterialPageRoute(
settings: const RouteSettings(name: Routes.CHANNEL_LIST),
builder: (_) {
return ChannelListPage();
});
case Routes.CHOOSE_USER:
return MaterialPageRoute(
settings: const RouteSettings(name: Routes.CHOOSE_USER),
builder: (_) {
return ChooseUserPage();
});
case Routes.ADVANCED_OPTIONS:
return MaterialPageRoute(
settings: const RouteSettings(name: Routes.ADVANCED_OPTIONS),
builder: (_) => AdvancedOptionsPage(),
);
case Routes.CHANNEL_PAGE:
return MaterialPageRoute(
settings: const RouteSettings(name: Routes.CHANNEL_PAGE),
builder: (_) {
return StreamChannel(
channel: args as Channel,
child: ChannelPage(),
);
});
case Routes.NEW_CHAT:
return MaterialPageRoute(
settings: const RouteSettings(name: Routes.NEW_CHAT),
builder: (_) {
return NewChatScreen();
});
case Routes.NEW_GROUP_CHAT:
return MaterialPageRoute(
settings: const RouteSettings(name: Routes.NEW_GROUP_CHAT),
builder: (_) {
return NewGroupChatScreen();
});
case Routes.NEW_GROUP_CHAT_DETAILS:
return MaterialPageRoute(
settings: const RouteSettings(name: Routes.NEW_GROUP_CHAT_DETAILS),
builder: (_) {
return GroupChatDetailsScreen(
selectedUsers: args,
);
});
}
}
}
+10
View File
@@ -0,0 +1,10 @@
/// Define all the route names here
class Routes {
static const String CHANNEL_LIST = '/channel_list';
static const String CHOOSE_USER = '/choose_user';
static const String ADVANCED_OPTIONS = '/advance_options';
static const String CHANNEL_PAGE = '/channel_page';
static const String NEW_CHAT = '/new_chat';
static const String NEW_GROUP_CHAT = '/new_group_chat';
static const String NEW_GROUP_CHAT_DETAILS = '/new_group_chat_details';
}
+92
View File
@@ -0,0 +1,92 @@
import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
class SearchTextField extends StatelessWidget {
final TextEditingController controller;
final ValueChanged<String> onChanged;
final String hintText;
final VoidCallback onTap;
final bool showCloseButton;
const SearchTextField({
Key key,
@required this.controller,
this.onChanged,
this.onTap,
this.hintText = 'Search',
this.showCloseButton = true,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
height: 36,
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(
color: Colors.grey.shade300,
),
borderRadius: BorderRadius.circular(24),
),
margin: const EdgeInsets.symmetric(
vertical: 8,
horizontal: 8,
),
child: Row(
children: [
Expanded(
child: TextField(
onTap: onTap,
controller: controller,
onChanged: onChanged,
decoration: InputDecoration(
prefixIconConstraints: BoxConstraints.tight(Size(40, 24)),
prefixIcon: Padding(
padding: const EdgeInsets.only(
left: 8,
right: 8,
),
child: StreamSvgIcon.search(
color: Colors.black,
size: 24,
),
),
hintText: hintText,
hintStyle: TextStyle(
color: Colors.black.withOpacity(0.5),
fontSize: 14,
),
contentPadding: const EdgeInsets.all(0),
border: OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.circular(24),
),
),
),
),
if (showCloseButton)
Material(
color: Colors.transparent,
child: IconButton(
padding: const EdgeInsets.all(0),
icon: StreamSvgIcon.close_small(
color: Colors.grey,
),
splashRadius: 24,
onPressed: () {
if (controller.text.isNotEmpty) {
Future.microtask(
() => [
controller.clear(),
onChanged(''),
],
);
}
},
),
),
],
),
);
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
name: example
description: A new Flutter project.
version: 1.0.66+68
version: 1.0.78+80
environment:
sdk: ">=2.2.2 <3.0.0"
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 981 B

+3 -7
View File
@@ -1,18 +1,15 @@
import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/stream_icons.dart';
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
import 'package:stream_chat_flutter/src/unread_indicator.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
class StreamBackButton extends StatelessWidget {
const StreamBackButton({
Key key,
this.onPressed,
this.icon = Icons.arrow_back_ios_outlined,
this.showUnreads = false,
}) : super(key: key);
final VoidCallback onPressed;
final IconData icon;
final bool showUnreads;
@override
@@ -33,11 +30,10 @@ class StreamBackButton extends StatelessWidget {
if (onPressed != null) {
onPressed();
} else {
Navigator.of(context).pop();
Navigator.maybePop(context);
}
},
child: Icon(
icon ?? StreamIcons.left,
child: StreamSvgIcon.left(
size: 24,
color: Theme.of(context).brightness == Brightness.dark
? Colors.white
+3 -6
View File
@@ -1,7 +1,6 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/src/stream_icons.dart';
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
import 'package:stream_chat_flutter/src/utils.dart';
import 'channel_info.dart';
@@ -73,8 +72,7 @@ class ChannelBottomSheet extends StatelessWidget {
initialData: channel.isMuted,
builder: (context, snapshot) {
return ListTile(
leading: Icon(
StreamIcons.mute,
leading: StreamSvgIcon.mute(
size: 22,
color: StreamChatTheme.of(context).primaryIconTheme.color,
),
@@ -94,8 +92,7 @@ class ChannelBottomSheet extends StatelessWidget {
Divider(),
if (channel.isGroup && !channel.isDistinct)
ListTile(
leading: Icon(
StreamIcons.user_minus,
leading: StreamSvgIcon.userRemove(
size: 22,
color: Colors.black,
),
+6
View File
@@ -64,12 +64,16 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
/// Callback to call when the image is tapped.
final VoidCallback onImageTap;
/// If true the typing indicator will be rendered if a user is typing
final bool showTypingIndicator;
/// Creates a channel header
ChannelHeader({
Key key,
this.showBackButton = true,
this.onBackPressed,
this.onTitleTap,
this.showTypingIndicator = true,
this.onImageTap,
}) : preferredSize = Size.fromHeight(kToolbarHeight),
super(key: key);
@@ -114,7 +118,9 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
.channelHeaderTheme
.title,
),
SizedBox(height: 2),
ChannelInfo(
showTypingIndicator: showTypingIndicator,
channel: channel,
textStyle:
StreamChatTheme.of(context).channelPreviewTheme.subtitle,
+111 -26
View File
@@ -8,16 +8,47 @@ class ChannelInfo extends StatelessWidget {
/// The style of the text displayed
final TextStyle textStyle;
/// If true the typing indicator will be rendered if a user is typing
final bool showTypingIndicator;
const ChannelInfo({
Key key,
@required this.channel,
this.textStyle,
this.showTypingIndicator = true,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final client = StreamChat.of(context).client;
return StreamBuilder<List<Member>>(
stream: channel.state.membersStream,
initialData: channel.state.members,
builder: (context, snapshot) {
return ValueListenableBuilder(
valueListenable: client.wsConnectionStatus,
builder: (context, status, child) {
switch (status) {
case ConnectionStatus.connected:
return _buildConnectedTitleState(context, snapshot.data);
case ConnectionStatus.connecting:
return _buildConnectingTitleState(context);
case ConnectionStatus.disconnected:
return _buildDisconnectedTitleState(context, client);
default:
return Offstage();
}
},
);
},
);
}
Widget _buildConnectedTitleState(BuildContext context, List<Member> members) {
var alternativeWidget;
if (channel.memberCount != null && channel.memberCount > 2) {
return Text(
alternativeWidget = Text(
'${channel.memberCount} Members, ${channel.state.watcherCount} Online',
style: StreamChatTheme.of(context)
.channelTheme
@@ -25,35 +56,89 @@ class ChannelInfo extends StatelessWidget {
.lastMessageAt,
);
} else {
return StreamBuilder<List<Member>>(
stream: channel.state.membersStream,
initialData: channel.state.members,
builder: (context, snapshot) {
final otherMember = snapshot.data.firstWhere(
(element) => element.userId != StreamChat.of(context).user.id,
orElse: () => null,
final otherMember = members.firstWhere(
(element) => element.userId != StreamChat.of(context).user.id,
orElse: () => null,
);
if (otherMember != null) {
if (otherMember.user.online) {
alternativeWidget = Text(
'Online',
style: StreamChatTheme.of(context)
.channelTheme
.channelHeaderTheme
.lastMessageAt,
);
if (otherMember == null) {
return SizedBox();
}
if (otherMember.user.online) {
return Text(
'Online',
style: StreamChatTheme.of(context)
.channelTheme
.channelHeaderTheme
.lastMessageAt,
);
}
return Text(
} else {
alternativeWidget = Text(
'Last seen ${Jiffy(otherMember.user.lastActive).fromNow()}',
style: textStyle,
);
},
);
}
}
}
if (!showTypingIndicator) {
return alternativeWidget;
}
return TypingIndicator(
alignment: Alignment.center,
alternativeWidget: alternativeWidget,
style: textStyle,
);
}
Widget _buildConnectingTitleState(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
height: 16,
width: 16,
child: Center(
child: CircularProgressIndicator(),
),
),
SizedBox(width: 10),
Text(
'Searching for Network',
style: textStyle,
),
],
);
}
Widget _buildDisconnectedTitleState(BuildContext context, Client client) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Offline...',
style: textStyle,
),
TextButton(
style: TextButton.styleFrom(
padding: const EdgeInsets.all(0),
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
visualDensity: VisualDensity(
horizontal: VisualDensity.minimumDensity,
vertical: VisualDensity.minimumDensity,
),
),
onPressed: () async {
await client.disconnect();
return client.connect();
},
child: Text(
'Try Again',
style: textStyle.copyWith(
color: Color(0xFF006CFF),
),
),
),
],
);
}
}
+223
View File
@@ -0,0 +1,223 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/src/stream_neumorphic_button.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'stream_chat.dart';
typedef _TitleBuilder = Widget Function(
BuildContext context,
ConnectionStatus status,
Client client,
);
///
/// It shows the current [Client] status.
///
/// ```dart
/// class MyApp extends StatelessWidget {
/// final Client client;
///
/// MyApp(this.client);
///
/// @override
/// Widget build(BuildContext context) {
/// return MaterialApp(
/// home: StreamChat(
/// client: client,
/// child: Scaffold(
/// appBar: ChannelListHeader(),
/// ),
/// ),
/// );
/// }
/// }
/// ```
///
/// Usually you would use this widget as an [AppBar] inside a [Scaffold].
/// However you can also use it as a normal widget.
///
/// The widget by default uses the inherited [Client] to fetch information about the status.
/// However you can also pass your own [Client] if you don't have it in the widget tree.
///
/// The widget components render the ui based on the first ancestor of type [StreamChatTheme] and on its [ChannelTheme.channelHeaderTheme] property.
/// Modify it to change the widget appearance.
class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
/// Instantiates a ChannelListHeader
const ChannelListHeader({
Key key,
this.client,
this.titleBuilder,
this.onUserAvatarTap,
this.onNewChatButtonTap,
}) : super(key: key);
/// Pass this if you don't have a [Client] in your widget tree.
final Client client;
/// Use this to build your own title as per different [ConnectionStatus]
final _TitleBuilder titleBuilder;
/// Callback to call when pressing the user avatar button.
/// By default it calls Scaffold.of(context).openDrawer()
final Function(User) onUserAvatarTap;
/// Callback to call when pressing the new chat button.
final VoidCallback onNewChatButtonTap;
@override
Widget build(BuildContext context) {
final _client = client ?? StreamChat.of(context).client;
final user = _client.state.user;
return AppBar(
brightness: Theme.of(context).brightness,
elevation: 1,
backgroundColor:
StreamChatTheme.of(context).channelTheme.channelHeaderTheme.color,
centerTitle: true,
leading: Center(
child: UserAvatar(
user: user,
showOnlineStatus: false,
onTap: onUserAvatarTap ?? (_) => Scaffold.of(context).openDrawer(),
borderRadius: BorderRadius.circular(20),
constraints: BoxConstraints.tightFor(
height: 40,
width: 40,
),
),
),
actions: [
StreamNeumorphicButton(
child: IconButton(
icon: ValueListenableBuilder<ConnectionStatus>(
valueListenable: _client.wsConnectionStatus,
builder: (context, status, child) {
var color;
switch (status) {
case ConnectionStatus.connected:
color = Color(0xFF006CFF);
break;
case ConnectionStatus.connecting:
color = Colors.grey;
break;
case ConnectionStatus.disconnected:
color = Colors.grey;
break;
}
return SvgPicture.asset(
'svgs/icon_pen_write.svg',
package: 'stream_chat_flutter',
width: 24.0,
height: 24.0,
color: color,
);
},
),
onPressed: onNewChatButtonTap,
),
)
],
title: ValueListenableBuilder<ConnectionStatus>(
valueListenable: _client.wsConnectionStatus,
builder: (context, status, child) {
if (titleBuilder != null) {
return titleBuilder(context, status, _client);
}
switch (status) {
case ConnectionStatus.connected:
return _buildConnectedTitleState(context);
case ConnectionStatus.connecting:
return _buildConnectingTitleState(context);
case ConnectionStatus.disconnected:
return _buildDisconnectedTitleState(context, _client);
default:
return Offstage();
}
},
),
);
}
Widget _buildConnectedTitleState(BuildContext context) => Text(
'Stream Chat',
style: StreamChatTheme.of(context)
.channelTheme
.channelHeaderTheme
.title
.copyWith(
fontSize: 16,
fontWeight: FontWeight.bold,
),
);
Widget _buildConnectingTitleState(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
height: 16,
width: 16,
child: Center(
child: CircularProgressIndicator(),
),
),
SizedBox(width: 10),
Text(
'Searching for Network',
style: StreamChatTheme.of(context)
.channelTheme
.channelHeaderTheme
.title
.copyWith(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
],
);
}
Widget _buildDisconnectedTitleState(BuildContext context, Client client) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Offline...',
style: StreamChatTheme.of(context)
.channelTheme
.channelHeaderTheme
.title
.copyWith(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
TextButton(
onPressed: () async {
await client.disconnect();
return client.connect();
},
child: Text(
'Try Again',
style: StreamChatTheme.of(context)
.channelTheme
.channelHeaderTheme
.title
.copyWith(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Color(0xFF006CFF),
),
),
),
],
);
}
@override
Size get preferredSize => Size.fromHeight(kToolbarHeight);
}
+111 -110
View File
@@ -7,6 +7,7 @@ import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:shimmer/shimmer.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/src/channels_bloc.dart';
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
import 'package:stream_chat_flutter/src/utils.dart';
import '../stream_chat_flutter.dart';
@@ -166,123 +167,123 @@ class _ChannelListViewState extends State<ChannelListView>
ChannelsBlocState channelsBlocState,
) {
return StreamBuilder<List<Channel>>(
stream: channelsBlocState.channelsStream,
builder: (context, snapshot) {
var child;
if (snapshot.hasError) {
child = _buildErrorWidget(
snapshot,
context,
channelsBlocState,
);
} else if (!snapshot.hasData) {
child = _buildLoadingWidget();
} else {
final channels = snapshot.data;
stream: channelsBlocState.channelsStream,
builder: (context, snapshot) {
var child;
if (snapshot.hasError) {
child = _buildErrorWidget(
snapshot,
context,
channelsBlocState,
);
} else if (!snapshot.hasData) {
child = _buildLoadingWidget();
} else {
final channels = snapshot.data;
if (channels.isEmpty && widget.emptyBuilder != null) {
child = widget.emptyBuilder(context);
}
if (channels.isEmpty && widget.emptyBuilder != null) {
child = widget.emptyBuilder(context);
}
if (channels.isEmpty && widget.emptyBuilder == null) {
child = LayoutBuilder(
builder: (context, viewportConstraints) {
return SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
child: Stack(
children: [
ConstrainedBox(
constraints: BoxConstraints(
minHeight: viewportConstraints.maxHeight,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(
StreamIcons.message,
size: 136,
color: Color(0xffDBDBDB),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'Lets start chatting!',
style: TextStyle(
fontSize: 16,
),
),
),
Padding(
padding: const EdgeInsets.symmetric(
vertical: 8.0,
horizontal: 52,
),
child: Text(
'How about sending your first message to a friend?',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
color: Color(0xff7A7A7A),
),
),
),
],
),
if (channels.isEmpty && widget.emptyBuilder == null) {
child = LayoutBuilder(
builder: (context, viewportConstraints) {
return SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
child: Stack(
children: [
ConstrainedBox(
constraints: BoxConstraints(
minHeight: viewportConstraints.maxHeight,
),
if (widget.onStartChatPressed != null)
Positioned(
right: 0,
left: 0,
bottom: 32,
child: Center(
child: FlatButton(
onPressed: widget.onStartChatPressed,
child: Text(
'Start a chat',
style: TextStyle(
color:
StreamChatTheme.of(context).accentColor,
fontWeight: FontWeight.bold,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: StreamSvgIcon.message(
size: 136,
color: Color(0xffDBDBDB),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'Lets start chatting!',
style: TextStyle(
fontSize: 16,
),
),
),
Padding(
padding: const EdgeInsets.symmetric(
vertical: 8.0,
horizontal: 52,
),
child: Text(
'How about sending your first message to a friend?',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
color: Color(0xff7A7A7A),
),
),
),
],
),
),
if (widget.onStartChatPressed != null)
Positioned(
right: 0,
left: 0,
bottom: 32,
child: Center(
child: FlatButton(
onPressed: widget.onStartChatPressed,
child: Text(
'Start a chat',
style: TextStyle(
color:
StreamChatTheme.of(context).accentColor,
fontWeight: FontWeight.bold,
),
),
),
),
],
),
);
},
);
}
if (channels.isNotEmpty) {
child = ListView.custom(
physics: AlwaysScrollableScrollPhysics(),
controller: _scrollController,
childrenDelegate: SliverChildBuilderDelegate(
(context, i) {
return _itemBuilder(context, i, channels);
},
childCount: (channels.length * 2) + 1,
findChildIndexCallback: (key) {
final ValueKey<String> valueKey = key;
final index = channels.indexWhere(
(channel) => 'CHANNEL-${channel.id}' == valueKey.value);
return index != -1 ? (index * 2) : null;
},
),
);
}
),
],
),
);
},
);
}
return AnimatedSwitcher(
child: child,
duration: Duration(milliseconds: 500),
);
});
if (channels.isNotEmpty) {
child = ListView.custom(
physics: AlwaysScrollableScrollPhysics(),
controller: _scrollController,
childrenDelegate: SliverChildBuilderDelegate(
(context, i) {
return _itemBuilder(context, i, channels);
},
childCount: (channels.length * 2) + 1,
findChildIndexCallback: (key) {
final ValueKey<String> valueKey = key;
final index = channels.indexWhere(
(channel) => 'CHANNEL-${channel.id}' == valueKey.value);
return index != -1 ? (index * 2) : null;
},
),
);
}
}
return AnimatedSwitcher(
child: child,
duration: Duration(milliseconds: 500),
);
},
);
}
Widget _buildLoadingWidget() {
@@ -517,7 +518,7 @@ class _ChannelListViewState extends State<ChannelListView>
),
IconSlideAction(
color: backgroundColor,
icon: StreamIcons.mute,
iconWidget: StreamSvgIcon.mute(),
onTap: () async {
if (!channel.isMuted) {
await channel.mute();
@@ -529,7 +530,7 @@ class _ChannelListViewState extends State<ChannelListView>
if (channel.isGroup && !channel.isDistinct)
IconSlideAction(
color: backgroundColor,
icon: StreamIcons.user_minus,
iconWidget: StreamSvgIcon.userRemove(),
onTap: () async {
final confirm = await showConfirmationDialog(
context,
+3 -3
View File
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:jiffy/jiffy.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
import '../stream_chat_flutter.dart';
import 'channel_name.dart';
@@ -103,7 +104,7 @@ class ChannelPreview extends StatelessWidget {
.isAfter(channel
.state.lastMessage.createdAt))
.length ==
(channel.memberCount ?? 0) - 1,
(channel.memberCount ?? 0),
),
);
}
@@ -152,8 +153,7 @@ class ChannelPreview extends StatelessWidget {
return Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
Icon(
StreamIcons.mute,
StreamSvgIcon.mute(
size: 16,
),
Text(
+20
View File
@@ -0,0 +1,20 @@
import 'dart:async';
import 'package:synchronized/synchronized.dart';
import 'package:video_compress/video_compress.dart';
class ICompressVideoService {
static final ICompressVideoService instance = ICompressVideoService._();
final _lock = Lock();
ICompressVideoService._();
Future<MediaInfo> compressVideo(String path) async {
return _lock.synchronized(() {
return VideoCompress.compressVideo(
path,
);
});
}
}
ICompressVideoService get CompressVideoService =>
ICompressVideoService.instance;
+40 -9
View File
@@ -1,25 +1,56 @@
import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
class DeletedMessage extends StatelessWidget {
const DeletedMessage({
Key key,
@required this.messageTheme,
this.borderRadiusGeometry,
this.shape,
this.borderSide,
}) : super(key: key);
/// The theme of the message
final MessageTheme messageTheme;
/// The border radius of the message text
final BorderRadiusGeometry borderRadiusGeometry;
/// The shape of the message text
final ShapeBorder shape;
/// The borderside of the message text
final BorderSide borderSide;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Text(
'This message was deleted...',
style: messageTheme.messageText.copyWith(
fontStyle: FontStyle.italic,
color: Theme.of(context).brightness == Brightness.dark
? Colors.white
: Colors.black,
return Material(
color: messageTheme.messageBackgroundColor,
shape: shape ??
RoundedRectangleBorder(
borderRadius: borderRadiusGeometry ?? BorderRadius.zero,
side: borderSide ??
BorderSide(
color: Theme.of(context).brightness == Brightness.dark
? Colors.white.withAlpha(24)
: Colors.black.withAlpha(24),
),
),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 8.0,
horizontal: 16,
),
child: Text(
'Message deleted',
style: messageTheme.messageText.copyWith(
fontStyle: FontStyle.italic,
color: (Theme.of(context).brightness == Brightness.dark
? Colors.white
: Colors.black)
.withOpacity(.5),
),
),
),
);
+6 -10
View File
@@ -1,6 +1,7 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
import '../stream_chat_flutter.dart';
import 'attachment_error.dart';
@@ -119,8 +120,7 @@ class GiphyAttachment extends StatelessWidget {
),
child: Row(
children: [
Icon(
StreamIcons.lightning,
StreamSvgIcon.lightning(
color: StreamChatTheme.of(context).accentColor,
size: 16.0,
),
@@ -152,8 +152,7 @@ class GiphyAttachment extends StatelessWidget {
child: IconButton(
padding: const EdgeInsets.all(0),
constraints: BoxConstraints.tight(Size(32, 32)),
icon: Icon(
StreamIcons.left,
icon: StreamSvgIcon.left(
size: 24.0,
),
splashRadius: 16,
@@ -180,8 +179,7 @@ class GiphyAttachment extends StatelessWidget {
child: IconButton(
padding: const EdgeInsets.all(0),
constraints: BoxConstraints.tight(Size(32, 32)),
icon: Icon(
StreamIcons.right,
icon: StreamSvgIcon.right(
size: 24.0,
),
splashRadius: 16,
@@ -261,8 +259,7 @@ class GiphyAttachment extends StatelessWidget {
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
StreamIcons.eye,
StreamSvgIcon.eye(
color: Colors.black.withOpacity(0.5),
size: 16.0,
),
@@ -342,8 +339,7 @@ class GiphyAttachment extends StatelessWidget {
),
child: Row(
children: [
Icon(
StreamIcons.lightning,
StreamSvgIcon.lightning(
color: Colors.white,
size: 16,
),
+81
View File
@@ -0,0 +1,81 @@
import 'package:flutter/widgets.dart';
enum LoadingStatus { LOADING, STABLE }
/// Signature for EndOfPageListeners
typedef EndOfPageListenerCallback = void Function();
/// A widget that wraps a [Widget] and will trigger [onEndOfPage] when it
/// reaches the bottom of the list
class LazyLoadScrollView extends StatefulWidget {
/// The [Widget] that this widget watches for changes on
final Widget child;
/// Called when the [child] reaches the end of the list
final EndOfPageListenerCallback onEndOfPage;
/// The offset to take into account when triggering [onEndOfPage] in pixels
final int scrollOffset;
/// Used to determine if loading of new data has finished. You should use set this if you aren't using a FutureBuilder or StreamBuilder
final bool isLoading;
LazyLoadScrollView({
Key key,
@required this.child,
@required this.onEndOfPage,
this.isLoading = false,
this.scrollOffset = 100,
}) : assert(onEndOfPage != null),
assert(child != null),
super(key: key);
@override
State<StatefulWidget> createState() => _LazyLoadScrollViewState();
}
class _LazyLoadScrollViewState extends State<LazyLoadScrollView> {
LoadingStatus _loadMoreStatus = LoadingStatus.STABLE;
@override
void didUpdateWidget(LazyLoadScrollView oldWidget) {
super.didUpdateWidget(oldWidget);
if (!widget.isLoading) {
_loadMoreStatus = LoadingStatus.STABLE;
}
}
@override
Widget build(BuildContext context) {
return NotificationListener(
child: widget.child,
onNotification: _onNotification,
);
}
bool _onNotification(Notification notification) {
if (notification is ScrollUpdateNotification) {
if (notification.metrics.maxScrollExtent > notification.metrics.pixels &&
notification.metrics.maxScrollExtent - notification.metrics.pixels <=
widget.scrollOffset) {
if (_loadMoreStatus != null &&
_loadMoreStatus == LoadingStatus.STABLE) {
_loadMoreStatus = LoadingStatus.LOADING;
widget.onEndOfPage();
}
}
return true;
}
if (notification is OverscrollNotification) {
if (notification.overscroll > 0) {
if (_loadMoreStatus != null &&
_loadMoreStatus == LoadingStatus.STABLE) {
_loadMoreStatus = LoadingStatus.LOADING;
widget.onEndOfPage();
}
}
return true;
}
return false;
}
}
+141 -104
View File
@@ -1,12 +1,14 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:media_gallery/media_gallery.dart';
import 'package:stream_chat_flutter/src/stream_icons.dart';
import 'package:transparent_image/transparent_image.dart';
import 'package:photo_manager/photo_manager.dart';
import 'package:stream_chat_flutter/src/lazy_load_scroll_view.dart';
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
import 'dart:ui' as ui;
class MediaListView extends StatefulWidget {
final List<String> selectedIds;
final void Function(Media media) onSelect;
final void Function(AssetEntity media) onSelect;
const MediaListView({
Key key,
@@ -18,84 +20,94 @@ class MediaListView extends StatefulWidget {
}
class _MediaListViewState extends State<MediaListView> {
final _media = <Media>[];
final _media = <AssetEntity>[];
final ScrollController _scrollController = ScrollController();
int _currentPage = 0;
bool _endPagination = false;
@override
Widget build(BuildContext context) {
return GridView.builder(
itemCount: _media.length,
controller: _scrollController,
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
itemBuilder: (
context,
position,
) {
final media = _media.elementAt(position);
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 1.0, vertical: 1.0),
child: InkWell(
child: Stack(
children: [
AspectRatio(
aspectRatio: 1.0,
child: FadeInImage(
fadeInDuration: Duration(milliseconds: 300),
placeholder: MemoryImage(kTransparentImage),
image: MediaThumbnailProvider(
media: media,
highQuality: true,
return LazyLoadScrollView(
onEndOfPage: () {
_getMedia();
},
child: GridView.builder(
itemCount: _media.length,
controller: _scrollController,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
),
cacheExtent: 1000,
itemBuilder: (
context,
position,
) {
final media = _media.elementAt(position);
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 1.0, vertical: 1.0),
child: InkWell(
child: Stack(
children: [
AspectRatio(
aspectRatio: 1.0,
child: FadeInImage(
fadeInDuration: Duration(milliseconds: 300),
placeholder: AssetImage(
'images/placeholder.png',
package: 'stream_chat_flutter',
),
image: MediaThumbnailProvider(
media: media,
),
fit: BoxFit.cover,
),
fit: BoxFit.cover,
),
),
Positioned.fill(
child: IgnorePointer(
child: AnimatedOpacity(
duration: Duration(milliseconds: 300),
opacity: widget.selectedIds.any((id) => id == media.id)
? 1.0
: 0.0,
child: Container(
color: Colors.black.withOpacity(0.5),
alignment: Alignment.topRight,
padding: const EdgeInsets.only(
top: 8,
right: 8,
),
child: CircleAvatar(
radius: 12,
backgroundColor: Colors.white,
child: Icon(
StreamIcons.check,
size: 24,
color: Colors.black,
Positioned.fill(
child: IgnorePointer(
child: AnimatedOpacity(
duration: Duration(milliseconds: 300),
opacity: widget.selectedIds.any((id) => id == media.id)
? 1.0
: 0.0,
child: Container(
color: Colors.black.withOpacity(0.5),
alignment: Alignment.topRight,
padding: const EdgeInsets.only(
top: 8,
right: 8,
),
child: CircleAvatar(
radius: 12,
backgroundColor: Colors.white,
child: StreamSvgIcon.check(
size: 24,
color: Colors.black,
),
),
),
),
),
),
),
if (media.mediaType == MediaType.video)
Positioned(
left: 8,
bottom: 10,
child: SvgPicture.asset(
'svgs/video_call_icon.svg',
package: 'stream_chat_flutter',
if (media.type == AssetType.video)
Positioned(
left: 8,
bottom: 10,
child: SvgPicture.asset(
'svgs/video_call_icon.svg',
package: 'stream_chat_flutter',
),
),
),
],
],
),
onTap: () {
if (widget.onSelect != null) {
widget.onSelect(media);
}
},
),
onTap: () {
if (widget.onSelect != null) {
widget.onSelect(media);
}
},
),
);
},
);
},
),
);
}
@@ -106,42 +118,67 @@ class _MediaListViewState extends State<MediaListView> {
}
void _getMedia() async {
final List<MediaCollection> collections =
await MediaGallery.listMediaCollections(
mediaTypes: [
MediaType.video,
MediaType.image,
],
);
final assetList = await PhotoManager.getAssetPathList(
hasAll: true,
).then((value) => value.singleWhere((element) => element.isAll));
if (collections.isEmpty) {
return;
final media = await assetList.getAssetListPaged(_currentPage, 50);
if (media.isEmpty) {
setState(() {
_endPagination = true;
});
} else {
setState(() {
_media.addAll(media);
});
}
final collection = collections.firstWhere(
(element) => element.isAllCollection,
orElse: () => collections.first,
);
final videoPage = await collection.getMedias(
mediaType: MediaType.video,
take: 500,
);
final imagePage = await collection.getMedias(
mediaType: MediaType.image,
take: 500,
);
final allItems = [
...videoPage.items,
...imagePage.items,
]..sort((
a,
b,
) =>
b.creationDate.compareTo(a.creationDate));
setState(() {
_media.addAll(allItems);
});
++_currentPage;
}
}
class MediaThumbnailProvider extends ImageProvider<MediaThumbnailProvider> {
const MediaThumbnailProvider({
@required this.media,
}) : assert(media != null);
final AssetEntity media;
@override
ImageStreamCompleter load(key, decode) {
return MultiFrameImageStreamCompleter(
codec: _loadAsync(key, decode),
scale: 1.0,
informationCollector: () sync* {
yield ErrorDescription('Id: ${media?.id}');
},
);
}
Future<ui.Codec> _loadAsync(
MediaThumbnailProvider key, DecoderCallback decode) async {
assert(key == this);
final bytes = await media.thumbData;
if (bytes.isEmpty) return null;
return await decode(bytes);
}
@override
Future<MediaThumbnailProvider> obtainKey(ImageConfiguration configuration) {
return SynchronousFuture<MediaThumbnailProvider>(this);
}
@override
bool operator ==(dynamic other) {
if (other.runtimeType != runtimeType) return false;
final MediaThumbnailProvider typedOther = other;
return media?.id == typedOther.media?.id;
}
@override
int get hashCode => media?.id?.hashCode ?? 0;
@override
String toString() => '$runtimeType("${media?.id}")';
}
+79 -61
View File
@@ -6,7 +6,7 @@ import 'package:flutter/services.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/src/reaction_picker.dart';
import 'package:stream_chat_flutter/src/stream_channel.dart';
import 'package:stream_chat_flutter/src/stream_icons.dart';
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
import 'message_input.dart';
import 'message_widget.dart';
@@ -95,59 +95,82 @@ class MessageActionsModal extends StatelessWidget {
messageTheme: messageTheme,
),
),
IgnorePointer(
child: MessageWidget(
key: Key('MessageWidget'),
reverse: reverse,
message: message.copyWith(
text: message.text.length > 200
? '${message.text.substring(0, 200)}...'
: message.text,
),
messageTheme: messageTheme,
showReactions: false,
showUsername: false,
showReplyIndicator: false,
showUserAvatar: showUserAvatar,
showTimestamp: false,
translateUserAvatar: false,
showReactionPickerIndicator: true,
showSendingIndicator: DisplayWidget.gone,
shape: messageShape,
),
),
TweenAnimationBuilder<double>(
tween: Tween(begin: 0.0, end: 1.0),
duration: Duration(milliseconds: 300),
builder: (context, val, snapshot) {
return Transform.scale(
scale: val,
child: IgnorePointer(
child: MessageWidget(
key: Key('MessageWidget'),
reverse: reverse,
message: message.copyWith(
text: message.text.length > 200
? '${message.text.substring(0, 200)}...'
: message.text,
),
messageTheme: messageTheme,
showReactions: false,
showUsername: false,
showReplyIndicator: false,
showUserAvatar: showUserAvatar,
showTimestamp: false,
translateUserAvatar: false,
showReactionPickerIndicator: true,
showSendingIndicator: DisplayWidget.gone,
shape: messageShape,
),
),
);
}),
SizedBox(
height: 8,
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 48.0,
),
child: Material(
clipBehavior: Clip.hardEdge,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: ListTile.divideTiles(
context: context,
tiles: [
if (showReply &&
(message.status ==
MessageSendingStatus.SENT ||
message.status == null) &&
message.parentId == null)
_buildReplyButton(context),
if (showEditMessage) _buildEditMessage(context),
if (showDeleteMessage)
_buildDeleteButton(context),
if (showCopyMessage) _buildCopyButton(context),
],
).toList(),
),
),
)
TweenAnimationBuilder<double>(
tween: Tween(begin: 0.0, end: 1.0),
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
builder: (context, val, wid) {
return Transform(
transform: Matrix4.identity()
..scale(val)
..rotateZ(-1.0 + val),
alignment: Alignment.topRight,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 48.0,
),
child: Material(
clipBehavior: Clip.hardEdge,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.stretch,
children: ListTile.divideTiles(
context: context,
tiles: [
if (showReply &&
(message.status ==
MessageSendingStatus.SENT ||
message.status == null) &&
message.parentId == null)
_buildReplyButton(context),
if (showEditMessage)
_buildEditMessage(context),
if (showDeleteMessage)
_buildDeleteButton(context),
if (showCopyMessage)
_buildCopyButton(context),
],
).toList(),
),
),
),
);
})
],
),
),
@@ -165,8 +188,7 @@ class MessageActionsModal extends StatelessWidget {
style:
Theme.of(context).textTheme.headline6.copyWith(color: Colors.red),
),
leading: Icon(
StreamIcons.delete,
leading: StreamSvgIcon.delete(
color: Colors.red,
),
onTap: () {
@@ -185,8 +207,7 @@ class MessageActionsModal extends StatelessWidget {
'Copy message',
style: Theme.of(context).textTheme.headline6,
),
leading: Icon(
StreamIcons.copy,
leading: StreamSvgIcon.copy(
color: StreamChatTheme.of(context).primaryIconTheme.color,
),
onTap: () async {
@@ -202,8 +223,7 @@ class MessageActionsModal extends StatelessWidget {
'Edit message',
style: Theme.of(context).textTheme.headline6,
),
leading: Icon(
StreamIcons.edit,
leading: StreamSvgIcon.edit(
color: StreamChatTheme.of(context).primaryIconTheme.color,
),
onTap: () async {
@@ -244,8 +264,7 @@ class MessageActionsModal extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
IconButton(
icon: Icon(
StreamIcons.edit,
icon: StreamSvgIcon.edit(
size: 22,
color:
StreamChatTheme.of(context).primaryIconTheme.color,
@@ -300,8 +319,7 @@ class MessageActionsModal extends StatelessWidget {
'Thread reply',
style: Theme.of(context).textTheme.headline6,
),
leading: Icon(
StreamIcons.sorting_up,
leading: StreamSvgIcon.thread(
color: StreamChatTheme.of(context).primaryIconTheme.color,
),
onTap: () {
+365 -274
View File
@@ -1,6 +1,5 @@
import 'dart:async';
import 'dart:io';
import 'dart:math';
import 'package:emojis/emoji.dart';
import 'package:file_picker/file_picker.dart';
@@ -11,16 +10,18 @@ import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:http_parser/http_parser.dart' as httpParser;
import 'package:image_picker/image_picker.dart';
import 'package:media_gallery/media_gallery.dart';
import 'package:mime/mime.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:photo_manager/photo_manager.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/src/compress_video_service.dart';
import 'package:stream_chat_flutter/src/media_list_view.dart';
import 'package:stream_chat_flutter/src/message_list_view.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
import 'package:stream_chat_flutter/src/user_avatar.dart';
import 'package:stream_chat_flutter/src/video_thumbnail.dart';
import 'package:substring_highlight/substring_highlight.dart';
import 'package:video_compress/video_compress.dart';
import '../stream_chat_flutter.dart';
import 'stream_channel.dart';
@@ -44,6 +45,8 @@ enum DefaultAttachmentTypes {
const _kMinMediaPickerSize = 360.0;
const _kMaxAttachmentSize = 20480; //20MB
/// Inactive state
/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/message_input.png)
/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/message_input_paint.png)
@@ -201,6 +204,11 @@ class MessageInputState extends State<MessageInput> {
onPanUpdate: (details) {
if (details.delta.dy > 0) {
_focusNode.unfocus();
if (_openFilePickerSection) {
setState(() {
_openFilePickerSection = false;
});
}
}
},
child: Column(
@@ -279,8 +287,7 @@ class MessageInputState extends State<MessageInput> {
crossFadeState: _sendAsDm
? CrossFadeState.showFirst
: CrossFadeState.showSecond,
firstChild: Icon(
StreamIcons.check,
firstChild: StreamSvgIcon.check(
size: 16.0,
color: Colors.white,
),
@@ -325,8 +332,7 @@ class MessageInputState extends State<MessageInput> {
_actionsShrunk = false;
});
},
icon: Icon(
StreamIcons.circle_left,
icon: StreamSvgIcon.emptyCircleLeft(
color: StreamChatTheme.of(context).accentColor,
),
),
@@ -398,8 +404,7 @@ class MessageInputState extends State<MessageInput> {
_chosenCommand?.name ?? "",
style: TextStyle(color: Colors.white),
),
avatar: Icon(
StreamIcons.lightning,
avatar: StreamSvgIcon.lightning(
color: Colors.white,
),
),
@@ -426,26 +431,36 @@ class MessageInputState extends State<MessageInput> {
);
}
Timer _debounce;
void _onChanged(BuildContext context, String s) {
StreamChannel.of(context).channel.keyStroke().catchError((e) {});
if (_debounce?.isActive == true) _debounce.cancel();
_debounce = Timer(
const Duration(milliseconds: 350),
() {
if (!mounted) {
return;
}
StreamChannel.of(context).channel.keyStroke().catchError((e) {});
setState(() {
_messageIsPresent = s.trim().isNotEmpty;
_actionsShrunk = s.trim().isNotEmpty;
});
setState(() {
_messageIsPresent = s.trim().isNotEmpty;
_actionsShrunk = s.trim().isNotEmpty;
});
_commandsOverlay?.remove();
_commandsOverlay = null;
_mentionsOverlay?.remove();
_mentionsOverlay = null;
_emojiOverlay?.remove();
_emojiOverlay = null;
_commandsOverlay?.remove();
_commandsOverlay = null;
_mentionsOverlay?.remove();
_mentionsOverlay = null;
_emojiOverlay?.remove();
_emojiOverlay = null;
_checkCommands(s.trim(), context);
_checkCommands(s.trim(), context);
_checkMentions(s, context);
_checkMentions(s, context);
_checkEmoji(s, context);
_checkEmoji(s, context);
},
);
}
String _getHint() {
@@ -540,86 +555,100 @@ class MessageInputState extends State<MessageInput> {
bottom: size.height + MediaQuery.of(context).viewInsets.bottom,
left: 0,
right: 0,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Card(
elevation: 2.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
color: StreamChatTheme.of(context).primaryColor,
clipBehavior: Clip.antiAlias,
child: Container(
constraints: BoxConstraints.loose(Size.fromHeight(400)),
decoration: BoxDecoration(
color: StreamChatTheme.of(context).primaryColor,
borderRadius: BorderRadius.circular(8.0)),
child: ListView(
padding: const EdgeInsets.all(0),
shrinkWrap: true,
children: [
if (commands.isNotEmpty)
Padding(
padding: const EdgeInsets.only(left: 8.0, top: 8.0),
child: Row(
child: TweenAnimationBuilder<double>(
tween: Tween(begin: 0.0, end: 1.0),
duration: Duration(milliseconds: 300),
curve: Curves.easeInOutExpo,
builder: (context, val, wid) {
return Transform.scale(
alignment: Alignment.center,
scale: val,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Card(
elevation: 2.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
color: StreamChatTheme.of(context).primaryColor,
clipBehavior: Clip.antiAlias,
child: Container(
constraints: BoxConstraints.loose(Size.fromHeight(400)),
decoration: BoxDecoration(
color: StreamChatTheme.of(context).primaryColor,
borderRadius: BorderRadius.circular(8.0)),
child: ListView(
padding: const EdgeInsets.all(0),
shrinkWrap: true,
children: [
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 8.0),
child: Icon(
StreamIcons.lightning,
color: StreamChatTheme.of(context).accentColor,
if (commands.isNotEmpty)
Padding(
padding:
const EdgeInsets.only(left: 8.0, top: 8.0),
child: Row(
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8.0,
),
child: StreamSvgIcon.lightning(
color: StreamChatTheme.of(context)
.accentColor,
),
),
Text(
'Instant Commands',
style: TextStyle(
color: Colors.black.withOpacity(.5),
),
)
],
),
),
),
Text(
'Instant Commands',
style: TextStyle(
color: Colors.black.withOpacity(.5),
),
)
...commands
.map(
(c) => ListTile(
leading: c.name == 'giphy'
? _buildGiphyIcon()
: null,
title: Text.rich(
TextSpan(
text: '${c.name.capitalize()}',
style: TextStyle(
fontWeight: FontWeight.bold),
children: [
TextSpan(
text: ' /${c.name} ${c.args}',
style: TextStyle(
fontWeight: FontWeight.w300,
),
),
],
),
),
trailing: CircleAvatar(
backgroundColor:
StreamChatTheme.of(context).accentColor,
child: StreamSvgIcon.lightning(
color: Colors.white,
size: 12.5,
),
maxRadius: 12,
),
//subtitle: Text(c.description),
onTap: () {
_setCommand(c);
},
),
)
.toList(),
],
),
),
...commands
.map(
(c) => ListTile(
leading: c.name == 'giphy' ? _buildGiphyIcon() : null,
title: Text.rich(
TextSpan(
text: '${c.name.capitalize()}',
style: TextStyle(fontWeight: FontWeight.bold),
children: [
TextSpan(
text: ' /${c.name} ${c.args}',
style: TextStyle(
fontWeight: FontWeight.w300,
),
),
],
),
),
trailing: CircleAvatar(
backgroundColor:
StreamChatTheme.of(context).accentColor,
child: Icon(
StreamIcons.lightning,
color: Colors.white,
size: 12.5,
),
maxRadius: 12,
),
//subtitle: Text(c.description),
onTap: () {
_setCommand(c);
},
),
)
.toList(),
],
),
),
),
),
),
),
);
}),
);
});
}
@@ -636,8 +665,7 @@ class MessageInputState extends State<MessageInput> {
mainAxisAlignment: MainAxisAlignment.start,
children: [
IconButton(
icon: Icon(
StreamIcons.picture,
icon: StreamSvgIcon.pictures(
size: 24,
color: _filePickerIndex == 0
? StreamChatTheme.of(context).accentColor
@@ -650,8 +678,7 @@ class MessageInputState extends State<MessageInput> {
},
),
IconButton(
icon: Icon(
StreamIcons.folder,
icon: StreamSvgIcon.files(
size: 24,
color: _filePickerIndex == 1
? StreamChatTheme.of(context).accentColor
@@ -662,11 +689,8 @@ class MessageInputState extends State<MessageInput> {
},
),
IconButton(
icon: SvgPicture.asset(
'svgs/icon_camera.svg',
package: 'stream_chat_flutter',
height: 24,
width: 24,
icon: StreamSvgIcon.camera(
size: 24,
color: _filePickerIndex == 2
? StreamChatTheme.of(context).accentColor
: Colors.black.withOpacity(0.5),
@@ -676,10 +700,9 @@ class MessageInputState extends State<MessageInput> {
},
),
IconButton(
icon: Icon(
StreamIcons.record,
icon: StreamSvgIcon.record(
size: 24,
color: _filePickerIndex == 2
color: _filePickerIndex == 3
? StreamChatTheme.of(context).accentColor
: Colors.black.withOpacity(0.5),
),
@@ -777,7 +800,6 @@ class MessageInputState extends State<MessageInput> {
var status = await (Platform.isAndroid
? Permission.storage.status
: Permission.photos.status);
print('status: ${status}');
if (status.isPermanentlyDenied || status.isDenied) {
if (await openAppSettings()) {
setState(() {});
@@ -823,56 +845,105 @@ class MessageInputState extends State<MessageInput> {
}
}
void _addAttachment(Media medium) async {
void _addAttachment(AssetEntity medium) async {
final attachment = _SendingAttachment(
id: medium.id,
);
try {
setState(() {
_attachments.add(attachment);
});
final mediaFile = await medium.file;
setState(() {
_attachments.add(attachment);
});
final mediaFile = await medium.getFile();
final file = PlatformFile(
path: mediaFile.path,
bytes: mediaFile.readAsBytesSync(),
);
final channel = StreamChannel.of(context).channel;
setState(() {
attachment
..file = file
..attachment = Attachment(
localUri: file.path != null ? Uri.parse(file.path) : null,
type: medium.mediaType == MediaType.image ? 'image' : 'video',
);
});
final url = await _uploadAttachment(
file,
medium.mediaType == MediaType.image
? DefaultAttachmentTypes.image
: DefaultAttachmentTypes.video,
channel);
final fileType = medium.mediaType == MediaType.image
? DefaultAttachmentTypes.image
: DefaultAttachmentTypes.video;
if (fileType == DefaultAttachmentTypes.image) {
attachment.attachment = attachment.attachment.copyWith(
imageUrl: url,
var file = PlatformFile(
path: mediaFile.path,
size: ((await mediaFile.length()) / 1024).ceil(),
bytes: mediaFile.readAsBytesSync(),
);
} else {
attachment.attachment = attachment.attachment.copyWith(
assetUrl: url,
if (file.size > _kMaxAttachmentSize) {
if (medium.type == AssetType.video) {
final mediaInfo = await CompressVideoService.compressVideo(file.path);
if (mediaInfo.filesize / (1024 * 1024) > _kMaxAttachmentSize) {
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text(
'The file is too large to upload. The file size limit is 20MB. We tried compressing it, but it was not enough.',
),
),
);
setState(() {
_attachments.remove(attachment);
});
return;
}
file = PlatformFile(
name: file.name,
size: mediaInfo.filesize,
bytes: await mediaInfo.file.readAsBytes(),
path: mediaInfo.path,
);
} else {
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text(
'The file is too large to upload. The file size limit is 20MB',
),
),
);
}
}
final channel = StreamChannel.of(context).channel;
setState(() {
attachment
..file = file
..attachment = Attachment(
localUri: file.path != null ? Uri.parse(file.path) : null,
type: medium.type == AssetType.image ? 'image' : 'video',
);
});
final url = await _uploadAttachment(
file,
medium.type == AssetType.image
? DefaultAttachmentTypes.image
: DefaultAttachmentTypes.video,
channel);
final fileType = medium.type == AssetType.image
? DefaultAttachmentTypes.image
: DefaultAttachmentTypes.video;
if (fileType == DefaultAttachmentTypes.image) {
attachment.attachment = attachment.attachment.copyWith(
imageUrl: url,
);
} else {
attachment.attachment = attachment.attachment.copyWith(
assetUrl: url,
);
}
if (mounted) {
setState(() {
attachment.uploaded = true;
});
}
} catch (e, s) {
setState(() {
_attachments.remove(attachment);
});
print(e);
print(s);
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text('Error adding the attachment: $e'),
),
);
}
setState(() {
attachment.uploaded = true;
});
}
CircleAvatar _buildGiphyIcon() {
@@ -929,71 +1000,83 @@ class MessageInputState extends State<MessageInput> {
bottom: size.height + MediaQuery.of(context).viewInsets.bottom,
left: 0,
right: 0,
child: Card(
margin: EdgeInsets.all(8.0),
elevation: 2.0,
color: StreamChatTheme.of(context).primaryColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
clipBehavior: Clip.antiAlias,
child: Container(
constraints: BoxConstraints.loose(Size.fromHeight(400)),
decoration: BoxDecoration(
color: StreamChatTheme.of(context).primaryColor,
),
child: FutureBuilder<List<Member>>(
future: queryMembers ?? Future.value(members),
initialData: members,
builder: (context, snapshot) {
return ListView(
padding: const EdgeInsets.all(0),
shrinkWrap: true,
children: snapshot.data
.map((m) => ListTile(
leading: UserAvatar(
constraints: BoxConstraints.tight(
Size(
40,
40,
),
),
user: m.user,
),
title: Text(
'${m.user.name}',
style: TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Text('@${m.userId}'),
trailing: Icon(
StreamIcons.at_mention,
color: StreamChatTheme.of(context).accentColor,
),
onTap: () {
_mentionedUsers.add(m.user);
child: TweenAnimationBuilder<double>(
tween: Tween(begin: 0.0, end: 1.0),
duration: Duration(milliseconds: 300),
curve: Curves.easeInOutExpo,
builder: (context, val, wid) {
return Transform.scale(
scale: val,
child: Card(
margin: EdgeInsets.all(8.0),
elevation: 2.0,
color: StreamChatTheme.of(context).primaryColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
clipBehavior: Clip.antiAlias,
child: Container(
constraints: BoxConstraints.loose(Size.fromHeight(400)),
decoration: BoxDecoration(
color: StreamChatTheme.of(context).primaryColor,
),
child: FutureBuilder<List<Member>>(
future: queryMembers ?? Future.value(members),
initialData: members,
builder: (context, snapshot) {
return ListView(
padding: const EdgeInsets.all(0),
shrinkWrap: true,
children: snapshot.data
.map((m) => ListTile(
leading: UserAvatar(
constraints: BoxConstraints.tight(
Size(
40,
40,
),
),
user: m.user,
),
title: Text(
'${m.user.name}',
style: TextStyle(
fontWeight: FontWeight.bold),
),
subtitle: Text('@${m.userId}'),
trailing: StreamSvgIcon.mentions(
color: StreamChatTheme.of(context)
.accentColor,
),
onTap: () {
_mentionedUsers.add(m.user);
splits[splits.length - 1] = m.user.name;
final rejoin = splits.join('@');
splits[splits.length - 1] = m.user.name;
final rejoin = splits.join('@');
textEditingController.value = TextEditingValue(
text: rejoin +
textEditingController.text.substring(
textEditingController
.selection.start),
selection: TextSelection.collapsed(
offset: rejoin.length,
),
);
textEditingController.value =
TextEditingValue(
text: rejoin +
textEditingController.text
.substring(
textEditingController
.selection.start),
selection: TextSelection.collapsed(
offset: rejoin.length,
),
);
_mentionsOverlay?.remove();
_mentionsOverlay = null;
},
))
.toList(),
);
}),
),
),
_mentionsOverlay?.remove();
_mentionsOverlay = null;
},
))
.toList(),
);
}),
),
),
);
}),
);
});
}
@@ -1058,8 +1141,7 @@ class MessageInputState extends State<MessageInput> {
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 8.0),
child: Icon(
StreamIcons.smile,
child: StreamSvgIcon.smile(
color: StreamChatTheme.of(context).accentColor,
),
),
@@ -1194,8 +1276,7 @@ class MessageInputState extends State<MessageInput> {
},
fillColor: Colors.black.withOpacity(.5),
child: Center(
child: Icon(
StreamIcons.close,
child: StreamSvgIcon.close(
size: 24,
color: Colors.white,
),
@@ -1236,10 +1317,19 @@ class MessageInputState extends State<MessageInput> {
children: [
Positioned.fill(
child: Container(
child: VideoThumbnail(
file: File(
attachment.file.path,
)),
child: FutureBuilder<File>(
future: VideoCompress.getFileThumbnail(attachment.file.path),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Offstage();
}
return Image.file(
snapshot.data,
fit: BoxFit.cover,
);
},
),
),
),
Positioned(
@@ -1266,8 +1356,7 @@ class MessageInputState extends State<MessageInput> {
child: Padding(
padding:
const EdgeInsets.only(left: 4.0, right: 8.0, top: 8.0, bottom: 8.0),
child: Icon(
StreamIcons.lightning,
child: StreamSvgIcon.lightning(
color: Color(0xFF000000).withAlpha(128),
),
),
@@ -1290,8 +1379,7 @@ class MessageInputState extends State<MessageInput> {
child: Padding(
padding:
EdgeInsets.only(left: 8.0, right: padding, top: 8.0, bottom: 8.0),
child: Icon(
StreamIcons.attach,
child: StreamSvgIcon.attach(
color: _openFilePickerSection
? StreamChatTheme.of(context).accentColor
: Color(0xFF000000).withAlpha(128),
@@ -1468,7 +1556,6 @@ class MessageInputState extends State<MessageInput> {
);
if (res?.files?.isNotEmpty == true) {
file = res.files.single;
print('file.bytes?.length: ${file.bytes?.length}');
}
}
@@ -1480,6 +1567,26 @@ class MessageInputState extends State<MessageInput> {
return;
}
if (file.size > _kMaxAttachmentSize) {
if (attachmentType == 'video') {
final mediaInfo = await CompressVideoService.compressVideo(file.path);
file = PlatformFile(
name: mediaInfo.title,
size: mediaInfo.filesize,
bytes: await mediaInfo.file.readAsBytes(),
path: mediaInfo.path,
);
} else {
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text(
'The file is too large to upload. The file size limit is 20MB',
),
),
);
}
}
final channel = StreamChannel.of(context).channel;
final attachment = _SendingAttachment(
file: file,
@@ -1561,69 +1668,53 @@ class MessageInputState extends State<MessageInput> {
}
Widget _buildIdleSendButton(BuildContext context) {
return IconTheme(
data:
StreamChatTheme.of(context).channelTheme.messageInputButtonIconTheme,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: InkWell(
onTap: () {
sendMessage();
},
child: Icon(
_getIdleSendIcon(),
color: Colors.grey,
),
)),
),
return Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: InkWell(
onTap: () {
sendMessage();
},
child: StreamSvgIcon(
assetName: _getIdleSendIcon(),
color: Colors.grey,
),
)),
);
}
Widget _buildSendButton(BuildContext context) {
return IconTheme(
data:
StreamChatTheme.of(context).channelTheme.messageInputButtonIconTheme,
child: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap: () {
sendMessage();
},
child: _getSendIcon(),
return Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap: () {
sendMessage();
},
child: StreamSvgIcon(
assetName: _getSendIcon(),
color: StreamChatTheme.of(context).accentColor,
),
),
),
);
}
IconData _getIdleSendIcon() {
String _getIdleSendIcon() {
if (_commandEnabled) {
return StreamIcons.search;
return 'Icon_search.svg';
} else {
return StreamIcons.send_message;
return 'Icon_circle_up.svg';
}
}
Widget _getSendIcon() {
String _getSendIcon() {
if (widget.editMessage != null) {
return Icon(
StreamIcons.check_send,
color: StreamChatTheme.of(context).accentColor,
);
return 'Icon_circle_right.svg';
} else if (_commandEnabled) {
return Icon(
StreamIcons.search,
color: StreamChatTheme.of(context).accentColor,
);
return 'Icon_search.svg';
} else {
return Transform.rotate(
angle: -pi / 2,
child: Icon(
StreamIcons.send_message,
color: StreamChatTheme.of(context).accentColor,
));
return 'Icon_circle_right.svg';
}
}
+40 -38
View File
@@ -6,6 +6,7 @@ import 'package:jiffy/jiffy.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/src/message_widget.dart';
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
import 'package:stream_chat_flutter/src/system_message.dart';
import 'package:visibility_detector/visibility_detector.dart';
@@ -175,10 +176,12 @@ class _MessageListViewState extends State<MessageListView> {
: streamChannel.channel.state.messagesStream;
return StreamBuilder<List<Message>>(
stream: messagesStream,
initialData: widget.parentMessage != null
? streamChannel.channel.state.threads[widget.parentMessage.id]
: streamChannel.channel.state.messages,
stream: messagesStream.map((messages) => messages
.where((e) =>
!e.isDeleted ||
(e.isDeleted &&
e.user.id == streamChannel.channel.client.state.user.id))
.toList()),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
@@ -308,19 +311,8 @@ class _MessageListViewState extends State<MessageListView> {
return messageWidget;
},
),
if (streamChannel.channel.state.members.contains((Member e) =>
e.userId == streamChannel.channel.client.state.user.id) &&
widget.showScrollToBottom)
StreamBuilder<int>(
stream: streamChannel.channel.state.unreadCountStream,
builder: (context, snapshot) {
if (!_showScrollToBottom ||
!snapshot.hasData ||
snapshot.data == 0) {
return SizedBox();
}
return _buildScrollToBottom(snapshot.data);
}),
if (widget.showScrollToBottom && _showScrollToBottom)
_buildScrollToBottom(),
Positioned(
top: 20.0,
child: ValueListenableBuilder<Iterable<ItemPosition>>(
@@ -363,7 +355,8 @@ class _MessageListViewState extends State<MessageListView> {
position.itemLeadingEdge > max.itemLeadingEdge ? position : max);
}
Widget _buildScrollToBottom(int unreadCount) {
Widget _buildScrollToBottom() {
final streamChannel = StreamChannel.of(context);
return Positioned(
bottom: 8,
right: 8,
@@ -374,8 +367,7 @@ class _MessageListViewState extends State<MessageListView> {
children: [
FloatingActionButton(
backgroundColor: Colors.white,
child: Icon(
StreamIcons.down,
child: StreamSvgIcon.down(
color: Colors.black,
),
onPressed: () {
@@ -389,24 +381,34 @@ class _MessageListViewState extends State<MessageListView> {
);
},
),
Positioned(
width: 20,
height: 20,
left: 10,
top: -10,
child: CircleAvatar(
child: Padding(
padding: const EdgeInsets.all(3.0),
child: Text(
unreadCount.toString(),
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.bold,
),
),
),
),
),
if (streamChannel.channel.state.members.any((Member e) =>
e.userId == streamChannel.channel.client.state.user.id))
StreamBuilder<int>(
stream: streamChannel.channel.state.unreadCountStream,
initialData: streamChannel.channel.state.unreadCount,
builder: (context, snapshot) {
if (!snapshot.hasData || snapshot.data <= 0) {
return Offstage();
}
return Positioned(
width: 20,
height: 20,
left: 10,
top: -10,
child: CircleAvatar(
child: Padding(
padding: const EdgeInsets.all(3.0),
child: Text(
snapshot.data.toString(),
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.bold,
),
),
),
),
);
}),
],
),
);
+82 -64
View File
@@ -86,27 +86,35 @@ class MessageReactionsModal extends StatelessWidget {
messageTheme: messageTheme,
),
),
IgnorePointer(
child: MessageWidget(
key: Key('MessageWidget'),
reverse: reverse,
message: message.copyWith(
text: message.text.length > 200
? '${message.text.substring(0, 200)}...'
: message.text,
),
messageTheme: messageTheme,
showReactions: false,
showUsername: false,
showUserAvatar: showUserAvatar,
showReplyIndicator: false,
showTimestamp: false,
translateUserAvatar: false,
showSendingIndicator: DisplayWidget.gone,
shape: messageShape,
showReactionPickerIndicator: true,
),
),
TweenAnimationBuilder<double>(
tween: Tween(begin: 0.0, end: 1.0),
duration: Duration(milliseconds: 300),
builder: (context, val, snapshot) {
return Transform.scale(
scale: val,
child: IgnorePointer(
child: MessageWidget(
key: Key('MessageWidget'),
reverse: reverse,
message: message.copyWith(
text: message.text.length > 200
? '${message.text.substring(0, 200)}...'
: message.text,
),
messageTheme: messageTheme,
showReactions: false,
showUsername: false,
showUserAvatar: showUserAvatar,
showReplyIndicator: false,
showTimestamp: false,
translateUserAvatar: false,
showSendingIndicator: DisplayWidget.gone,
shape: messageShape,
showReactionPickerIndicator: true,
),
),
);
}),
SizedBox(
height: 16,
),
@@ -178,50 +186,60 @@ class MessageReactionsModal extends StatelessWidget {
BuildContext context,
) {
final isCurrentUser = reaction.user.id == currentUser.id;
return ConstrainedBox(
constraints: BoxConstraints.loose(Size(
64,
98,
)),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Stack(
children: [
UserAvatar(
onTap: onUserAvatarTap,
user: reaction.user,
constraints: BoxConstraints.tightFor(
height: 64,
width: 64,
),
borderRadius: BorderRadius.circular(32),
),
Positioned(
child: Align(
alignment: Alignment.centerLeft,
child: ReactionBubble(
reactions: [reaction],
borderColor: messageTheme.reactionsBorderColor,
backgroundColor: messageTheme.reactionsBackgroundColor,
highlightOwnReactions: false,
return TweenAnimationBuilder<double>(
tween: Tween(begin: 0.0, end: 1.0),
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
builder: (context, val, snapshot) {
return Transform.scale(
scale: val,
child: ConstrainedBox(
constraints: BoxConstraints.loose(Size(
64,
98,
)),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Stack(
children: [
UserAvatar(
onTap: onUserAvatarTap,
user: reaction.user,
constraints: BoxConstraints.tightFor(
height: 64,
width: 64,
),
borderRadius: BorderRadius.circular(32),
),
Positioned(
child: Align(
alignment: Alignment.centerLeft,
child: ReactionBubble(
reactions: [reaction],
borderColor: messageTheme.reactionsBorderColor,
backgroundColor:
messageTheme.reactionsBackgroundColor,
highlightOwnReactions: false,
),
),
bottom: 4,
left: isCurrentUser ? 0 : null,
right: isCurrentUser ? 0 : null,
),
],
),
),
bottom: 4,
left: isCurrentUser ? 0 : null,
right: isCurrentUser ? 0 : null,
Text(
reaction.user.name,
style: Theme.of(context).textTheme.subtitle2,
textAlign: TextAlign.center,
),
],
),
],
),
Text(
reaction.user.name,
style: Theme.of(context).textTheme.subtitle2,
textAlign: TextAlign.center,
),
],
),
);
),
);
});
}
}
+34 -1
View File
@@ -287,6 +287,10 @@ class _MessageWidgetState extends State<MessageWidget> {
transform: Matrix4.rotationY(
widget.reverse ? pi : 0),
child: DeletedMessage(
borderRadiusGeometry:
widget.borderRadiusGeometry,
borderSide: widget.borderSide,
shape: widget.shape,
messageTheme: widget.messageTheme,
),
)
@@ -411,6 +415,34 @@ class _MessageWidgetState extends State<MessageWidget> {
),
),
),
if (widget.message.isDeleted)
Transform(
transform: Matrix4.rotationY(widget.reverse ? pi : 0),
alignment: Alignment.center,
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
StreamSvgIcon.eye(
color: Colors.black.withOpacity(0.5),
size: 16.0,
),
SizedBox(
width: 8.0,
),
Text(
'Only visible to you',
style: TextStyle(
color: Colors.black.withOpacity(0.5),
fontSize: 12.0,
),
),
],
),
),
),
],
),
);
@@ -427,12 +459,13 @@ class _MessageWidgetState extends State<MessageWidget> {
top: 0,
child: Material(
color: Colors.white,
clipBehavior: Clip.antiAlias,
shape: CircleBorder(),
child: Padding(
padding: const EdgeInsets.all(1.0),
child: UserAvatar(
user: e.user,
constraints: BoxConstraints.loose(Size.fromRadius(16)),
constraints: BoxConstraints.loose(Size.fromRadius(8)),
showOnlineStatus: false,
),
),
+19 -8
View File
@@ -3,6 +3,7 @@ import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:stream_chat_flutter/src/reaction_icon.dart';
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
class ReactionBubble extends StatelessWidget {
@@ -99,14 +100,24 @@ class ReactionBubble extends StatelessWidget {
padding: const EdgeInsets.symmetric(
horizontal: 4.0,
),
child: Icon(
reactionIcon?.iconData ?? Icons.help_outline_rounded,
size: 16,
color: (!highlightOwnReactions ||
reaction.user.id == StreamChat.of(context).user.id)
? StreamChatTheme.of(context).accentColor
: Colors.black.withOpacity(.5),
),
child: reactionIcon != null
? StreamSvgIcon(
assetName: reactionIcon.assetName,
width: 16,
height: 16,
color: (!highlightOwnReactions ||
reaction.user.id == StreamChat.of(context).user.id)
? StreamChatTheme.of(context).accentColor
: Colors.black.withOpacity(.5),
)
: Icon(
Icons.help_outline_rounded,
size: 16,
color: (!highlightOwnReactions ||
reaction.user.id == StreamChat.of(context).user.id)
? StreamChatTheme.of(context).accentColor
: Colors.black.withOpacity(.5),
),
);
}
+2 -4
View File
@@ -1,11 +1,9 @@
import 'package:flutter/material.dart';
class ReactionIcon {
final String type;
final IconData iconData;
final String assetName;
ReactionIcon({
this.type,
this.iconData,
this.assetName,
});
}
+122 -42
View File
@@ -1,4 +1,6 @@
import 'package:ezanimation/ezanimation.dart';
import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
import '../stream_chat_flutter.dart';
@@ -8,7 +10,8 @@ import '../stream_chat_flutter.dart';
/// It shows a reaction picker
///
/// Usually you don't use this widget as it's one of the default widgets used by [MessageWidget.onMessageActions].
class ReactionPicker extends StatelessWidget {
class ReactionPicker extends StatefulWidget {
const ReactionPicker({
Key key,
@required this.message,
@@ -18,59 +21,136 @@ class ReactionPicker extends StatelessWidget {
final Message message;
final MessageTheme messageTheme;
@override
_ReactionPickerState createState() => _ReactionPickerState();
}
class _ReactionPickerState extends State<ReactionPicker>
with TickerProviderStateMixin {
List<EzAnimation> animations = [];
@override
Widget build(BuildContext context) {
final reactionIcons = StreamChatTheme.of(context).reactionIcons;
return Material(
color: messageTheme.reactionsBackgroundColor,
clipBehavior: Clip.hardEdge,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: reactionIcons.map((reactionIcon) {
final ownReactionIndex = message.ownReactions?.indexWhere(
(reaction) => reaction.type == reactionIcon.type) ??
-1;
return IconButton(
iconSize: 24,
icon: Icon(
reactionIcon.iconData,
color: ownReactionIndex != -1
? StreamChatTheme.of(context).accentColor
: Theme.of(context).iconTheme.color.withOpacity(.5),
if (animations.isEmpty && reactionIcons.isNotEmpty) {
reactionIcons.forEach((element) {
animations.add(
EzAnimation.sequence(
[
SequenceItem(0.0, 1.4),
SequenceItem(1.4, 1.0),
],
Duration(milliseconds: 500),
vsync: this,
),
);
});
triggerAnimations();
}
return TweenAnimationBuilder<double>(
tween: Tween(begin: 0.0, end: 1.0),
curve: Curves.easeInOutExpo,
duration: Duration(milliseconds: 500),
builder: (context, val, wid) {
return Transform.scale(
scale: val,
child: Material(
color: widget.messageTheme.reactionsBackgroundColor,
clipBehavior: Clip.hardEdge,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: reactionIcons.map((reactionIcon) {
final ownReactionIndex = widget.message.ownReactions
?.indexWhere((reaction) =>
reaction.type == reactionIcon.type) ??
-1;
var index = reactionIcons.indexOf(reactionIcon);
return IconButton(
iconSize: 24,
icon: AnimatedBuilder(
animation: animations[index],
builder: (context, val) {
return Transform(
transform: Matrix4.identity()
..scale(animations[index].value,
animations[index].value)
..rotateZ(1.0 - animations[index].value),
child: StreamSvgIcon(
assetName: reactionIcon.assetName,
height: animations[index].value * 24.0,
width: animations[index].value * 24.0,
color: ownReactionIndex != -1
? StreamChatTheme.of(context).accentColor
: Theme.of(context)
.iconTheme
.color
.withOpacity(.5),
),
);
}),
onPressed: () {
if (ownReactionIndex != -1) {
removeReaction(
context,
widget.message.ownReactions[ownReactionIndex],
);
} else {
sendReaction(
context,
reactionIcon.type,
);
}
},
);
}).toList(),
),
),
onPressed: () {
if (ownReactionIndex != -1) {
removeReaction(
context,
message.ownReactions[ownReactionIndex],
);
} else {
sendReaction(
context,
reactionIcon.type,
);
}
},
);
}).toList(),
),
);
});
}
void triggerAnimations() async {
for (var a in animations) {
a.start();
await Future.delayed(Duration(milliseconds: 100));
}
}
void pop() async {
for (var a in animations) {
a.stop();
}
Navigator.of(context).pop();
}
/// Add a reaction to the message
void sendReaction(BuildContext context, String reactionType) {
StreamChannel.of(context).channel.sendReaction(message, reactionType);
Navigator.of(context).pop();
StreamChannel.of(context)
.channel
.sendReaction(widget.message, reactionType);
pop();
}
/// Remove a reaction from the message
void removeReaction(BuildContext context, Reaction reaction) {
StreamChannel.of(context).channel.deleteReaction(message, reaction);
Navigator.of(context).pop();
StreamChannel.of(context).channel.deleteReaction(widget.message, reaction);
pop();
}
@override
void dispose() {
for (var a in animations) {
a?.dispose();
}
super.dispose();
}
}
+5 -6
View File
@@ -5,7 +5,6 @@ import 'package:stream_chat_flutter/src/channel_header.dart';
import 'package:stream_chat_flutter/src/channel_preview.dart';
import 'package:stream_chat_flutter/src/message_input.dart';
import 'package:stream_chat_flutter/src/reaction_icon.dart';
import 'package:stream_chat_flutter/src/stream_icons.dart';
import 'package:stream_chat_flutter/src/utils.dart';
/// Inherited widget providing the [StreamChatThemeData] to the widget tree
@@ -353,23 +352,23 @@ class StreamChatThemeData {
reactionIcons: [
ReactionIcon(
type: 'love',
iconData: StreamIcons.love_reaction,
assetName: 'Icon_love_reaction.svg',
),
ReactionIcon(
type: 'thumbs_up',
iconData: StreamIcons.thumbs_up_reaction,
assetName: 'Icon_thumbs_up_reaction.svg',
),
ReactionIcon(
type: 'thumbs_down',
iconData: StreamIcons.thumbs_down_reaction,
assetName: 'Icon_thumbs_down_reaction.svg',
),
ReactionIcon(
type: 'lol',
iconData: StreamIcons.LOL_reaction,
assetName: 'Icon_LOL_reaction.svg',
),
ReactionIcon(
type: 'wut',
iconData: StreamIcons.wut_reaction,
assetName: 'Icon_wut_reaction.svg',
),
],
);
File diff suppressed because it is too large Load Diff
@@ -1,10 +1,10 @@
import 'package:flutter/material.dart';
class NeumorphicButton extends StatelessWidget {
class StreamNeumorphicButton extends StatelessWidget {
final Widget child;
final Color backgroundColor;
const NeumorphicButton({
const StreamNeumorphicButton({
Key key,
@required this.child,
this.backgroundColor = Colors.white,
+400
View File
@@ -0,0 +1,400 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
class StreamSvgIcon extends StatelessWidget {
final String assetName;
final double width;
final double height;
final Color color;
const StreamSvgIcon({
this.assetName,
this.width,
this.height,
this.color,
});
@override
Widget build(BuildContext context) {
final key = Key('StreamSvgIcon-$assetName');
return kIsWeb
? Image.network(
'packages/stream_chat_flutter/svgs/$assetName',
width: width,
height: height,
key: key,
color: color,
alignment: Alignment.center,
)
: SvgPicture.asset(
'lib/svgs/$assetName',
package: 'stream_chat_flutter',
key: key,
width: width,
height: height,
color: color,
alignment: Alignment.center,
);
}
factory StreamSvgIcon.settings({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'settings.svg',
color: color,
width: size,
height: size,
);
}
factory StreamSvgIcon.down({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'Icon_down.svg',
color: color,
width: size,
height: size,
);
}
factory StreamSvgIcon.attach({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'Icon_attach.svg',
color: color,
width: size,
height: size,
);
}
factory StreamSvgIcon.smile({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'Icon_smile.svg',
color: color,
width: size,
height: size,
);
}
factory StreamSvgIcon.mentions({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'mentions.svg',
color: color,
width: size,
height: size,
);
}
factory StreamSvgIcon.record({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'Icon_record.svg',
color: color,
width: size,
height: size,
);
}
factory StreamSvgIcon.camera({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'Icon_camera.svg',
color: color,
width: size,
height: size,
);
}
factory StreamSvgIcon.files({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'files.svg',
color: color,
width: size,
height: size,
);
}
factory StreamSvgIcon.pictures({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'pictures.svg',
color: color,
width: size,
height: size,
);
}
factory StreamSvgIcon.left({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'Icon_left.svg',
color: color,
width: size,
height: size,
);
}
factory StreamSvgIcon.user({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'Icon_user.svg',
color: color,
width: size,
height: size,
);
}
factory StreamSvgIcon.userAdd({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'Icon_User_add.svg',
color: color,
width: size,
height: size,
);
}
factory StreamSvgIcon.check({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'Icon_check.svg',
color: color,
width: size,
height: size,
);
}
factory StreamSvgIcon.penWrite({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'Icon_pen-write.svg',
color: color,
width: size,
height: size,
);
}
factory StreamSvgIcon.contacts({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'Icon_contacts.svg',
color: color,
width: size,
height: size,
);
}
factory StreamSvgIcon.close({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'Icon_close.svg',
color: color,
width: size,
height: size,
);
}
factory StreamSvgIcon.search({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'Icon_search.svg',
color: color,
width: size,
height: size,
);
}
factory StreamSvgIcon.right({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'Icon_right.svg',
color: color,
width: size,
height: size,
);
}
factory StreamSvgIcon.mute({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'Icon_mute.svg',
color: color,
width: size,
height: size,
);
}
factory StreamSvgIcon.userRemove({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'Icon_User_deselect.svg',
color: color,
width: size,
height: size,
);
}
factory StreamSvgIcon.lightning({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'Icon_lightning-command runner.svg',
color: color,
width: size,
height: size,
);
}
factory StreamSvgIcon.emptyCircleLeft({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'Icon_empty_circle_left.svg',
color: color,
width: size,
height: size,
);
}
factory StreamSvgIcon.message({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'Icon_message.svg',
color: color,
width: size,
height: size,
);
}
factory StreamSvgIcon.thread({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'Icon_Thread_Reply.svg',
color: color,
width: size,
height: size,
);
}
factory StreamSvgIcon.edit({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'Icon_edit.svg',
color: color,
width: size,
height: size,
);
}
factory StreamSvgIcon.copy({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'Icon_copy.svg',
color: color,
width: size,
height: size,
);
}
factory StreamSvgIcon.delete({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'Icon_delete.svg',
color: color,
width: size,
height: size,
);
}
factory StreamSvgIcon.eye({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'Icon_eye-off.svg',
color: color,
width: size,
height: size,
);
}
factory StreamSvgIcon.arrow_right({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'Icon_arrow_right.svg',
color: color,
width: size,
height: size,
);
}
factory StreamSvgIcon.close_small({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'Icon_close_sml.svg',
color: color,
width: size,
height: size,
);
}
}
+9 -4
View File
@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/src/back_button.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/thread_header.png)
/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/thread_header_paint.png)
@@ -84,9 +84,14 @@ class ThreadHeader extends StatelessWidget implements PreferredSizeWidget {
child: showBackButton
? AspectRatio(
aspectRatio: 1,
child: StreamBackButton(
onPressed: onBackPressed,
icon: Icons.close,
child: IconButton(
onPressed: onBackPressed ?? () => Navigator.pop(context),
icon: StreamSvgIcon.close(
size: 24,
color: Theme.of(context).brightness == Brightness.dark
? Colors.white
: Colors.black,
),
),
)
: SizedBox(),
+27 -9
View File
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/src/stream_channel.dart';
@@ -8,9 +9,10 @@ class TypingIndicator extends StatelessWidget {
const TypingIndicator({
Key key,
this.channel,
this.alternativeWidget = const SizedBox(),
this.alternativeWidget,
this.style,
this.alignment = Alignment.centerLeft,
this.padding = const EdgeInsets.all(0),
}) : super(key: key);
/// Style of the text widget
@@ -22,6 +24,9 @@ class TypingIndicator extends StatelessWidget {
/// Widget built when no typings is happening
final Widget alternativeWidget;
/// The padding of this widget
final EdgeInsets padding;
final Alignment alignment;
@override
@@ -35,20 +40,33 @@ class TypingIndicator extends StatelessWidget {
return AnimatedSwitcher(
duration: Duration(milliseconds: 300),
child: snapshot.data?.isNotEmpty == true
? Align(
key: Key('typings'),
alignment: alignment,
child: Text(
'${snapshot.data.map((u) => u.name).join(',')} ${snapshot.data.length == 1 ? 'is' : 'are'} typing...',
maxLines: 1,
style: style,
? Padding(
padding: padding,
child: Align(
key: Key('typings'),
alignment: alignment,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Lottie.asset(
'animations/typing_dots.json',
package: 'stream_chat_flutter',
height: 4,
),
Text(
' ${snapshot.data.map((u) => u.name).join(',')} ${snapshot.data.length == 1 ? 'is' : 'are'} typing',
maxLines: 1,
style: style,
),
],
),
),
)
: Align(
key: Key('alternative'),
alignment: alignment,
child: Container(
child: alternativeWidget,
child: alternativeWidget ?? Offstage(),
),
),
);
+1
View File
@@ -40,6 +40,7 @@ class UserAvatar extends StatelessWidget {
final streamChatTheme = StreamChatTheme.of(context);
Widget avatar = ClipRRect(
clipBehavior: Clip.antiAlias,
borderRadius: borderRadius ??
streamChatTheme.ownMessageTheme.avatarTheme.borderRadius,
child: Container(
+3 -2
View File
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:jiffy/jiffy.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
import 'package:stream_chat_flutter/src/user_list_view.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
@@ -73,9 +74,9 @@ class UserItem extends StatelessWidget {
),
trailing: selected
? CircleAvatar(
child: Icon(
StreamIcons.check,
child: StreamSvgIcon.check(
size: 20,
color: Colors.white,
),
radius: 10,
)
+38 -67
View File
@@ -2,6 +2,7 @@ import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/src/lazy_load_scroll_view.dart';
import 'package:stream_chat_flutter/src/users_bloc.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
@@ -144,8 +145,6 @@ class UserListView extends StatefulWidget {
class _UserListViewState extends State<UserListView>
with WidgetsBindingObserver {
final ScrollController _scrollController = ScrollController();
bool get _isListView => widget.crossAxisCount == 1;
@override
@@ -158,14 +157,6 @@ class _UserListViewState extends State<UserListView>
pagination: widget.pagination,
options: widget.options,
);
_scrollController.addListener(() {
usersBloc.queryUsersLoading.first.then((loading) {
if (!loading) {
_listenUserPagination(usersBloc);
}
});
});
}
@override
@@ -327,57 +318,41 @@ class _UserListViewState extends State<UserListView>
);
}
if (_isListView) {
return ListView.custom(
physics: AlwaysScrollableScrollPhysics(),
controller: _scrollController,
childrenDelegate: SliverChildBuilderDelegate(
(context, i) {
return _listItemBuilder(context, i, items);
},
childCount: (items.length * 2) + 1,
findChildIndexCallback: (key) {
final ValueKey<String> valueKey = key;
final index =
items.indexWhere((item) => item.key == valueKey.value);
return index != -1 ? (index * 2) : null;
},
),
);
}
return GridView.custom(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: widget.crossAxisCount,
),
physics: AlwaysScrollableScrollPhysics(),
controller: _scrollController,
childrenDelegate: SliverChildBuilderDelegate(
(context, i) {
return _gridItemBuilder(context, i, items);
},
childCount: items.length,
findChildIndexCallback: (key) {
final ValueKey<String> valueKey = key;
final index =
items.indexWhere((item) => item.key == valueKey.value);
return index != -1 ? index : null;
},
),
final child = _isListView
? ListView.separated(
physics: AlwaysScrollableScrollPhysics(),
// controller: _scrollController,
itemCount: items.isNotEmpty ? items.length + 1 : items.length,
separatorBuilder: (_, index) {
if (widget.separatorBuilder != null) {
return widget.separatorBuilder(context, index);
}
return _separatorBuilder(context, index);
},
itemBuilder: (context, index) {
return _listItemBuilder(context, index, items);
},
)
: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: widget.crossAxisCount,
),
itemCount: items.isNotEmpty ? items.length + 1 : items.length,
physics: AlwaysScrollableScrollPhysics(),
itemBuilder: (context, index) {
return _gridItemBuilder(context, index, items);
},
);
return LazyLoadScrollView(
onEndOfPage: () => _listenUserPagination(usersBlocState),
child: child,
);
},
);
}
Widget _listItemBuilder(BuildContext context, int i, List<ListItem> items) {
if (i % 2 != 0) {
if (widget.separatorBuilder != null) {
return widget.separatorBuilder(context, i);
}
return _separatorBuilder(context, i);
}
i = i ~/ 2;
final usersProvider = UsersBloc.of(context);
if (i < items.length) {
final item = items[i];
@@ -507,18 +482,14 @@ class _UserListViewState extends State<UserListView>
}
void _listenUserPagination(UsersBlocState usersProvider) {
if (_scrollController.position.maxScrollExtent ==
_scrollController.offset &&
_scrollController.offset != 0) {
usersProvider.queryUsers(
filter: widget.filter,
sort: widget.sort,
pagination: widget.pagination.copyWith(
offset: usersProvider.users?.length ?? 0,
),
options: widget.options,
);
}
usersProvider.queryUsers(
filter: widget.filter,
sort: widget.sort,
pagination: widget.pagination.copyWith(
offset: usersProvider.users?.length ?? 0,
),
options: widget.options,
);
}
@override
-37
View File
@@ -1,37 +0,0 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
class VideoThumbnail extends StatefulWidget {
final File file;
const VideoThumbnail({
Key key,
@required this.file,
}) : super(key: key);
@override
_VideoThumbnailState createState() => _VideoThumbnailState();
}
class _VideoThumbnailState extends State<VideoThumbnail> {
VideoPlayerController _videoPlayerController;
@override
Widget build(BuildContext context) {
return VideoPlayer(_videoPlayerController);
}
@override
void initState() {
_videoPlayerController = VideoPlayerController.file(widget.file)
..initialize();
super.initState();
}
@override
void dispose() {
_videoPlayerController.dispose();
super.dispose();
}
}
+9 -4
View File
@@ -3,6 +3,7 @@ export 'package:stream_chat/stream_chat.dart';
export 'src/back_button.dart';
export 'src/channel_header.dart';
export 'src/channel_image.dart';
export 'src/channel_list_header.dart';
export 'src/channel_list_view.dart';
export 'src/channel_name.dart';
export 'src/channel_preview.dart';
@@ -25,13 +26,17 @@ export 'src/sending_indicator.dart';
export 'src/stream_channel.dart';
export 'src/stream_chat.dart';
export 'src/stream_chat_theme.dart';
export 'src/stream_icons.dart';
export 'src/stream_neumorphic_button.dart';
export 'src/stream_svg_icon.dart';
export 'src/system_message.dart';
export 'src/thread_header.dart';
export 'src/typing_indicator.dart';
export 'src/user_avatar.dart';
export 'src/user_item.dart';
export 'src/user_item.dart';
export 'src/user_list_view.dart';
export 'src/user_list_view.dart';
export 'src/users_bloc.dart';
export 'src/users_bloc.dart';
export 'src/utils.dart';
export 'src/video_attachment.dart';
export 'src/users_bloc.dart';
export 'src/user_list_view.dart';
export 'src/user_item.dart';
+3
View File
@@ -0,0 +1,3 @@
<svg width="136" height="136" viewBox="0 0 136 136" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M84.9999 17H50.9999L39.6666 28.3333H16.9999C13.8703 28.3333 11.3333 30.8704 11.3333 34V113.333C11.3333 116.463 13.8703 119 16.9999 119H119C122.13 119 124.667 116.463 124.667 113.333V34C124.667 30.8704 122.13 28.3333 119 28.3333H96.3333L84.9999 17ZM44.3586 39.6667L55.6918 28.3333H80.3079L91.6413 39.6667H113.333V107.667H22.6666V39.6667H44.3586ZM67.9999 102C50.787 102 36.8333 88.0462 36.8333 70.8333C36.8333 53.6204 50.787 39.6667 67.9999 39.6667C85.2128 39.6667 99.1666 53.6204 99.1666 70.8333C99.1666 88.0462 85.2128 102 67.9999 102ZM87.8333 70.8333C87.8333 81.787 78.9536 90.6667 67.9999 90.6667C57.0463 90.6667 48.1665 81.787 48.1665 70.8333C48.1665 59.8797 57.0463 51 67.9999 51C78.9536 51 87.8333 59.8797 87.8333 70.8333Z" fill="#DBDBDB"/>
</svg>

After

Width:  |  Height:  |  Size: 902 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="136" height="136" viewBox="0 0 136 136" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M119 28.3333H70.3459L59.0126 17H16.9999C13.8703 17 11.3333 19.5371 11.3333 22.6667V113.333C11.3333 116.463 13.8703 119 16.9999 119H119C122.13 119 124.667 116.463 124.667 113.333V34C124.667 30.8704 122.13 28.3333 119 28.3333ZM22.6666 107.667V39.6667H113.333V107.667H22.6666Z" fill="#D6D6D6"/>
</svg>

After

Width:  |  Height:  |  Size: 448 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="136" height="136" viewBox="0 0 136 136" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M68 28.3333C42.3172 28.3333 22.6666 46.7017 22.6666 68C22.6666 89.2982 42.3172 107.667 68 107.667C71.5184 107.667 74.936 107.316 78.213 106.655C78.8975 106.517 79.6013 106.507 80.2893 106.625L110.112 111.73L104.192 96.428C103.462 94.5387 103.795 92.4029 105.067 90.8265C110.315 84.3194 113.333 76.4456 113.333 68C113.333 46.7017 93.683 28.3333 68 28.3333ZM11.3333 68C11.3333 39.2244 37.3497 17 68 17C98.6504 17 124.667 39.2244 124.667 68C124.667 78.0719 121.423 87.4265 115.899 95.2754L124.285 116.955C125.021 118.858 124.677 121.009 123.386 122.588C122.095 124.167 120.055 124.93 118.044 124.586L79.3922 117.969C75.7072 118.646 71.8969 119 68 119C37.3497 119 11.3333 96.7753 11.3333 68Z" fill="#DBDBDB"/>
</svg>

After

Width:  |  Height:  |  Size: 862 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="136" height="136" viewBox="0 0 136 136" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M119.382 119.665C121.592 117.453 121.592 113.868 119.38 111.657L92.9937 85.27C99.244 77.3253 102.667 67.6013 102.667 57.3333C102.667 45.2237 97.9463 33.8393 89.384 25.2827C80.8273 16.7147 69.443 12 57.3333 12C45.2237 12 33.8393 16.7147 25.2827 25.2827C16.7203 33.8393 12 45.2237 12 57.3333C12 69.443 16.7203 80.8273 25.2827 89.3897C33.8393 97.952 45.2237 102.667 57.3333 102.667C67.4597 102.667 77.0533 99.329 84.9413 93.2373L111.371 119.667C113.583 121.879 117.17 121.878 119.382 119.665ZM33.2953 81.377C26.8693 74.9567 23.3333 66.417 23.3333 57.3333C23.3333 48.244 26.8693 39.7157 33.2953 33.2953C39.7157 26.8693 48.2497 23.3333 57.3333 23.3333C66.417 23.3333 74.951 26.8693 81.3713 33.301C87.7973 39.7157 91.3333 48.244 91.3333 57.3333C91.3333 66.417 87.7973 74.9567 81.3713 81.377C74.951 87.7973 66.417 91.3333 57.3333 91.3333C48.2497 91.3333 39.7157 87.7973 33.2953 81.377Z" fill="#DBDBDB"/>
</svg>

After

Width:  |  Height:  |  Size: 1013 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="136" height="136" viewBox="0 0 136 136" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M28.3333 28.3335C18.9444 28.3335 11.3333 35.9447 11.3333 45.3335V90.6669C11.3333 100.055 18.9444 107.667 28.3333 107.667H107.667C117.055 107.667 124.667 100.055 124.667 90.6669V45.3335C124.667 35.9447 117.055 28.3335 107.667 28.3335H28.3333ZM22.6666 45.3335C22.6666 42.2039 25.2036 39.6668 28.3333 39.6668H107.667C110.796 39.6668 113.333 42.2039 113.333 45.3335V75.0149L95.0199 53.039C93.9306 51.7318 92.3115 50.9833 90.61 51.0003C88.9085 51.0173 87.3047 51.798 86.2417 53.1267L67.0061 77.1713L48.7333 63.4667C46.4773 61.7747 43.3204 61.999 41.3264 63.9931L22.6666 82.6528V45.3335ZM25.5002 95.5756C26.3336 96.0576 27.3012 96.3335 28.3333 96.3335H107.667C110.193 96.3335 112.334 94.6797 113.064 92.3954L90.7563 65.6258L72.4249 88.5399C70.5129 90.9299 67.0486 91.3698 64.6 89.5333L45.8651 75.4821L26.6736 94.6736C26.3147 95.0325 25.9197 95.3331 25.5002 95.5756Z" fill="#D6D6D6"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

+8
View File
@@ -0,0 +1,8 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 24C18.627 24 24 18.627 24 12C24 5.373 18.627 0 12 0C5.373 0 0 5.373 0 12C0 18.627 5.373 24 12 24Z" fill="black"/>
<path d="M7 7H8V17H7V7Z" fill="#00FF99"/>
<path d="M16 10H17V18H16V10Z" fill="#9D34FF"/>
<path d="M7 6H14V7H7V6Z" fill="#FFFF9C"/>
<path d="M7 17H16V18H7V17Z" fill="#00CCFF"/>
<path d="M14 6H15V7.5H16V9H17V10H14V6Z" fill="#FF6666"/>
</svg>

After

Width:  |  Height:  |  Size: 463 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.677 21.168L5.897 21.521C4.029 22.365 3.07 22.562 3.02 22.111C2.962 21.588 2.937 19.657 2.946 16.317C2.956 14.101 2.974 11.895 2.998 9.69796C3.044 9.40796 3.407 9.11096 4.086 8.80396C4.754 8.50096 5.18 8.41796 5.362 8.55296C5.412 8.60696 5.453 8.77296 5.484 9.05296C5.608 10.173 5.638 13.055 5.574 17.698C5.564 18.214 5.56 18.688 5.564 19.121L8.405 17.76C8.684 17.66 8.887 17.737 9.016 17.992C9.145 18.247 9.22 18.478 9.243 18.685C9.326 19.427 9.109 19.935 8.593 20.206L8.069 20.462C7.564 20.729 7.099 20.964 6.677 21.168ZM12.127 18.762C10.868 19.267 10.02 19.078 9.582 18.196C9.144 17.314 8.863 16.313 8.739 15.193C8.682 14.683 8.649 14.123 8.637 13.514C8.581 11.609 8.787 9.89896 9.255 8.38396C9.722 6.86896 10.582 5.82896 11.835 5.26296L11.866 5.24796C13.128 4.76796 13.979 4.95796 14.42 5.81896C14.861 6.68096 15.142 7.65296 15.262 8.73596C15.322 9.27196 15.352 9.85196 15.354 10.477C15.482 14.944 14.406 17.706 12.127 18.762ZM12.051 16.112C12.661 15.785 12.935 14.242 12.875 11.483C12.8684 10.8526 12.832 10.2229 12.766 9.59596C12.7136 9.16803 12.6443 8.74236 12.558 8.31996C12.455 7.79696 12.23 7.57596 11.882 7.65696C11.542 7.80996 11.311 8.33396 11.187 9.22696C11.058 10.2248 11.0079 11.2313 11.037 12.237C11.048 12.948 11.085 13.583 11.147 14.143C11.185 14.484 11.257 14.917 11.367 15.443C11.475 15.97 11.703 16.193 12.051 16.113V16.112ZM20 15.145L19.22 15.497C17.353 16.342 16.394 16.539 16.344 16.088C16.286 15.565 16.262 13.633 16.27 10.294C16.28 8.07796 16.298 5.87096 16.322 3.67396C16.368 3.38596 16.731 3.08796 17.41 2.77996C18.078 2.47796 18.504 2.39496 18.686 2.52996C18.736 2.58296 18.777 2.74996 18.808 3.02996C18.933 4.14996 18.962 7.03196 18.898 11.675C18.888 12.191 18.884 12.665 18.888 13.098L21.729 11.737C22.008 11.637 22.212 11.714 22.34 11.969C22.469 12.224 22.544 12.455 22.567 12.662C22.65 13.404 22.433 13.912 21.917 14.182L21.394 14.438C20.888 14.705 20.424 14.941 20.001 15.145H20Z" fill="#808080"/>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4 5C3.73478 5 3.48043 5.10536 3.29289 5.29289C3.10536 5.48043 3 5.73478 3 6C3 6.26522 3.10536 6.51957 3.29289 6.70711C3.48043 6.89464 3.73478 7 4 7H20C20.2652 7 20.5196 6.89464 20.7071 6.70711C20.8946 6.51957 21 6.26522 21 6C21 5.73478 20.8946 5.48043 20.7071 5.29289C20.5196 5.10536 20.2652 5 20 5H4ZM3 12C3 11.7348 3.10536 11.4804 3.29289 11.2929C3.48043 11.1054 3.73478 11 4 11H13C13.2652 11 13.5196 11.1054 13.7071 11.2929C13.8946 11.4804 14 11.7348 14 12C14 12.2652 13.8946 12.5196 13.7071 12.7071C13.5196 12.8946 13.2652 13 13 13H4C3.73478 13 3.48043 12.8946 3.29289 12.7071C3.10536 12.5196 3 12.2652 3 12ZM10 18C10 17.7348 10.1054 17.4804 10.2929 17.2929C10.4804 17.1054 10.7348 17 11 17H20C20.2652 17 20.5196 17.1054 20.7071 17.2929C20.8946 17.4804 21 17.7348 21 18C21 18.2652 20.8946 18.5196 20.7071 18.7071C20.5196 18.8946 20.2652 19 20 19H11C10.7348 19 10.4804 18.8946 10.2929 18.7071C10.1054 18.5196 10 18.2652 10 18Z" fill="#808080"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M16 7C16 8.31528 15.3652 9.48229 14.3852 10.2113C15.3987 10.3984 16.318 10.7031 17.1402 11.0991C17.6378 11.3387 17.8469 11.9363 17.6072 12.4339C17.3676 12.9315 16.7699 13.1406 16.2724 12.9009C15.1351 12.3532 13.716 12 12 12C6.4686 12 4 15.6324 4 18C4 18.5523 3.55228 19 3 19C2.44771 19 2 18.5523 2 18C2 14.8422 4.66883 11.1288 9.61611 10.2123C8.63539 9.48334 8 8.31587 8 7C8 4.79086 9.79086 3 12 3C14.2091 3 16 4.79086 16 7ZM12 9C13.1046 9 14 8.10457 14 7C14 5.89543 13.1046 5 12 5C10.8954 5 10 5.89543 10 7C10 8.10457 10.8954 9 12 9ZM18 14H20V16H22V18H20V20H18V18H16V16H18V14Z" fill="#808080"/>
</svg>

After

Width:  |  Height:  |  Size: 748 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.9113 7C16.9113 8.31528 16.2783 9.4823 15.301 10.2113C16.3117 10.3984 17.2284 10.7031 18.0483 11.0991C18.5445 11.3387 18.753 11.9363 18.514 12.4339C18.2751 12.9315 17.6791 13.1406 17.1829 12.9009C16.0488 12.3532 14.6336 12 12.9223 12C7.4063 12 4.94453 15.6324 4.94453 18C4.94453 18.5523 4.49806 19 3.9473 19C3.39655 19 2.95007 18.5523 2.95007 18C2.95007 14.8422 5.61152 11.1288 10.5451 10.2123C9.56709 9.48333 8.93347 8.31587 8.93347 7C8.93347 4.79086 10.7193 3 12.9224 3C15.1254 3 16.9113 4.79086 16.9113 7ZM12.9224 9C14.0239 9 14.9168 8.10457 14.9168 7C14.9168 5.89543 14.0239 5 12.9224 5C11.8208 5 10.9279 5.89543 10.9279 7C10.9279 8.10457 11.8208 9 12.9224 9ZM22.8948 18V16H16.9114V18H22.8948Z" fill="#808080"/>
</svg>

After

Width:  |  Height:  |  Size: 871 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.2929 7.29289C15.6834 6.90237 16.3166 6.90237 16.7071 7.29289L20.7011 11.287C20.7168 11.3024 20.7319 11.3183 20.7466 11.3347C20.7814 11.3736 20.8126 11.4147 20.8402 11.4574C20.8626 11.492 20.8827 11.5278 20.9004 11.5644C20.9026 11.5689 20.9047 11.5733 20.9068 11.5778C20.9666 11.7061 21 11.8491 21 12C21 12.1561 20.9642 12.3039 20.9004 12.4356C20.8676 12.5034 20.8267 12.5682 20.7777 12.6287C20.7738 12.6335 20.7698 12.6383 20.7658 12.6431C20.745 12.6679 20.723 12.6916 20.6999 12.7143L16.7071 16.7071C16.3166 17.0976 15.6834 17.0976 15.2929 16.7071C14.9024 16.3166 14.9024 15.6834 15.2929 15.2929L17.5858 13H4C3.44772 13 3 12.5523 3 12C3 11.4477 3.44772 11 4 11H17.5858L15.2929 8.70711C14.9024 8.31658 14.9024 7.68342 15.2929 7.29289Z" fill="#006CFF"/>
</svg>

After

Width:  |  Height:  |  Size: 909 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.468 2.88798C14.5039 2.78848 15.5454 3.01018 16.451 3.52298C17.3476 4.05069 18.0602 4.84146 18.492 5.78798C18.942 6.79798 19.038 8.04198 18.348 9.23698L13.848 17.031C13.5149 17.5973 13.0171 18.0487 12.421 18.325C11.664 18.667 10.725 18.692 9.74998 18.129C8.77498 17.566 8.32798 16.74 8.24398 15.914C8.18545 15.2594 8.32777 14.6026 8.65198 14.031L12.652 7.10298C12.7855 6.87518 13.0038 6.70944 13.2591 6.64195C13.5144 6.57445 13.786 6.61067 14.0147 6.74271C14.2434 6.87475 14.4106 7.09188 14.4797 7.34672C14.5489 7.60156 14.5145 7.87343 14.384 8.10298L10.384 15.031C10.297 15.181 10.208 15.456 10.234 15.713C10.254 15.909 10.339 16.16 10.75 16.397C11.16 16.634 11.421 16.582 11.6 16.502C11.836 16.395 12.03 16.182 12.116 16.032L16.616 8.23698C16.926 7.69898 16.906 7.14398 16.665 6.60098C16.4049 6.04043 15.9818 5.57134 15.451 5.25498C14.9115 4.9535 14.2936 4.82176 13.678 4.87698C13.087 4.93898 12.596 5.19898 12.286 5.73698L7.28598 14.397C6.63198 15.53 6.99998 17.697 9.24998 18.995C11.5 20.295 13.56 19.53 14.214 18.397L17.714 12.335C17.7792 12.2203 17.8664 12.1197 17.9706 12.0389C18.0748 11.9581 18.194 11.8987 18.3213 11.8642C18.4486 11.8296 18.5814 11.8206 18.7122 11.8376C18.843 11.8546 18.9691 11.8973 19.0833 11.9632C19.1975 12.0292 19.2976 12.1171 19.3777 12.2218C19.4578 12.3266 19.5164 12.4461 19.5501 12.5736C19.5838 12.7011 19.592 12.8341 19.5741 12.9647C19.5562 13.0954 19.5127 13.2212 19.446 13.335L15.946 19.397C14.601 21.727 11.196 22.428 8.24998 20.727C5.30398 19.027 4.20798 15.727 5.55398 13.397L10.554 4.73698C11.244 3.54198 12.369 3.00398 13.468 2.88698V2.88798Z" fill="#808080"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M21 9H15L13 11H9C8.44771 11 8 11.4477 8 12V26C8 26.5523 8.44771 27 9 27H27C27.5523 27 28 26.5523 28 26V12C28 11.4477 27.5523 11 27 11H23L21 9ZM13.828 13L15.828 11H20.172L22.172 13H26V25H10V13H13.828ZM18 24C14.9624 24 12.5 21.5376 12.5 18.5C12.5 15.4624 14.9624 13 18 13C21.0376 13 23.5 15.4624 23.5 18.5C23.5 21.5376 21.0376 24 18 24ZM21.5 18.5C21.5 20.433 19.933 22 18 22C16.067 22 14.5 20.433 14.5 18.5C14.5 16.567 16.067 15 18 15C19.933 15 21.5 16.567 21.5 18.5Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 634 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.471 5.80499C12.731 5.54399 12.731 5.12199 12.471 4.86199C12.211 4.60199 11.789 4.60199 11.529 4.86199L6.66648 9.72359L4.47105 7.52898C4.21105 7.26898 3.78905 7.26898 3.52905 7.52898H3.53005C3.40536 7.65401 3.33533 7.82339 3.33533 7.99998C3.33533 8.17657 3.40536 8.34595 3.53005 8.47098L6.19505 11.138C6.2564 11.2024 6.33002 11.2539 6.41158 11.2895C6.49313 11.325 6.58097 11.3439 6.66992 11.345C6.75887 11.346 6.84715 11.3293 6.92954 11.2958C7.01193 11.2622 7.08679 11.2125 7.14969 11.1496C7.18485 11.1144 7.21588 11.0756 7.24227 11.0337L12.471 5.80499Z" fill="#808080"/>
</svg>

After

Width:  |  Height:  |  Size: 727 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.3 4.86177C11.5611 5.12212 11.5611 5.54423 11.3 5.80458L8.43002 8.66658L9.4902 9.72383L14.3658 4.86177C14.6269 4.60142 15.0502 4.60142 15.3112 4.86177C15.5723 5.12212 15.5723 5.54423 15.3112 5.80458L9.97014 11.1309C9.96777 11.1333 9.96538 11.1357 9.96297 11.1381C9.70189 11.3984 9.27861 11.3984 9.01753 11.1381L7.48459 9.60939L5.96419 11.1256L5.95181 11.1382C5.69073 11.3985 5.26745 11.3985 5.00637 11.1382L2.33228 8.47156C2.0712 8.21121 2.0712 7.7891 2.33228 7.52876C2.59335 7.26841 3.01664 7.26841 3.27771 7.52876L5.47893 9.72388L10.3546 4.86177C10.6156 4.60142 11.0389 4.60142 11.3 4.86177Z" fill="#006CFF"/>
</svg>

After

Width:  |  Height:  |  Size: 767 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.9998 7.99979C18.9998 7.73462 18.8945 7.48031 18.707 7.29279C18.5195 7.10532 18.2652 7 18 7C17.7349 7 17.4806 7.10532 17.293 7.29279L10 14.5858L6.70703 11.2928C6.5195 11.1053 6.26519 11 6.00003 11C5.73487 11 5.48056 11.1053 5.29303 11.2928C5.10556 11.4803 5.00024 11.7346 5.00024 11.9998C5.00024 12.265 5.10556 12.5193 5.29303 12.7068L9.29303 16.7068C9.48163 16.8889 9.73423 16.9897 9.99643 16.9875C9.99763 16.9875 9.99883 16.9874 10 16.9874C10.0012 16.9874 10.0024 16.9875 10.0036 16.9875C10.2658 16.9897 10.5184 16.8889 10.707 16.7068L18.707 8.70679C18.8945 8.51926 18.9998 8.26495 18.9998 7.99979Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 772 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C6.477 2 2 6.477 2 12C2 17.523 6.477 22 12 22C17.523 22 22 17.523 22 12C22 6.477 17.523 2 12 2ZM15.707 9.293C15.8945 9.48053 15.9998 9.73484 15.9998 10C15.9998 10.2652 15.8945 10.5195 15.707 10.707L11.707 14.707C11.5195 14.8945 11.2652 14.9998 11 14.9998C10.7348 14.9998 10.4805 14.8945 10.293 14.707L8.293 12.707C8.19749 12.6148 8.12131 12.5044 8.0689 12.3824C8.01649 12.2604 7.9889 12.1292 7.98775 11.9964C7.9866 11.8636 8.0119 11.7319 8.06218 11.609C8.11246 11.4861 8.18671 11.3745 8.28061 11.2806C8.3745 11.1867 8.48615 11.1125 8.60905 11.0622C8.73194 11.0119 8.86362 10.9866 8.9964 10.9877C9.12918 10.9889 9.2604 11.0165 9.3824 11.0689C9.50441 11.1213 9.61475 11.1975 9.707 11.293L11 12.586L14.293 9.293C14.4805 9.10553 14.7348 9.00021 15 9.00021C15.2652 9.00021 15.5195 9.10553 15.707 9.293V9.293Z" fill="#006CFF"/>
</svg>

After

Width:  |  Height:  |  Size: 979 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M22 12C22 6.48 17.52 2 12 2C6.48 2 2 6.48 2 12C2 17.52 6.48 22 12 22C17.52 22 22 17.52 22 12ZM8 11H12V8L16 12L12 16V13H8V11Z" fill="#808080"/>
</svg>

After

Width:  |  Height:  |  Size: 295 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C6.48 2 2 6.48 2 12C2 17.52 6.48 22 12 22C17.52 22 22 17.52 22 12C22 6.48 17.52 2 12 2ZM11 16V12H8L12 8L16 12H13V16H11Z" fill="#006BFE"/>
</svg>

After

Width:  |  Height:  |  Size: 295 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.05004 7.05011C6.86257 7.23764 6.75725 7.49195 6.75725 7.75711C6.75725 8.02227 6.86257 8.27658 7.05004 8.46411L10.586 12.0001L7.05004 15.5361C6.95453 15.6284 6.87835 15.7387 6.82594 15.8607C6.77353 15.9827 6.74594 16.1139 6.74479 16.2467C6.74363 16.3795 6.76894 16.5112 6.81922 16.6341C6.8695 16.757 6.94375 16.8686 7.03764 16.9625C7.13154 17.0564 7.24319 17.1307 7.36609 17.1809C7.48898 17.2312 7.62066 17.2565 7.75344 17.2554C7.88622 17.2542 8.01744 17.2266 8.13944 17.1742C8.26145 17.1218 8.37179 17.0456 8.46404 16.9501L12 13.4141L15.536 16.9501C15.7246 17.1323 15.9772 17.2331 16.2394 17.2308C16.5016 17.2285 16.7524 17.1233 16.9379 16.9379C17.1233 16.7525 17.2284 16.5017 17.2307 16.2395C17.233 15.9773 17.1322 15.7247 16.95 15.5361L13.414 12.0001L16.95 8.46411C17.1322 8.27551 17.233 8.02291 17.2307 7.76071C17.2284 7.49851 17.1233 7.2477 16.9379 7.06229C16.7524 6.87688 16.5016 6.77171 16.2394 6.76944C15.9772 6.76716 15.7246 6.86795 15.536 7.05011L12 10.5861L8.46404 7.05011C8.27651 6.86264 8.0222 6.75732 7.75704 6.75732C7.49188 6.75732 7.23757 6.86264 7.05004 7.05011V7.05011Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.05004 7.05011C6.86257 7.23764 6.75725 7.49195 6.75725 7.75711C6.75725 8.02227 6.86257 8.27658 7.05004 8.46411L10.586 12.0001L7.05004 15.5361C6.95453 15.6284 6.87835 15.7387 6.82594 15.8607C6.77353 15.9827 6.74594 16.1139 6.74479 16.2467C6.74363 16.3795 6.76894 16.5112 6.81922 16.6341C6.8695 16.757 6.94375 16.8686 7.03764 16.9625C7.13154 17.0564 7.24319 17.1307 7.36609 17.1809C7.48898 17.2312 7.62066 17.2565 7.75344 17.2554C7.88622 17.2542 8.01744 17.2266 8.13944 17.1742C8.26145 17.1218 8.37179 17.0456 8.46404 16.9501L12 13.4141L15.536 16.9501C15.7246 17.1323 15.9772 17.2331 16.2394 17.2308C16.5016 17.2285 16.7524 17.1233 16.9379 16.9379C17.1233 16.7525 17.2284 16.5017 17.2307 16.2395C17.233 15.9773 17.1322 15.7247 16.95 15.5361L13.414 12.0001L16.95 8.46411C17.1322 8.27551 17.233 8.02291 17.2307 7.76071C17.2284 7.49851 17.1233 7.2477 16.9379 7.06229C16.7524 6.87688 16.5016 6.77171 16.2394 6.76944C15.9772 6.76716 15.7246 6.86795 15.536 7.05011L12 10.5861L8.46404 7.05011C8.27651 6.86264 8.0222 6.75732 7.75704 6.75732C7.49188 6.75732 7.23757 6.86264 7.05004 7.05011V7.05011Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M2 12C2 6.477 6.477 2 12 2C17.523 2 22 6.477 22 12C22 17.523 17.523 22 12 22C6.477 22 2 17.523 2 12ZM6.34315 6.34315C4.84285 7.84344 4 9.87827 4 12C4 14.1217 4.84285 16.1566 6.34315 17.6569C7.84344 19.1571 9.87827 20 12 20C14.1217 20 16.1566 19.1571 17.6569 17.6569C19.1571 16.1566 20 14.1217 20 12C20 9.87827 19.1571 7.84344 17.6569 6.34315C16.1566 4.84285 14.1217 4 12 4C9.87827 4 7.84344 4.84285 6.34315 6.34315ZM14.4456 8.24707C14.3242 8.29735 14.2139 8.37105 14.121 8.46396L12 10.586L9.87895 8.46396C9.78604 8.37112 9.67575 8.29748 9.55439 8.24726C9.43302 8.19704 9.30295 8.17121 9.1716 8.17126C9.04025 8.1713 8.9102 8.19722 8.78886 8.24753C8.66753 8.29784 8.5573 8.37155 8.46445 8.46446C8.37161 8.55737 8.29797 8.66766 8.24775 8.78903C8.19753 8.91039 8.1717 9.04047 8.17175 9.17181C8.1718 9.30316 8.19771 9.43321 8.24802 9.55455C8.29833 9.67588 8.37204 9.78612 8.46495 9.87896L10.585 12L8.46495 14.121C8.37204 14.2138 8.29833 14.3241 8.24802 14.4454C8.19771 14.5667 8.1718 14.6968 8.17175 14.8281C8.1717 14.9595 8.19753 15.0896 8.24775 15.2109C8.29797 15.3323 8.37161 15.4426 8.46445 15.5355C8.5573 15.6284 8.66753 15.7021 8.78886 15.7524C8.9102 15.8027 9.04025 15.8287 9.1716 15.8287C9.30295 15.8287 9.43302 15.8029 9.55439 15.7527C9.67575 15.7025 9.78604 15.6288 9.87895 15.536L12.001 13.414L14.121 15.536C14.3086 15.7236 14.5631 15.8291 14.8285 15.8291C15.0939 15.8291 15.3484 15.7236 15.536 15.536C15.7236 15.3484 15.8291 15.0939 15.8291 14.8285C15.8291 14.5631 15.7236 14.3086 15.536 14.121L13.414 12L15.536 9.87896C15.6289 9.78605 15.7026 9.67575 15.7529 9.55436C15.8032 9.43296 15.8291 9.30286 15.8291 9.17146C15.8291 9.04007 15.8032 8.90996 15.7529 8.78856C15.7026 8.66717 15.6289 8.55687 15.536 8.46396C15.4431 8.37105 15.3328 8.29735 15.2114 8.24707C15.09 8.19678 14.9599 8.1709 14.8285 8.1709C14.6971 8.1709 14.567 8.19678 14.4456 8.24707Z" fill="#808080"/>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.0965 6.92857C9.94415 6.32452 10.9591 5.99988 12 5.99988C13.326 5.99988 14.5978 6.52667 15.5355 7.46435C16.4732 8.40203 17 9.6738 17 10.9999C17 11.2651 17.1053 11.5195 17.2929 11.707C17.4804 11.8945 17.7347 11.9999 18 11.9999C18.7956 11.9999 19.5587 12.316 20.1213 12.8786C20.6839 13.4412 21 14.2042 21 14.9999C21 15.7955 20.6839 16.5586 20.1213 17.1212C19.5587 17.6838 18.7956 17.9999 18 17.9999C17.7347 17.9999 17.4804 18.1052 17.2929 18.2928C17.1053 18.4803 17 18.7347 17 18.9999C17 19.2651 17.1053 19.5195 17.2929 19.707C17.4804 19.8945 17.7347 19.9999 18 19.9999C19.2423 19.9964 20.439 19.5308 21.3569 18.6936C22.2748 17.8563 22.8483 16.7075 22.9657 15.4706C23.0832 14.2338 22.7361 12.9976 21.9921 12.0026C21.2481 11.0076 20.1605 10.325 18.941 10.0879C18.7429 8.57347 18.055 7.16539 16.9821 6.07838C15.9092 4.99136 14.5103 4.28506 12.9986 4.0672C11.4869 3.84933 9.94545 4.13186 8.60929 4.87168C7.27313 5.6115 6.21559 6.76803 5.59797 8.16488C4.16853 8.50721 2.91447 9.36235 2.07375 10.568C1.23303 11.7737 0.864124 13.2461 1.03704 14.7057C1.20995 16.1654 1.91265 17.5108 3.01181 18.4867C4.11096 19.4625 5.5301 20.001 6.99997 19.9999C7.26518 19.9999 7.51954 19.8945 7.70707 19.707C7.89461 19.5195 7.99997 19.2651 7.99997 18.9999C7.99997 18.7347 7.89461 18.4803 7.70707 18.2928C7.51954 18.1052 7.26518 17.9999 6.99997 17.9999C5.98734 17.9976 5.01325 17.6114 4.27413 16.9192C3.53501 16.2271 3.08584 15.2804 3.01721 14.2701C2.94858 13.2598 3.26559 12.261 3.90431 11.4752C4.54304 10.6895 5.45595 10.1751 6.45897 10.0359C6.64276 10.0111 6.81603 9.93567 6.95943 9.81805C7.10282 9.70044 7.21069 9.54527 7.27097 9.36988C7.61071 8.38603 8.24885 7.53263 9.0965 6.92857ZM11.2929 12.2929C11.4804 12.1054 11.7348 12 12 12C12.2652 12 12.5196 12.1054 12.7071 12.2929C12.8946 12.4804 13 12.7348 13 13V16.5859L13.293 16.2929C13.4816 16.1108 13.7342 16.01 13.9964 16.0122C14.2586 16.0145 14.5094 16.1197 14.6948 16.3051C14.8803 16.4905 14.9854 16.7413 14.9877 17.0035C14.99 17.2657 14.8892 17.5183 14.707 17.7069L12.7206 19.6933C12.7162 19.698 12.7117 19.7026 12.7071 19.7071C12.5196 19.8946 12.2652 20 12 20C11.7348 20 11.4804 19.8946 11.2929 19.7071C11.2869 19.7011 11.281 19.695 11.2751 19.6889L9.29303 17.7068C9.10556 17.5193 9.00024 17.2649 9.00024 16.9998C9.00024 16.7346 9.10556 16.4803 9.29303 16.2928C9.48056 16.1053 9.73487 16 10 16C10.2652 16 10.5195 16.1053 10.707 16.2928L11 16.5858V13C11 12.7348 11.1054 12.4804 11.2929 12.2929Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 11C14.2091 11 16 9.20914 16 7C16 4.79086 14.2091 3 12 3C9.79086 3 8 4.79086 8 7C8 9.20914 9.79086 11 12 11ZM12 9C13.1046 9 14 8.10457 14 7C14 5.89543 13.1046 5 12 5C10.8954 5 10 5.89543 10 7C10 8.10457 10.8954 9 12 9ZM6.5 4C4.567 4 3 5.567 3 7.5C3 9.433 4.567 11 6.5 11C7.05228 11 7.5 10.5523 7.5 10C7.5 9.44772 7.05228 9 6.5 9C5.67157 9 5 8.32843 5 7.5C5 6.67157 5.67157 6 6.5 6C7.05228 6 7.5 5.55228 7.5 5C7.5 4.44772 7.05228 4 6.5 4ZM5 19C5 15.134 8.13401 12 12 12C15.866 12 19 15.134 19 19C19 19.397 18.9669 19.7869 18.903 20.1671C18.8115 20.7118 18.2958 21.0791 17.7511 20.9876C17.2065 20.8961 16.8391 20.3804 16.9306 19.8357C16.9762 19.5646 17 19.2855 17 19C17 16.2386 14.7614 14 12 14C9.23858 14 7 16.2386 7 19C7 19.2864 7.02397 19.5664 7.06981 19.8383C7.16161 20.3829 6.79454 20.8988 6.24994 20.9906C5.70533 21.0824 5.18943 20.7154 5.09763 20.1708C5.03335 19.7894 5 19.3982 5 19ZM4.85402 13.7725C5.28304 13.4247 5.34889 12.7949 5.0011 12.3659C4.6533 11.9369 4.02357 11.8711 3.59455 12.2188C2.01345 13.5006 1 15.4618 1 17.659C1 18.0572 1.03335 18.4484 1.09763 18.8297C1.18943 19.3743 1.70533 19.7414 2.24994 19.6496C2.79454 19.5578 3.16161 19.0419 3.06981 18.4973C3.02397 18.2253 3 17.9453 3 17.659C3 16.0903 3.72123 14.6908 4.85402 13.7725ZM21.5 7.5C21.5 5.567 19.933 4 18 4C17.4477 4 17 4.44772 17 5C17 5.55228 17.4477 6 18 6C18.8284 6 19.5 6.67157 19.5 7.5C19.5 8.32843 18.8284 9 18 9C17.4477 9 17 9.44772 17 10C17 10.5523 17.4477 11 18 11C19.933 11 21.5 9.433 21.5 7.5ZM19.3703 13.7725C18.9413 13.4247 18.8755 12.7949 19.2233 12.3659C19.5711 11.9369 20.2008 11.8711 20.6298 12.2188C22.2109 13.5006 23.2244 15.4618 23.2244 17.659C23.2244 18.0572 23.191 18.4484 23.1267 18.8297C23.0349 19.3743 22.519 19.7414 21.9744 19.6496C21.4298 19.5578 21.0628 19.0419 21.1546 18.4973C21.2004 18.2253 21.2244 17.9453 21.2244 17.659C21.2244 16.0903 20.5031 14.6908 19.3703 13.7725Z" fill="#006CFF"/>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 6V3C7 2.73478 7.10536 2.48043 7.29289 2.29289C7.48043 2.10536 7.73478 2 8 2H20C20.2652 2 20.5196 2.10536 20.7071 2.29289C20.8946 2.48043 21 2.73478 21 3V17C21 17.2652 20.8946 17.5196 20.7071 17.7071C20.5196 17.8946 20.2652 18 20 18H17V21C17 21.552 16.55 22 15.993 22H4.007C3.87555 22.0008 3.74522 21.9757 3.62347 21.9261C3.50172 21.8765 3.39093 21.8035 3.29742 21.7111C3.20391 21.6187 3.12952 21.5088 3.07849 21.3876C3.02746 21.2665 3.00079 21.1365 3 21.005V21L3.003 7C3.003 6.448 3.453 6 4.01 6H7ZM5.003 8L5 20H15V8H5.003ZM17 6H9V4H19V16H17V6Z" fill="#808080"/>
</svg>

After

Width:  |  Height:  |  Size: 718 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.66675 5.33339C2.66675 5.33342 2.66675 5.33346 2.66675 5.33349C2.66675 5.51032 2.73697 5.67991 2.86197 5.80499L4.86197 7.80499C4.92332 7.86941 4.99694 7.92091 5.07849 7.95645C5.16004 7.99199 5.24788 8.01086 5.33684 8.01194C5.42579 8.01302 5.51406 7.9963 5.59646 7.96276C5.67885 7.92922 5.7537 7.87953 5.81661 7.81663C5.87951 7.75373 5.9292 7.67887 5.96274 7.59648C5.99628 7.51408 6.013 7.42581 6.01192 7.33686C6.01083 7.2479 5.99197 7.16007 5.95642 7.07851C5.92088 6.99696 5.86939 6.92334 5.80497 6.86199L4.95612 6.01314C6.62432 6.04367 7.6477 6.12838 8.36399 6.27799C9.21799 6.45499 9.62699 6.72099 10.246 7.18499C10.882 7.74299 11.472 8.34299 11.912 9.16699C12.353 9.99599 12.667 11.094 12.667 12.667C12.6737 12.8393 12.7468 13.0023 12.8711 13.1219C12.9953 13.2414 13.1611 13.3082 13.3335 13.3082C13.5059 13.3082 13.6717 13.2414 13.7959 13.1219C13.9202 13.0023 13.9933 12.8393 14 12.667C14 10.906 13.647 9.58799 13.088 8.54099C12.532 7.49799 11.796 6.76899 11.106 6.16499L11.066 6.13299C10.364 5.60599 9.77199 5.20899 8.63599 4.97199C7.79563 4.79705 6.657 4.70933 4.93046 4.6793L5.80497 3.80479C5.86939 3.74344 5.92088 3.66982 5.95642 3.58827C5.99197 3.50671 6.01083 3.41887 6.01192 3.32992C6.013 3.24097 5.99628 3.1527 5.96274 3.0703C5.9292 2.98791 5.87951 2.91305 5.81661 2.85015C5.7537 2.78724 5.67885 2.73756 5.59646 2.70402C5.51406 2.67047 5.42579 2.65375 5.33684 2.65484C5.24788 2.65592 5.16004 2.67479 5.07849 2.71033C4.99694 2.74587 4.92332 2.79737 4.86197 2.86179L2.86197 4.86178C2.7795 4.9443 2.72087 5.0462 2.69053 5.15677C2.67586 5.21019 2.6678 5.26564 2.66684 5.32191C2.66678 5.3257 2.66675 5.32949 2.66675 5.33329" fill="#808080"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.954 7.459C11.6934 7.19579 9.98507 7.06374 7.39542 7.01852L8.70703 5.70692C8.88919 5.51831 8.98998 5.26572 8.9877 5.00351C8.98543 4.74132 8.88026 4.4905 8.69485 4.30509C8.50945 4.11969 8.25863 4.01453 7.99643 4.01224C7.73423 4.00997 7.48163 4.11077 7.29303 4.29292L4.30392 7.28203L4.29843 7.28743L4.29289 7.29289C4.10536 7.48043 4 7.73478 4 8C4 8.26522 4.10536 8.51957 4.29289 8.70711C4.29894 8.71316 4.30506 8.71912 4.31124 8.725L7.29303 11.7068C7.38528 11.8023 7.49562 11.8785 7.61763 11.9309C7.73963 11.9833 7.87085 12.0109 8.00363 12.0121C8.13641 12.0132 8.26809 11.9879 8.39098 11.9376C8.51388 11.8873 8.62553 11.8131 8.71942 11.7192C8.81332 11.6253 8.88758 11.5137 8.93785 11.3908C8.98813 11.2679 9.01343 11.1362 9.01228 11.0034C9.01113 10.8706 8.98354 10.7394 8.93113 10.6174C8.87872 10.4954 8.80254 10.385 8.70703 10.2928L7.43378 9.01954C9.93674 9.06498 11.4715 9.19141 12.546 9.417C13.828 9.683 14.441 10.082 15.37 10.777C16.324 11.614 17.208 12.515 17.868 13.752C18.53 14.994 19 16.64 19 19C19 19.2652 19.1054 19.5196 19.2929 19.7071C19.4804 19.8946 19.7348 20 20 20C20.2652 20 20.5196 19.8946 20.7071 19.7071C20.8946 19.5196 21 19.2652 21 19C21 16.36 20.47 14.38 19.632 12.811C18.798 11.247 17.694 10.153 16.658 9.247C16.6393 9.23062 16.6199 9.21494 16.6 9.2C15.546 8.409 14.658 7.814 12.954 7.459Z" fill="#808080"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9 2C8.44771 2 8 2.44771 8 3V4H5C4.44771 4 4 4.44772 4 5C4 5.55228 4.44771 6 5 6H19C19.5523 6 20 5.55228 20 5C20 4.44772 19.5523 4 19 4H16V3C16 2.44771 15.5523 2 15 2H9ZM7 8C7 7.44772 6.55228 7 6 7C5.44772 7 5 7.44772 5 8V19C5 20.6569 6.34315 22 8 22H16C17.6569 22 19 20.6569 19 19V8C19 7.44772 18.5523 7 18 7C17.4477 7 17 7.44772 17 8V19C17 19.5523 16.5523 20 16 20H8C7.44772 20 7 19.5523 7 19V8ZM10 8C10.5523 8 11 8.44771 11 9V17C11 17.5523 10.5523 18 10 18C9.44771 18 9 17.5523 9 17V9C9 8.44771 9.44771 8 10 8ZM15 9C15 8.44771 14.5523 8 14 8C13.4477 8 13 8.44771 13 9V17C13 17.5523 13.4477 18 14 18C14.5523 18 15 17.5523 15 17V9Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 801 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.30605 8.30597C5.11052 8.50222 5.00073 8.76795 5.00073 9.04498C5.00073 9.322 5.11052 9.58774 5.30605 9.78398L11.2061 15.697C11.4321 15.923 11.7351 16.024 12.0291 16C12.17 16.0032 12.3102 15.9778 12.441 15.9252C12.5718 15.8726 12.6906 15.7939 12.7901 15.694L18.6941 9.78398C18.8898 9.58786 18.9998 9.32208 18.9998 9.04498C18.9998 8.76787 18.8898 8.50209 18.6941 8.30597C18.5972 8.20898 18.4821 8.13203 18.3555 8.07952C18.2289 8.02702 18.0931 8 17.9561 8C17.819 8 17.6832 8.02702 17.5566 8.07952C17.43 8.13203 17.3149 8.20898 17.2181 8.30597L11.9981 13.533L6.78005 8.30597C6.68327 8.20901 6.56831 8.13208 6.44176 8.07959C6.31521 8.0271 6.17956 8.00008 6.04255 8.00008C5.90555 8.00008 5.76989 8.0271 5.64334 8.07959C5.51679 8.13208 5.40184 8.20901 5.30505 8.30597H5.30605Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 900 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 4C11.7348 4 11.4804 4.10536 11.2929 4.29289C11.1054 4.48043 11 4.73478 11 5V14.529L6.782 10.306C6.68513 10.209 6.57009 10.1321 6.44346 10.0795C6.31683 10.027 6.18108 10 6.044 10C5.90692 10 5.77118 10.027 5.64454 10.0795C5.51791 10.1321 5.40287 10.209 5.306 10.306C5.11022 10.5021 5.00027 10.7679 5.00027 11.045C5.00027 11.3221 5.11022 11.5879 5.306 11.784L11.21 17.694C11.427 17.911 11.716 18.013 12 17.999C12.284 18.013 12.573 17.911 12.79 17.694L18.694 11.784C18.8898 11.5879 18.9997 11.3221 18.9997 11.045C18.9997 10.7679 18.8898 10.5021 18.694 10.306C18.5971 10.209 18.4821 10.1321 18.3555 10.0795C18.2288 10.027 18.0931 10 17.956 10C17.8189 10 17.6832 10.027 17.5565 10.0795C17.4299 10.1321 17.3149 10.209 17.218 10.306L13 14.529V5C13 4.73478 12.8946 4.48043 12.7071 4.29289C12.5196 4.10536 12.2652 4 12 4ZM5 21C5 20.7348 5.10536 20.4804 5.29289 20.2929C5.48043 20.1054 5.73478 20 6 20H18C18.2652 20 18.5196 20.1054 18.7071 20.2929C18.8946 20.4804 19 20.7348 19 21C19 21.2652 18.8946 21.5196 18.7071 21.7071C18.5196 21.8946 18.2652 22 18 22H6C5.73478 22 5.48043 21.8946 5.29289 21.7071C5.10536 21.5196 5 21.2652 5 21Z" fill="#808080"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M21 21.0001H3V16.7571L16.435 3.32208C16.8255 2.9317 17.4585 2.9317 17.849 3.32208L20.678 6.15108C21.0684 6.54158 21.0684 7.17458 20.678 7.56508L9.243 19.0001H21V21.0001ZM5 19.0001H6.414L15.728 9.68609L14.314 8.27208L5 17.5861V19.0001ZM18.556 6.85808L17.142 8.27208L15.728 6.85808L17.142 5.44408L18.556 6.85808Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 479 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M20 10C20 15.523 15.523 20 10 20C4.477 20 0 15.523 0 10C0 4.477 4.477 0 10 0C15.523 0 20 4.477 20 10ZM15.6569 15.6569C14.1566 17.1571 12.1217 18 10 18C7.87827 18 5.84344 17.1571 4.34315 15.6569C2.84285 14.1566 2 12.1217 2 10C2 7.87827 2.84285 5.84344 4.34315 4.34315C5.84344 2.84285 7.87827 2 10 2C12.1217 2 14.1566 2.84285 15.6569 4.34315C17.1571 5.84344 18 7.87827 18 10C18 12.1217 17.1571 14.1566 15.6569 15.6569ZM7.07416 14.411C7.12237 14.5267 7.19303 14.6317 7.28204 14.7199L7.28004 14.7209C7.46107 14.9 7.70542 15.0004 7.96004 15.0004C8.21466 15.0004 8.45901 14.9 8.64004 14.7209L12.719 10.6739C12.8083 10.5858 12.8792 10.4809 12.9275 10.3652C12.9759 10.2495 13.0008 10.1253 13.0008 9.9999C13.0008 9.87449 12.9759 9.75034 12.9275 9.63464C12.8792 9.51894 12.8083 9.41399 12.719 9.3259L8.64104 5.2789C8.46018 5.09991 8.216 4.99951 7.96154 4.99951C7.70708 4.99951 7.4629 5.09991 7.28204 5.2789C7.19288 5.36715 7.1221 5.4722 7.07379 5.58797C7.02548 5.70375 7.00061 5.82795 7.00061 5.9534C7.00061 6.07885 7.02548 6.20305 7.07379 6.31883C7.1221 6.4346 7.19288 6.53965 7.28204 6.6279L10.68 9.9999L7.28204 13.3719C7.19303 13.4601 7.12237 13.5651 7.07416 13.6808C7.02594 13.7965 7.00112 13.9206 7.00112 14.0459C7.00112 14.1712 7.02594 14.2953 7.07416 14.411Z" fill="#006CFF"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 22C6.477 22 2 17.523 2 12C2 6.477 6.477 2 12 2C17.523 2 22 6.477 22 12C22 17.523 17.523 22 12 22ZM11 15V17H13V15H11ZM11 13V7H13V13H11Z" fill="#FF3742"/>
</svg>

After

Width:  |  Height:  |  Size: 309 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M15.213 8.00022C14.9005 6.31539 14.0081 4.79309 12.6906 3.69745C11.3731 2.60181 9.71355 2.00195 7.99999 2.00195C6.28642 2.00195 4.62692 2.60181 3.3094 3.69745C1.99187 4.79309 1.09946 6.31539 0.786987 8.00022C1.09946 9.68506 1.99187 11.2073 3.3094 12.303C4.62692 13.3986 6.28642 13.9985 7.99999 13.9985C9.71355 13.9985 11.3731 13.3986 12.6906 12.303C14.0081 11.2073 14.9005 9.68506 15.213 8.00022V8.00022ZM13.85 8.00022C13.5474 9.3256 12.804 10.509 11.7412 11.3569C10.6785 12.2047 9.35947 12.6667 7.99999 12.6672C6.64034 12.6669 5.32107 12.205 4.25814 11.3572C3.19521 10.5093 2.45158 9.32577 2.14899 8.00022C2.45323 6.67633 3.1975 5.49476 4.26017 4.6486C5.32284 3.80244 6.64108 3.34171 7.99949 3.34171C9.35789 3.34171 10.6761 3.80244 11.7388 4.6486C12.8015 5.49476 13.5457 6.67633 13.85 8.00022V8.00022ZM7.99999 11.0002C7.53836 11 7.083 10.8933 6.66935 10.6884C6.2557 10.4835 5.89493 10.1859 5.6151 9.81873C5.33528 9.45159 5.14396 9.02482 5.05603 8.57164C4.9681 8.11846 4.98594 7.65111 5.10816 7.20596C5.23038 6.7608 5.45367 6.34986 5.76067 6.00511C6.06767 5.66036 6.45009 5.39111 6.87815 5.21832C7.30622 5.04552 7.76839 4.97385 8.22869 5.00887C8.68898 5.04389 9.13499 5.18466 9.53199 5.42022C9.17569 5.30027 8.7893 5.30428 8.43556 5.4316C8.08183 5.55892 7.78152 5.80208 7.58339 6.12158C7.38526 6.44109 7.30096 6.81819 7.34416 7.19165C7.38736 7.56511 7.55553 7.913 7.82137 8.17884C8.0872 8.44468 8.4351 8.61285 8.80856 8.65605C9.18202 8.69925 9.55912 8.61495 9.87862 8.41682C10.1981 8.21869 10.4413 7.91838 10.5686 7.56464C10.6959 7.21091 10.6999 6.82452 10.58 6.46822C10.8505 6.92374 10.9956 7.44279 11.0005 7.97257C11.0054 8.50236 10.8699 9.02399 10.6078 9.48442C10.3457 9.94485 9.9663 10.3277 9.50824 10.5939C9.05019 10.8601 8.5298 11.0003 7.99999 11.0002V11.0002ZM10.58 6.46822C10.4977 6.2247 10.3603 6.00344 10.1785 5.82168C9.99677 5.63991 9.77551 5.50253 9.53199 5.42022C9.96199 5.67622 10.324 6.03722 10.58 6.46822Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.72998 5C4.72998 4.73478 4.83534 4.48043 5.02287 4.29289C5.21041 4.10536 5.46476 4 5.72998 4H7.72998C7.9952 4 8.24955 4.10536 8.43709 4.29289C8.62462 4.48043 8.72998 4.73478 8.72998 5V7C8.72998 7.26522 8.62462 7.51957 8.43709 7.70711C8.24955 7.89464 7.9952 8 7.72998 8H5.72998C5.46476 8 5.21041 7.89464 5.02287 7.70711C4.83534 7.51957 4.72998 7.26522 4.72998 7V5ZM10.73 5C10.73 4.73478 10.8353 4.48043 11.0229 4.29289C11.2104 4.10536 11.4648 4 11.73 4H13.73C13.9952 4 14.2496 4.10536 14.4371 4.29289C14.6246 4.48043 14.73 4.73478 14.73 5V7C14.73 7.26522 14.6246 7.51957 14.4371 7.70711C14.2496 7.89464 13.9952 8 13.73 8H11.73C11.4648 8 11.2104 7.89464 11.0229 7.70711C10.8353 7.51957 10.73 7.26522 10.73 7V5ZM17.73 4C17.4648 4 17.2104 4.10536 17.0229 4.29289C16.8353 4.48043 16.73 4.73478 16.73 5V7C16.73 7.26522 16.8353 7.51957 17.0229 7.70711C17.2104 7.89464 17.4648 8 17.73 8H19.73C19.9952 8 20.2496 7.89464 20.4371 7.70711C20.6246 7.51957 20.73 7.26522 20.73 7V5C20.73 4.73478 20.6246 4.48043 20.4371 4.29289C20.2496 4.10536 19.9952 4 19.73 4H17.73ZM4.72998 11C4.72998 10.7348 4.83534 10.4804 5.02287 10.2929C5.21041 10.1054 5.46476 10 5.72998 10H7.72998C7.9952 10 8.24955 10.1054 8.43709 10.2929C8.62462 10.4804 8.72998 10.7348 8.72998 11V13C8.72998 13.2652 8.62462 13.5196 8.43709 13.7071C8.24955 13.8946 7.9952 14 7.72998 14H5.72998C5.46476 14 5.21041 13.8946 5.02287 13.7071C4.83534 13.5196 4.72998 13.2652 4.72998 13V11ZM11.73 10C11.4648 10 11.2104 10.1054 11.0229 10.2929C10.8353 10.4804 10.73 10.7348 10.73 11V13C10.73 13.2652 10.8353 13.5196 11.0229 13.7071C11.2104 13.8946 11.4648 14 11.73 14H13.73C13.9952 14 14.2496 13.8946 14.4371 13.7071C14.6246 13.5196 14.73 13.2652 14.73 13V11C14.73 10.7348 14.6246 10.4804 14.4371 10.2929C14.2496 10.1054 13.9952 10 13.73 10H11.73ZM16.73 11C16.73 10.7348 16.8353 10.4804 17.0229 10.2929C17.2104 10.1054 17.4648 10 17.73 10H19.73C19.9952 10 20.2496 10.1054 20.4371 10.2929C20.6246 10.4804 20.73 10.7348 20.73 11V13C20.73 13.2652 20.6246 13.5196 20.4371 13.7071C20.2496 13.8946 19.9952 14 19.73 14H17.73C17.4648 14 17.2104 13.8946 17.0229 13.7071C16.8353 13.5196 16.73 13.2652 16.73 13V11ZM5.72998 16C5.46476 16 5.21041 16.1054 5.02287 16.2929C4.83534 16.4804 4.72998 16.7348 4.72998 17V19C4.72998 19.2652 4.83534 19.5196 5.02287 19.7071C5.21041 19.8946 5.46476 20 5.72998 20H7.72998C7.9952 20 8.24955 19.8946 8.43709 19.7071C8.62462 19.5196 8.72998 19.2652 8.72998 19V17C8.72998 16.7348 8.62462 16.4804 8.43709 16.2929C8.24955 16.1054 7.9952 16 7.72998 16H5.72998ZM10.73 17C10.73 16.7348 10.8353 16.4804 11.0229 16.2929C11.2104 16.1054 11.4648 16 11.73 16H13.73C13.9952 16 14.2496 16.1054 14.4371 16.2929C14.6246 16.4804 14.73 16.7348 14.73 17V19C14.73 19.2652 14.6246 19.5196 14.4371 19.7071C14.2496 19.8946 13.9952 20 13.73 20H11.73C11.4648 20 11.2104 19.8946 11.0229 19.7071C10.8353 19.5196 10.73 19.2652 10.73 19V17ZM17.73 16C17.4648 16 17.2104 16.1054 17.0229 16.2929C16.8353 16.4804 16.73 16.7348 16.73 17V19C16.73 19.2652 16.8353 19.5196 17.0229 19.7071C17.2104 19.8946 17.4648 20 17.73 20H19.73C19.9952 20 20.2496 19.8946 20.4371 19.7071C20.6246 19.5196 20.73 19.2652 20.73 19V17C20.73 16.7348 20.6246 16.4804 20.4371 16.2929C20.2496 16.1054 19.9952 16 19.73 16H17.73Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.694 18.6943C16.102 18.2867 16.102 17.6259 15.694 17.2184L10.4699 12L15.694 6.78165C16.102 6.37408 16.102 5.71326 15.694 5.30568C15.2859 4.89811 14.6244 4.8981 14.2164 5.30568L8.30602 11.2096C8.08861 11.4267 7.98704 11.7158 8.00132 12.0002C7.98713 12.2844 8.0887 12.5733 8.30603 12.7904L14.2164 18.6943C14.6244 19.1019 15.2859 19.1019 15.694 18.6943Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 522 B

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.636 4H16L13.09 9.818H16L10.546 20L11.636 12.727H8L11.636 4Z" fill="#808080"/>
</svg>

After

Width:  |  Height:  |  Size: 194 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 21C12.908 21.034 19.233 16.949 21.135 12.854C22.289 10.37 22.288 8.061 21.132 6.351C20.136 4.879 18.396 4 16.478 4C13.712 4 12.575 6.684 12 6.684C11.425 6.684 10.422 4 7.52299 4C5.603 4 3.86299 4.879 2.86799 6.35C1.71199 8.06 1.71099 10.37 2.86499 12.855C4.76699 16.948 11.092 20.966 12 21Z" fill="#808080"/>
</svg>

After

Width:  |  Height:  |  Size: 465 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 5C7.46774 5 4 8.24148 4 12C4 15.7585 7.46774 19 12 19C12.6209 19 13.224 18.9381 13.8023 18.8215C13.9231 18.7971 14.0473 18.7953 14.1687 18.8161L19.4315 19.717L18.3869 17.0167C18.258 16.6833 18.3168 16.3064 18.5412 16.0282C19.4673 14.8799 20 13.4904 20 12C20 8.24148 16.5323 5 12 5ZM2 12C2 6.92196 6.59113 3 12 3C17.4089 3 22 6.92196 22 12C22 13.7774 21.4276 15.4282 20.4527 16.8133L21.9327 20.6392C22.0625 20.9749 22.0019 21.3545 21.774 21.6332C21.5461 21.9118 21.1861 22.0464 20.8313 21.9857L14.0104 20.8181C13.3601 20.9375 12.6877 21 12 21C6.59113 21 2 17.078 2 12Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 740 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.889 16H2C1.73478 16 1.48043 15.8946 1.29289 15.7071C1.10536 15.5196 1 15.2652 1 15V9.00002C1 8.7348 1.10536 8.48045 1.29289 8.29291C1.48043 8.10538 1.73478 8.00002 2 8.00002H5.889L11.183 3.66802C11.2563 3.60797 11.3451 3.56995 11.4391 3.55841C11.5331 3.54687 11.6284 3.56227 11.714 3.60282C11.7996 3.64336 11.872 3.70739 11.9226 3.78745C11.9732 3.86751 12.0001 3.9603 12 4.05502V19.945C12.0001 20.0397 11.9732 20.1325 11.9226 20.2126C11.872 20.2926 11.7996 20.3567 11.714 20.3972C11.6284 20.4377 11.5331 20.4531 11.4391 20.4416C11.3451 20.4301 11.2563 20.3921 11.183 20.332L5.89 16H5.889ZM10 7.22002L6.603 10H3V14H6.603L10 16.78V7.22002ZM23.95 15.536L20.414 12L23.95 8.46402L22.536 7.05002L19 10.586L15.464 7.05002L14.05 8.46402L17.586 12L14.05 15.536L15.464 16.95L19 13.414L22.536 16.95L23.95 15.536Z" fill="#808080"/>
</svg>

After

Width:  |  Height:  |  Size: 975 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M21 21.0001H3V16.7571L16.435 3.32208C16.8255 2.9317 17.4585 2.9317 17.849 3.32208L20.678 6.15108C21.0684 6.54158 21.0684 7.17458 20.678 7.56508L9.243 19.0001H21V21.0001ZM5 19.0001H6.414L15.728 9.68609L14.314 8.27208L5 17.5861V19.0001ZM18.556 6.85808L17.142 8.27208L15.728 6.85808L17.142 5.44408L18.556 6.85808Z" fill="#006CFF"/>
</svg>

After

Width:  |  Height:  |  Size: 481 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 12C8.89543 12 8 12.8954 8 14V22C8 23.1046 8.89543 24 10 24H18C19.1046 24 20 23.1046 20 22V14C20 12.8954 19.1046 12 18 12H10ZM10 14H18V22H10V14ZM28 13C28 12.6397 27.8062 12.3073 27.4927 12.1298C27.1792 11.9523 26.7944 11.9572 26.4855 12.1425L21.4855 15.1425C21.1843 15.3232 21 15.6487 21 16V20C21 20.3513 21.1843 20.6768 21.4855 20.8575L26.4855 23.8575C26.7944 24.0429 27.1792 24.0477 27.4927 23.8702C27.8062 23.6927 28 23.3603 28 23V13ZM23 19.4338V16.5662L26 14.7662V21.2338L23 19.4338Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 659 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.30597 18.6938C8.71397 19.1018 9.37598 19.1018 9.78398 18.6938L15.697 12.7938C15.8045 12.6868 15.8873 12.5576 15.9397 12.4153C15.992 12.273 16.0126 12.1209 16 11.9698C16.0032 11.8288 15.9778 11.6886 15.9252 11.5578C15.8726 11.427 15.7939 11.3082 15.694 11.2088L9.78398 5.30476C9.58786 5.10898 9.32208 4.99902 9.04498 4.99902C8.76787 4.99902 8.50209 5.10898 8.30597 5.30476C8.20898 5.40163 8.13203 5.51667 8.07952 5.6433C8.02702 5.76993 8 5.90567 8 6.04276C8 6.17984 8.02702 6.31558 8.07952 6.44221C8.13203 6.56885 8.20898 6.68389 8.30597 6.78076L13.533 12.0008L8.30597 17.2178C8.20901 17.3145 8.13208 17.4295 8.07959 17.556C8.0271 17.6826 8.00008 17.8183 8.00008 17.9553C8.00008 18.0923 8.0271 18.2279 8.07959 18.3545C8.13208 18.481 8.20901 18.596 8.30597 18.6928V18.6938Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 903 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11 2C15.968 2 20 6.032 20 11C20 15.968 15.968 20 11 20C6.032 20 2 15.968 2 11C2 6.032 6.032 2 11 2ZM11 18C14.867 18 18 14.867 18 11C18 7.132 14.867 4 11 4C7.132 4 4 7.132 4 11C4 14.867 7.132 18 11 18ZM22.314 20.899L19.485 18.071L18.071 19.485L20.899 22.314L22.314 20.899Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 441 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.34315 6.34315C7.84344 4.84285 9.87827 4 12 4C14.1217 4 16.1566 4.84285 17.6569 6.34315C19.1571 7.84344 20 9.87827 20 12C20 14.1217 19.1571 16.1566 17.6569 17.6569C16.1566 19.1571 14.1217 20 12 20C9.87827 20 7.84344 19.1571 6.34315 17.6569C4.84285 16.1566 4 14.1217 4 12C4 9.87827 4.84285 7.84344 6.34315 6.34315ZM12 2C6.477 2 2 6.477 2 12C2 17.523 6.477 22 12 22C17.523 22 22 17.523 22 12C22 6.477 17.523 2 12 2ZM10.5607 10.5607C10.842 10.2794 11 9.89782 11 9.5C11 9.10218 10.842 8.72064 10.5607 8.43934C10.2794 8.15804 9.89787 8 9.50004 8C9.10222 8 8.72069 8.15804 8.43938 8.43934C8.15808 8.72064 8.00004 9.10218 8.00004 9.5C8.00004 9.89782 8.15808 10.2794 8.43938 10.5607C8.72069 10.842 9.10222 11 9.50004 11C9.89787 11 10.2794 10.842 10.5607 10.5607ZM15.5607 10.5607C15.842 10.2794 16 9.89782 16 9.5C16 9.10218 15.842 8.72064 15.5607 8.43934C15.2794 8.15804 14.8979 8 14.5 8C14.1022 8 13.7207 8.15804 13.4394 8.43934C13.1581 8.72064 13 9.10218 13 9.5C13 9.89782 13.1581 10.2794 13.4394 10.5607C13.7207 10.842 14.1022 11 14.5 11C14.8979 11 15.2794 10.842 15.5607 10.5607ZM7.78345 13.023C7.65516 13.0514 7.53371 13.1047 7.42604 13.18V13.181C7.31848 13.2564 7.22681 13.3522 7.15629 13.463C7.08576 13.5738 7.03775 13.6974 7.01499 13.8268C6.99224 13.9561 6.9952 14.0887 7.02369 14.2169C7.05218 14.3451 7.10565 14.4665 7.18104 14.574C7.85804 15.541 9.15404 16.744 10.884 17.104C12.708 17.483 14.759 16.874 16.743 14.669C16.834 14.5719 16.9046 14.4577 16.9509 14.3331C16.9971 14.2084 17.018 14.0757 17.0124 13.9429C17.0067 13.81 16.9746 13.6796 16.918 13.5593C16.8613 13.439 16.7812 13.3312 16.6824 13.2422C16.5836 13.1532 16.468 13.0848 16.3424 13.041C16.2168 12.9973 16.0838 12.979 15.9511 12.9873C15.8183 12.9955 15.6886 13.0302 15.5694 13.0892C15.4503 13.1482 15.3441 13.2304 15.257 13.331C13.641 15.126 12.275 15.35 11.291 15.146C10.212 14.922 9.30904 14.125 8.81904 13.427C8.74383 13.3193 8.64812 13.2274 8.53739 13.1567C8.42667 13.086 8.30309 13.0378 8.17373 13.0149C8.04436 12.9919 7.91174 12.9947 7.78345 13.023Z" fill="#006CFF"/>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 7.681C5 8.923 5.087 10.713 5.394 11.211C5.7 11.708 8.37 14.172 9.234 16.179C10.097 18.186 10.246 21 11.664 21C13.083 21 13.547 19.927 13.397 17.723C13.247 15.519 12.193 14.158 12.934 13.622C13.437 13.257 14.325 13.853 16.48 13.853C17.418 13.853 19 13.853 19 12.595C19 11.337 18.723 11.481 18.596 10.627C18.47 9.773 18.851 9.359 18.43 8.396C18.01 7.432 17.492 7.314 17.25 6.624C17.01 5.934 17.095 5.192 15.968 4.181C14.842 3.17 13.045 3 11.665 3C10.285 3 6.183 3.462 5.619 4.768C5.054 6.074 5 6.55 5 7.681Z" fill="#808080"/>
</svg>

After

Width:  |  Height:  |  Size: 679 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 16.3185C5 15.0769 5.08724 13.2867 5.39357 12.7892C5.6999 12.2917 8.37046 9.82769 9.23394 7.8211C10.0974 5.81451 10.2463 3 11.6645 3C13.0826 3 13.5471 4.07299 13.3966 6.27715C13.2462 8.48132 12.1925 9.8417 12.9335 10.3785C13.437 10.7433 14.3252 10.1467 16.4795 10.1467C17.418 10.1467 19 10.1467 19 11.4049C19 12.663 18.7229 12.5186 18.5964 13.3727C18.47 14.2267 18.8508 14.6408 18.4302 15.6044C18.0095 16.568 17.4917 16.6862 17.2509 17.3763C17.0102 18.0664 17.0945 18.8083 15.9684 19.8194C14.8422 20.8305 13.0448 21 11.6645 21C10.2842 21 6.18342 20.5376 5.61863 19.2318C5.05383 17.9261 5 17.4502 5 16.3185Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 777 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22ZM12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20ZM13 8C12.4477 8 12 8.44772 12 9V12H9C8.44771 12 8 12.4477 8 13C8 13.5523 8.44772 14 9 14H13C13.5523 14 14 13.5523 14 13V9C14 8.44772 13.5523 8 13 8Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 543 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 11C14.2091 11 16 9.20914 16 7C16 4.79086 14.2091 3 12 3C9.79086 3 8 4.79086 8 7C8 9.20914 9.79086 11 12 11ZM12 9C13.1046 9 14 8.10457 14 7C14 5.89543 13.1046 5 12 5C10.8954 5 10 5.89543 10 7C10 8.10457 10.8954 9 12 9ZM12 14C6.4686 14 4 17.6324 4 20C4 20.5523 3.55228 21 3 21C2.44771 21 2 20.5523 2 20C2 16.3676 5.5314 12 12 12C18.4686 12 22 16.3676 22 20C22 20.5523 21.5523 21 21 21C20.4477 21 20 20.5523 20 20C20 17.6324 17.5314 14 12 14Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 612 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.3848 10.2113C14.5409 10.0954 14.6893 9.96758 14.8284 9.82843C15.5786 9.07828 16 8.06087 16 7C16 5.93913 15.5786 4.92172 14.8284 4.17157C14.0783 3.42143 13.0609 3 12 3C10.9391 3 9.92172 3.42143 9.17157 4.17157C8.42143 4.92172 8 5.93913 8 7C8 8.06087 8.42143 9.07828 9.17157 9.82843C9.31108 9.96795 9.45984 10.0961 9.6164 10.2123C4.66867 11.1287 2 14.8425 2 18C2 18.2652 2.10536 18.5196 2.29289 18.7071C2.48043 18.8946 2.73478 19 3 19C3.26522 19 3.51957 18.8946 3.70711 18.7071C3.89464 18.5196 4 18.2652 4 18C4 15.632 6.469 12 12 12C13.716 12 15.135 12.353 16.272 12.9C16.3909 12.9634 16.5214 13.0021 16.6556 13.0138C16.7898 13.0255 16.925 13.01 17.0531 12.9682C17.1812 12.9263 17.2995 12.859 17.4009 12.7703C17.5023 12.6816 17.5847 12.5733 17.6433 12.452C17.7018 12.3306 17.7352 12.1987 17.7414 12.0641C17.7477 11.9295 17.7267 11.795 17.6797 11.6688C17.6327 11.5425 17.5607 11.427 17.4679 11.3293C17.3752 11.2315 17.2636 11.1536 17.14 11.1C16.3179 10.7034 15.3984 10.3985 14.3848 10.2113ZM13.4142 8.41421C13.0391 8.78929 12.5304 9 12 9C11.4696 9 10.9609 8.78929 10.5858 8.41421C10.2107 8.03914 10 7.53043 10 7C10 6.46957 10.2107 5.96086 10.5858 5.58579C10.9609 5.21071 11.4696 5 12 5C12.5304 5 13.0391 5.21071 13.4142 5.58579C13.7893 5.96086 14 6.46957 14 7C14 7.53043 13.7893 8.03914 13.4142 8.41421ZM20.414 14.172L21.828 15.586L20.414 17L21.828 18.414L20.414 19.828L19 18.414L17.586 19.828L16.172 18.414L17.586 17L16.172 15.586L17.586 14.172L19 15.586L20.414 14.172Z" fill="#808080"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.125 16.81C18.2702 16.7988 18.4075 16.7393 18.515 16.641C18.5678 16.5937 18.6097 16.5355 18.6376 16.4704C18.6656 16.4053 18.6791 16.3348 18.677 16.264C18.753 15.426 18.937 14.144 19.229 12.419C19.52 10.694 19.834 9.014 20.172 7.379C20.509 5.745 20.778 4.634 20.977 4.048C20.9922 3.99329 20.9999 3.93678 21 3.88C20.9937 3.73899 20.9365 3.60504 20.839 3.503C20.532 3.196 20.252 3.035 19.999 3.021C19.8345 3.01052 19.6698 3.00352 19.505 3C19.171 2.99954 18.8383 3.04189 18.515 3.126C18.1917 3.21046 17.8762 3.32254 17.572 3.461C17.358 3.545 17.235 3.691 17.204 3.901C17.0133 5.15788 16.8256 6.41522 16.641 7.673C16.372 9.489 16.161 11.078 16.008 12.44C15.855 13.802 15.778 14.756 15.778 15.3C15.778 15.664 15.801 15.922 15.848 16.076C16.031 16.369 16.346 16.565 16.79 16.662C17.235 16.76 17.603 16.809 17.895 16.809H18.125V16.81ZM9.165 16.369C9.0984 16.4491 9.01597 16.5146 8.92288 16.5615C8.82979 16.6083 8.72804 16.6353 8.624 16.641C8.317 16.641 8.007 16.603 7.692 16.526C7.378 16.449 7.19 16.292 7.129 16.055C7.083 15.845 7.059 15.545 7.059 15.154C7.059 14.972 7.071 14.647 7.094 14.179C7.117 13.711 7.205 13.135 7.359 12.45C7.512 11.766 7.749 11.277 8.072 10.984C8.317 10.802 8.593 10.607 8.9 10.397C9.64611 9.93899 10.3269 9.38229 10.924 8.742C11.308 8.336 11.499 7.896 11.499 7.422C11.499 7.156 11.438 6.884 11.315 6.604C11.208 6.339 11.047 6.15 10.832 6.038C10.632 5.927 10.342 5.871 9.958 5.871H9.843C8.463 5.941 7.619 6.346 7.313 7.086C7.21115 7.32493 7.15648 7.58131 7.152 7.841C7.152 8.301 7.328 8.735 7.681 9.14C7.77817 9.24219 7.83501 9.37612 7.841 9.517C7.841 9.559 7.823 9.639 7.784 9.757C7.746 9.877 7.627 9.977 7.428 10.062C7.0802 10.184 6.71458 10.2476 6.346 10.25C5.902 10.25 5.415 10.104 4.886 9.81C4.356 9.517 4.061 8.867 4 7.861V7.778C4 7.079 4.203 6.36 4.61 5.619C5.016 4.879 5.695 4.257 6.645 3.754C7.505 3.307 8.616 3.084 9.981 3.084H10.349C11.591 3.126 12.646 3.52 13.512 4.268C14.379 5.015 14.866 5.948 14.972 7.065V7.191C14.972 8.057 14.666 8.77 14.052 9.328C13.426 9.89565 12.7304 10.3815 11.982 10.774C11.092 11.235 10.48 11.724 10.142 12.241C9.85597 12.799 9.63968 13.3901 9.498 14.001C9.39431 14.4053 9.34025 14.8207 9.337 15.238C9.337 15.433 9.352 15.628 9.383 15.824V15.866C9.383 16.034 9.31 16.201 9.164 16.369H9.165ZM9.601 20.466C9.22353 20.8167 8.72518 21.008 8.21 21C7.6871 21.0112 7.18011 20.8199 6.795 20.466C6.404 20.109 6.208 19.686 6.208 19.198C6.208 18.695 6.404 18.265 6.795 17.909C7.18011 17.5551 7.6871 17.3638 8.21 17.375C8.746 17.375 9.21 17.553 9.601 17.909C9.993 18.265 10.188 18.695 10.188 19.198C10.188 19.687 9.993 20.109 9.601 20.466ZM18.32 20.466C17.9349 20.8199 17.4279 21.0112 16.905 21C16.368 21 15.905 20.822 15.513 20.466C15.123 20.109 14.927 19.686 14.927 19.198C14.927 18.695 15.123 18.265 15.514 17.909C15.8915 17.5583 16.3898 17.367 16.905 17.375C17.457 17.375 17.929 17.553 18.32 17.909C18.711 18.265 18.907 18.695 18.907 19.198C18.907 19.687 18.711 20.109 18.32 20.466Z" fill="#808080"/>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

+10
View File
@@ -0,0 +1,10 @@
<svg width="80" height="40" viewBox="0 0 80 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M52.2984 12.8561L77.9046 11.0409C79.5948 10.9211 80.659 12.8259 79.6708 14.2023L61.7582 39.1527C61.3826 39.6759 60.7778 39.9863 60.1336 39.9863H19.9646C19.3214 39.9863 18.7174 39.6769 18.3414 39.1547L0.381197 14.2043C-0.609403 12.8281 0.454797 10.9207 2.1464 11.0409L27.6746 12.8563L38.578 0.666539C39.377 -0.226661 40.777 -0.221461 41.5694 0.677939L52.2984 12.8561ZM57.6608 36.0305L41.0426 29.8199V36.0305H57.6608ZM39.0426 36.0305V29.8199L22.4244 36.0305H39.0426ZM37.125 28.3363L20.146 34.6743L7.6548 17.3111L37.125 28.3363ZM42.9256 28.3363L59.9046 34.6743L72.3958 17.3111L42.9256 28.3363ZM39.0586 26.8871V5.98954L25.1758 21.6611L39.0586 26.8871ZM41.0426 26.8871V5.99214L54.9256 21.6611L41.0426 26.8871ZM21.1046 20.2165L24.2988 16.6273L8.6934 15.5089L21.1046 20.2165ZM58.8298 20.2165L55.6358 16.6273L71.2412 15.5089L58.8298 20.2165Z" fill="#006CFF"/>
</g>
<defs>
<clipPath id="clip0">
<rect width="80" height="40" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1,14 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M16 8C16.0003 9.7172 15.448 11.3889 14.4248 12.768C13.4017 14.1471 11.9619 15.1604 10.3184 15.658C8.67488 16.1556 6.91486 16.1112 5.29853 15.5314C3.6822 14.9515 2.29536 13.8669 1.34302 12.438L3.87002 10.753C4.35597 11.4645 5.01753 12.0383 5.79055 12.4189C6.56357 12.7994 7.42181 12.9738 8.28204 12.925C9.14227 12.8762 9.9753 12.6059 10.7003 12.1404C11.4254 11.6749 12.0178 11.0299 12.4202 10.268C12.8225 9.50614 13.0212 8.6532 12.9969 7.79193C12.9726 6.93066 12.7261 6.09029 12.2814 5.35231C11.8367 4.61433 11.2088 4.0038 10.4587 3.57992C9.70857 3.15604 8.86163 2.93319 8.00002 2.933V0C10.1217 0 12.1566 0.842855 13.6569 2.34315C15.1572 3.84344 16 5.87827 16 8Z" fill="url(#paint0_linear)"/>
</g>
<defs>
<linearGradient id="paint0_linear" x1="1.34302" y1="0" x2="1.34302" y2="16" gradientUnits="userSpaceOnUse">
<stop stop-color="#0169F6" stop-opacity="0.01"/>
<stop offset="1" stop-color="#006CFF"/>
</linearGradient>
<clipPath id="clip0">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

Some files were not shown because too many files have changed in this diff Show More