@@ -13,6 +13,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
analyze:
|
analyze:
|
||||||
|
if: github.base_ref == 'master'
|
||||||
timeout-minutes: 15
|
timeout-minutes: 15
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
## 1.3.0-beta
|
||||||
|
|
||||||
|
- Save pinned messages in offline storage
|
||||||
|
- Minor fixes
|
||||||
|
- `StreamClient.QueryChannels` now returns a Stream and fetches the channels from storage before calling the api
|
||||||
|
- Added `StreamClient.QueryChannelsOnline` and `StreamClient.QueryChannelsOffline` to fetch channels only from online or offline
|
||||||
|
|
||||||
## 1.2.0-beta
|
## 1.2.0-beta
|
||||||
|
|
||||||
- 🛑 **BREAKING** Changed signature of `StreamClient.search` method
|
- 🛑 **BREAKING** Changed signature of `StreamClient.search` method
|
||||||
|
|||||||
@@ -1,2 +1 @@
|
|||||||
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
|
|
||||||
#include "Generated.xcconfig"
|
#include "Generated.xcconfig"
|
||||||
|
|||||||
@@ -1,2 +1 @@
|
|||||||
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
|
|
||||||
#include "Generated.xcconfig"
|
#include "Generated.xcconfig"
|
||||||
|
|||||||
@@ -0,0 +1,495 @@
|
|||||||
|
// !$*UTF8*$!
|
||||||
|
{
|
||||||
|
archiveVersion = 1;
|
||||||
|
classes = {
|
||||||
|
};
|
||||||
|
objectVersion = 46;
|
||||||
|
objects = {
|
||||||
|
|
||||||
|
/* Begin PBXBuildFile section */
|
||||||
|
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
||||||
|
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||||
|
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
||||||
|
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||||
|
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||||
|
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||||
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
|
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = "";
|
||||||
|
dstSubfolderSpec = 10;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
name = "Embed Frameworks";
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXFileReference section */
|
||||||
|
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
||||||
|
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||||
|
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||||
|
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
|
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
|
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||||
|
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
||||||
|
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
||||||
|
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||||
|
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
|
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
|
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
97C146EB1CF9000F007C117D /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXGroup section */
|
||||||
|
9740EEB11CF90186004384FC /* Flutter */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
|
||||||
|
9740EEB21CF90195004384FC /* Debug.xcconfig */,
|
||||||
|
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
|
||||||
|
9740EEB31CF90195004384FC /* Generated.xcconfig */,
|
||||||
|
);
|
||||||
|
name = Flutter;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
97C146E51CF9000F007C117D = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
9740EEB11CF90186004384FC /* Flutter */,
|
||||||
|
97C146F01CF9000F007C117D /* Runner */,
|
||||||
|
97C146EF1CF9000F007C117D /* Products */,
|
||||||
|
);
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
97C146EF1CF9000F007C117D /* Products */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
97C146EE1CF9000F007C117D /* Runner.app */,
|
||||||
|
);
|
||||||
|
name = Products;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
97C146F01CF9000F007C117D /* Runner */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
97C146FA1CF9000F007C117D /* Main.storyboard */,
|
||||||
|
97C146FD1CF9000F007C117D /* Assets.xcassets */,
|
||||||
|
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
|
||||||
|
97C147021CF9000F007C117D /* Info.plist */,
|
||||||
|
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
|
||||||
|
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
|
||||||
|
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
|
||||||
|
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
|
||||||
|
);
|
||||||
|
path = Runner;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
/* End PBXGroup section */
|
||||||
|
|
||||||
|
/* Begin PBXNativeTarget section */
|
||||||
|
97C146ED1CF9000F007C117D /* Runner */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||||
|
buildPhases = (
|
||||||
|
9740EEB61CF901F6004384FC /* Run Script */,
|
||||||
|
97C146EA1CF9000F007C117D /* Sources */,
|
||||||
|
97C146EB1CF9000F007C117D /* Frameworks */,
|
||||||
|
97C146EC1CF9000F007C117D /* Resources */,
|
||||||
|
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||||
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = Runner;
|
||||||
|
productName = Runner;
|
||||||
|
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
|
||||||
|
productType = "com.apple.product-type.application";
|
||||||
|
};
|
||||||
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
|
/* Begin PBXProject section */
|
||||||
|
97C146E61CF9000F007C117D /* Project object */ = {
|
||||||
|
isa = PBXProject;
|
||||||
|
attributes = {
|
||||||
|
LastUpgradeCheck = 1020;
|
||||||
|
ORGANIZATIONNAME = "";
|
||||||
|
TargetAttributes = {
|
||||||
|
97C146ED1CF9000F007C117D = {
|
||||||
|
CreatedOnToolsVersion = 7.3.1;
|
||||||
|
LastSwiftMigration = 1100;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
|
||||||
|
compatibilityVersion = "Xcode 9.3";
|
||||||
|
developmentRegion = en;
|
||||||
|
hasScannedForEncodings = 0;
|
||||||
|
knownRegions = (
|
||||||
|
en,
|
||||||
|
Base,
|
||||||
|
);
|
||||||
|
mainGroup = 97C146E51CF9000F007C117D;
|
||||||
|
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
|
||||||
|
projectDirPath = "";
|
||||||
|
projectRoot = "";
|
||||||
|
targets = (
|
||||||
|
97C146ED1CF9000F007C117D /* Runner */,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
/* End PBXProject section */
|
||||||
|
|
||||||
|
/* Begin PBXResourcesBuildPhase section */
|
||||||
|
97C146EC1CF9000F007C117D /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
|
||||||
|
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
|
||||||
|
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
|
||||||
|
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
|
name = "Thin Binary";
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
|
||||||
|
};
|
||||||
|
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
|
name = "Run Script";
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
||||||
|
};
|
||||||
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
97C146EA1CF9000F007C117D /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
|
||||||
|
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXVariantGroup section */
|
||||||
|
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
|
||||||
|
isa = PBXVariantGroup;
|
||||||
|
children = (
|
||||||
|
97C146FB1CF9000F007C117D /* Base */,
|
||||||
|
);
|
||||||
|
name = Main.storyboard;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
|
||||||
|
isa = PBXVariantGroup;
|
||||||
|
children = (
|
||||||
|
97C147001CF9000F007C117D /* Base */,
|
||||||
|
);
|
||||||
|
name = LaunchScreen.storyboard;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
/* End PBXVariantGroup section */
|
||||||
|
|
||||||
|
/* Begin XCBuildConfiguration section */
|
||||||
|
249021D3217E4FDB00AE95B9 /* Profile */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SUPPORTED_PLATFORMS = iphoneos;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
VALIDATE_PRODUCT = YES;
|
||||||
|
};
|
||||||
|
name = Profile;
|
||||||
|
};
|
||||||
|
249021D4217E4FDB00AE95B9 /* Profile */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
|
ENABLE_BITCODE = NO;
|
||||||
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(PROJECT_DIR)/Flutter",
|
||||||
|
);
|
||||||
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||||
|
LIBRARY_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(PROJECT_DIR)/Flutter",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
|
};
|
||||||
|
name = Profile;
|
||||||
|
};
|
||||||
|
97C147031CF9000F007C117D /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_TESTABILITY = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
|
"DEBUG=1",
|
||||||
|
"$(inherited)",
|
||||||
|
);
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
97C147041CF9000F007C117D /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SUPPORTED_PLATFORMS = iphoneos;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
VALIDATE_PRODUCT = YES;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
97C147061CF9000F007C117D /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
|
ENABLE_BITCODE = NO;
|
||||||
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(PROJECT_DIR)/Flutter",
|
||||||
|
);
|
||||||
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||||
|
LIBRARY_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(PROJECT_DIR)/Flutter",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
97C147071CF9000F007C117D /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
|
ENABLE_BITCODE = NO;
|
||||||
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(PROJECT_DIR)/Flutter",
|
||||||
|
);
|
||||||
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||||
|
LIBRARY_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(PROJECT_DIR)/Flutter",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
|
/* Begin XCConfigurationList section */
|
||||||
|
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
97C147031CF9000F007C117D /* Debug */,
|
||||||
|
97C147041CF9000F007C117D /* Release */,
|
||||||
|
249021D3217E4FDB00AE95B9 /* Profile */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
97C147061CF9000F007C117D /* Debug */,
|
||||||
|
97C147071CF9000F007C117D /* Release */,
|
||||||
|
249021D4217E4FDB00AE95B9 /* Profile */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
/* End XCConfigurationList section */
|
||||||
|
};
|
||||||
|
rootObject = 97C146E61CF9000F007C117D /* Project object */;
|
||||||
|
}
|
||||||
@@ -4,7 +4,10 @@ import 'package:stream_chat/stream_chat.dart';
|
|||||||
Future<void> main() async {
|
Future<void> main() async {
|
||||||
/// Create a new instance of [StreamChatClient] passing the apikey obtained from your
|
/// Create a new instance of [StreamChatClient] passing the apikey obtained from your
|
||||||
/// project dashboard.
|
/// project dashboard.
|
||||||
final client = StreamChatClient('b67pax5b2wdq');
|
final client = StreamChatClient(
|
||||||
|
'b67pax5b2wdq',
|
||||||
|
logLevel: Level.INFO,
|
||||||
|
);
|
||||||
|
|
||||||
/// Set the current user. In a production scenario, this should be done using
|
/// Set the current user. In a production scenario, this should be done using
|
||||||
/// a backend to generate a user token using our server SDK.
|
/// a backend to generate a user token using our server SDK.
|
||||||
|
|||||||
@@ -40,8 +40,6 @@ class Channel {
|
|||||||
|
|
||||||
state = ChannelClientState(this, channelState);
|
state = ChannelClientState(this, channelState);
|
||||||
_initializedCompleter.complete(true);
|
_initializedCompleter.complete(true);
|
||||||
_startCleaning();
|
|
||||||
|
|
||||||
_client.logger.info('New Channel instance initialized created');
|
_client.logger.info('New Channel instance initialized created');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -844,7 +842,6 @@ class Channel {
|
|||||||
if (!_initializedCompleter.isCompleted) {
|
if (!_initializedCompleter.isCompleted) {
|
||||||
_initializedCompleter.complete(true);
|
_initializedCompleter.complete(true);
|
||||||
}
|
}
|
||||||
_startCleaning();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stop watching the channel
|
/// Stop watching the channel
|
||||||
@@ -1184,28 +1181,8 @@ class Channel {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer _cleaningTimer;
|
|
||||||
|
|
||||||
void _startCleaning() {
|
|
||||||
if (config?.typingEvents == false) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_cleaningTimer = Timer.periodic(Duration(milliseconds: 500), (_) {
|
|
||||||
final now = DateTime.now();
|
|
||||||
|
|
||||||
if (_lastTypingEvent != null &&
|
|
||||||
now.difference(_lastTypingEvent).inSeconds > 1) {
|
|
||||||
stopTyping();
|
|
||||||
}
|
|
||||||
|
|
||||||
state._clean();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Call this method to dispose the channel client
|
/// Call this method to dispose the channel client
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_cleaningTimer.cancel();
|
|
||||||
state.dispose();
|
state.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1256,6 +1233,10 @@ class ChannelClientState {
|
|||||||
|
|
||||||
_computeInitialUnread();
|
_computeInitialUnread();
|
||||||
|
|
||||||
|
_startCleaning();
|
||||||
|
|
||||||
|
_startCleaningPinnedMessages();
|
||||||
|
|
||||||
_channel._client.chatPersistenceClient
|
_channel._client.chatPersistenceClient
|
||||||
?.getChannelThreads(_channel.cid)
|
?.getChannelThreads(_channel.cid)
|
||||||
?.then((threads) {
|
?.then((threads) {
|
||||||
@@ -1264,7 +1245,9 @@ class ChannelClientState {
|
|||||||
_channel._client.chatPersistenceClient
|
_channel._client.chatPersistenceClient
|
||||||
?.getChannelStateByCid(_channel.cid)
|
?.getChannelStateByCid(_channel.cid)
|
||||||
?.then((state) {
|
?.then((state) {
|
||||||
updateChannelState(state);
|
// Replacing the persistence state members with the latest `channelState.members`
|
||||||
|
// as they may have changes over the time.
|
||||||
|
updateChannelState(state.copyWith(members: channelState.members));
|
||||||
retryFailedMessages();
|
retryFailedMessages();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1413,6 +1396,15 @@ class ChannelClientState {
|
|||||||
..removeWhere((it) => it.userId != userId),
|
..removeWhere((it) => it.userId != userId),
|
||||||
);
|
);
|
||||||
addMessage(message);
|
addMessage(message);
|
||||||
|
|
||||||
|
if (message.pinned == true) {
|
||||||
|
_channelState = _channelState.copyWith(
|
||||||
|
pinnedMessages: [
|
||||||
|
..._channelState.pinnedMessages ?? [],
|
||||||
|
message,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1502,6 +1494,13 @@ class ChannelClientState {
|
|||||||
Stream<List<Message>> get messagesStream =>
|
Stream<List<Message>> get messagesStream =>
|
||||||
channelStateStream.map((cs) => cs.messages);
|
channelStateStream.map((cs) => cs.messages);
|
||||||
|
|
||||||
|
/// Channel pinned message list
|
||||||
|
List<Message> get pinnedMessages => _channelState.pinnedMessages?.toList();
|
||||||
|
|
||||||
|
/// Channel pinned message list as a stream
|
||||||
|
Stream<List<Message>> get pinnedMessagesStream =>
|
||||||
|
channelStateStream.map((cs) => cs.pinnedMessages?.toList());
|
||||||
|
|
||||||
/// Get channel last message
|
/// Get channel last message
|
||||||
Message get lastMessage => _channelState.messages?.isNotEmpty == true
|
Message get lastMessage => _channelState.messages?.isNotEmpty == true
|
||||||
? _channelState.messages.last
|
? _channelState.messages.last
|
||||||
@@ -1658,6 +1657,7 @@ class ChannelClientState {
|
|||||||
watcherCount: updatedState.watcherCount,
|
watcherCount: updatedState.watcherCount,
|
||||||
members: newMembers,
|
members: newMembers,
|
||||||
read: newReads,
|
read: newReads,
|
||||||
|
pinnedMessages: updatedState.pinnedMessages,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1736,6 +1736,50 @@ class ChannelClientState {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Timer _cleaningTimer;
|
||||||
|
void _startCleaning() {
|
||||||
|
if (_channel.config?.typingEvents == false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_cleaningTimer = Timer.periodic(Duration(seconds: 1), (_) {
|
||||||
|
final now = DateTime.now();
|
||||||
|
|
||||||
|
if (_channel._lastTypingEvent != null &&
|
||||||
|
now.difference(_channel._lastTypingEvent).inSeconds > 1) {
|
||||||
|
_channel.stopTyping();
|
||||||
|
}
|
||||||
|
|
||||||
|
_clean();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer _pinnedMessagesTimer;
|
||||||
|
void _startCleaningPinnedMessages() {
|
||||||
|
_pinnedMessagesTimer = Timer.periodic(Duration(seconds: 30), (_) {
|
||||||
|
final now = DateTime.now();
|
||||||
|
var expiredMessages = channelState.pinnedMessages
|
||||||
|
?.where((m) => m.pinExpires?.isBefore(now) == true)
|
||||||
|
?.toList() ??
|
||||||
|
[];
|
||||||
|
if (expiredMessages.isNotEmpty) {
|
||||||
|
expiredMessages = expiredMessages
|
||||||
|
.map((m) => m.copyWith(
|
||||||
|
pinExpires: null,
|
||||||
|
pinned: false,
|
||||||
|
pinnedAt: null,
|
||||||
|
pinnedBy: null,
|
||||||
|
))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
updateChannelState(_channelState.copyWith(
|
||||||
|
pinnedMessages: pinnedMessages.where(_pinIsValid()).toList(),
|
||||||
|
messages: expiredMessages,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void _clean() {
|
void _clean() {
|
||||||
final now = DateTime.now();
|
final now = DateTime.now();
|
||||||
_typings.forEach((user, lastTypingEvent) {
|
_typings.forEach((user, lastTypingEvent) {
|
||||||
@@ -1759,6 +1803,13 @@ class ChannelClientState {
|
|||||||
_channelStateController.close();
|
_channelStateController.close();
|
||||||
_isUpToDateController.close();
|
_isUpToDateController.close();
|
||||||
_threadsController.close();
|
_threadsController.close();
|
||||||
|
_cleaningTimer.cancel();
|
||||||
|
_pinnedMessagesTimer.cancel();
|
||||||
_typingEventsController.close();
|
_typingEventsController.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Function(Message) _pinIsValid() {
|
||||||
|
final now = DateTime.now();
|
||||||
|
return (Message m) => m.pinExpires.isAfter(now);
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ part 'requests.g.dart';
|
|||||||
|
|
||||||
/// Sorting options
|
/// Sorting options
|
||||||
@JsonSerializable(createFactory: false)
|
@JsonSerializable(createFactory: false)
|
||||||
class SortOption {
|
class SortOption<T> {
|
||||||
/// Ascending order
|
/// Ascending order
|
||||||
static const ASC = 1;
|
static const ASC = 1;
|
||||||
|
|
||||||
@@ -17,6 +17,10 @@ class SortOption {
|
|||||||
/// A sorting direction
|
/// A sorting direction
|
||||||
final int direction;
|
final int direction;
|
||||||
|
|
||||||
|
/// Sorting field Comparator required for offline sorting
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
final Comparator<T> comparator;
|
||||||
|
|
||||||
/// Creates a new SortOption instance
|
/// Creates a new SortOption instance
|
||||||
///
|
///
|
||||||
/// For example:
|
/// For example:
|
||||||
@@ -24,7 +28,11 @@ class SortOption {
|
|||||||
/// // Sort channels by the last message date:
|
/// // Sort channels by the last message date:
|
||||||
/// final sorting = SortOption("last_message_at")
|
/// final sorting = SortOption("last_message_at")
|
||||||
/// ```
|
/// ```
|
||||||
const SortOption(this.field, {this.direction = DESC});
|
const SortOption(
|
||||||
|
this.field, {
|
||||||
|
this.direction = DESC,
|
||||||
|
this.comparator,
|
||||||
|
});
|
||||||
|
|
||||||
/// Serialize model to json
|
/// Serialize model to json
|
||||||
Map<String, dynamic> toJson() => _$SortOptionToJson(this);
|
Map<String, dynamic> toJson() => _$SortOptionToJson(this);
|
||||||
@@ -67,7 +75,7 @@ class PaginationParams {
|
|||||||
/// ```
|
/// ```
|
||||||
const PaginationParams({
|
const PaginationParams({
|
||||||
this.limit = 10,
|
this.limit = 10,
|
||||||
this.offset,
|
this.offset = 0,
|
||||||
this.greaterThan,
|
this.greaterThan,
|
||||||
this.greaterThanOrEqual,
|
this.greaterThanOrEqual,
|
||||||
this.lessThan,
|
this.lessThan,
|
||||||
|
|||||||
@@ -9,7 +9,9 @@ import 'package:rxdart/rxdart.dart';
|
|||||||
import 'package:stream_chat/src/api/retry_policy.dart';
|
import 'package:stream_chat/src/api/retry_policy.dart';
|
||||||
import 'package:stream_chat/src/event_type.dart';
|
import 'package:stream_chat/src/event_type.dart';
|
||||||
import 'package:stream_chat/src/models/attachment_file.dart';
|
import 'package:stream_chat/src/models/attachment_file.dart';
|
||||||
|
import 'package:stream_chat/src/models/channel_model.dart';
|
||||||
import 'package:stream_chat/src/models/own_user.dart';
|
import 'package:stream_chat/src/models/own_user.dart';
|
||||||
|
import 'package:stream_chat/src/platform_detector/platform_detector.dart';
|
||||||
import 'package:stream_chat/version.dart';
|
import 'package:stream_chat/version.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
@@ -21,6 +23,7 @@ import 'api/responses.dart';
|
|||||||
import 'api/websocket.dart';
|
import 'api/websocket.dart';
|
||||||
import 'db/chat_persistence_client.dart';
|
import 'db/chat_persistence_client.dart';
|
||||||
import 'exceptions.dart';
|
import 'exceptions.dart';
|
||||||
|
import 'models/channel_state.dart';
|
||||||
import 'models/event.dart';
|
import 'models/event.dart';
|
||||||
import 'models/message.dart';
|
import 'models/message.dart';
|
||||||
import 'models/user.dart';
|
import 'models/user.dart';
|
||||||
@@ -488,7 +491,7 @@ class StreamChatClient {
|
|||||||
|
|
||||||
if (status == ConnectionStatus.connected &&
|
if (status == ConnectionStatus.connected &&
|
||||||
state.channels?.isNotEmpty == true) {
|
state.channels?.isNotEmpty == true) {
|
||||||
unawaited(queryChannels(filter: {
|
unawaited(queryChannelsOnline(filter: {
|
||||||
'cid': {
|
'cid': {
|
||||||
'\$in': state.channels.keys.toList(),
|
'\$in': state.channels.keys.toList(),
|
||||||
},
|
},
|
||||||
@@ -577,13 +580,52 @@ class StreamChatClient {
|
|||||||
final _queryChannelsStreams = <String, Future<List<Channel>>>{};
|
final _queryChannelsStreams = <String, Future<List<Channel>>>{};
|
||||||
|
|
||||||
/// Requests channels with a given query.
|
/// Requests channels with a given query.
|
||||||
Future<List<Channel>> queryChannels({
|
Stream<List<Channel>> queryChannels({
|
||||||
Map<String, dynamic> filter,
|
Map<String, dynamic> filter,
|
||||||
List<SortOption> sort,
|
List<SortOption<ChannelModel>> sort,
|
||||||
Map<String, dynamic> options,
|
Map<String, dynamic> options,
|
||||||
PaginationParams paginationParams = const PaginationParams(limit: 10),
|
PaginationParams paginationParams = const PaginationParams(limit: 10),
|
||||||
int messageLimit,
|
int messageLimit,
|
||||||
bool onlyOffline = false,
|
bool preferOffline = false,
|
||||||
|
bool waitForConnect = true,
|
||||||
|
}) async* {
|
||||||
|
final hash = base64.encode(utf8.encode(
|
||||||
|
'$filter${_asMap(sort)}$options${paginationParams?.toJson()}$messageLimit$preferOffline',
|
||||||
|
));
|
||||||
|
|
||||||
|
if (_queryChannelsStreams.containsKey(hash)) {
|
||||||
|
yield await _queryChannelsStreams[hash];
|
||||||
|
} else {
|
||||||
|
if (preferOffline) {
|
||||||
|
final channels = await queryChannelsOffline(
|
||||||
|
filter: filter,
|
||||||
|
sort: sort,
|
||||||
|
paginationParams: paginationParams,
|
||||||
|
);
|
||||||
|
if (channels.isNotEmpty) yield channels;
|
||||||
|
}
|
||||||
|
|
||||||
|
final newQueryChannelsFuture = queryChannelsOnline(
|
||||||
|
filter: filter,
|
||||||
|
sort: sort,
|
||||||
|
options: options,
|
||||||
|
paginationParams: paginationParams,
|
||||||
|
messageLimit: messageLimit,
|
||||||
|
);
|
||||||
|
|
||||||
|
_queryChannelsStreams[hash] = newQueryChannelsFuture;
|
||||||
|
|
||||||
|
yield await newQueryChannelsFuture;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Requests channels with a given query from the API.
|
||||||
|
Future<List<Channel>> queryChannelsOnline({
|
||||||
|
@required Map<String, dynamic> filter,
|
||||||
|
List<SortOption<ChannelModel>> sort,
|
||||||
|
Map<String, dynamic> options,
|
||||||
|
int messageLimit,
|
||||||
|
PaginationParams paginationParams = const PaginationParams(limit: 10),
|
||||||
bool waitForConnect = true,
|
bool waitForConnect = true,
|
||||||
}) async {
|
}) async {
|
||||||
if (waitForConnect) {
|
if (waitForConnect) {
|
||||||
@@ -592,48 +634,13 @@ class StreamChatClient {
|
|||||||
await _connectCompleter.future;
|
await _connectCompleter.future;
|
||||||
}
|
}
|
||||||
if (wsConnectionStatus != ConnectionStatus.connected) {
|
if (wsConnectionStatus != ConnectionStatus.connected) {
|
||||||
final errorMessage =
|
throw Exception(
|
||||||
'You cannot use queryChannels without an active connection. Please call `connectUser` to connect the client.';
|
'You cannot use queryChannels without an active connection.'
|
||||||
if (persistenceEnabled) {
|
' Please call `connectUser` to connect the client.',
|
||||||
logger.warning(
|
);
|
||||||
'$errorMessage\nTrying to retrieve channels from the offline storage.');
|
|
||||||
onlyOffline = true;
|
|
||||||
} else {
|
|
||||||
throw Exception(errorMessage);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final hash = base64.encode(utf8.encode(
|
|
||||||
'$filter${_asMap(sort)}$options${paginationParams?.toJson()}$messageLimit$onlyOffline'));
|
|
||||||
if (_queryChannelsStreams.containsKey(hash)) {
|
|
||||||
return _queryChannelsStreams[hash];
|
|
||||||
}
|
|
||||||
|
|
||||||
final newQueryChannelsStream = _doQueryChannels(
|
|
||||||
filter: filter,
|
|
||||||
sort: sort,
|
|
||||||
options: options,
|
|
||||||
paginationParams: paginationParams,
|
|
||||||
messageLimit: messageLimit,
|
|
||||||
onlyOffline: onlyOffline,
|
|
||||||
).whenComplete(() {
|
|
||||||
_queryChannelsStreams.remove(hash);
|
|
||||||
});
|
|
||||||
|
|
||||||
_queryChannelsStreams[hash] = newQueryChannelsStream;
|
|
||||||
|
|
||||||
return newQueryChannelsStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<Channel>> _doQueryChannels({
|
|
||||||
@required Map<String, dynamic> filter,
|
|
||||||
@required List<SortOption> sort,
|
|
||||||
@required Map<String, dynamic> options,
|
|
||||||
@required int messageLimit,
|
|
||||||
PaginationParams paginationParams = const PaginationParams(limit: 10),
|
|
||||||
bool onlyOffline = false,
|
|
||||||
}) async {
|
|
||||||
logger.info('Query channel start');
|
logger.info('Query channel start');
|
||||||
final defaultOptions = {
|
final defaultOptions = {
|
||||||
'state': true,
|
'state': true,
|
||||||
@@ -660,86 +667,86 @@ class StreamChatClient {
|
|||||||
payload.addAll(paginationParams.toJson());
|
payload.addAll(paginationParams.toJson());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (onlyOffline) {
|
final response = await get(
|
||||||
return _queryChannelsOffline(
|
'/channels',
|
||||||
filter: filter,
|
queryParameters: {
|
||||||
sort: sort,
|
'payload': jsonEncode(payload),
|
||||||
paginationParams: paginationParams,
|
},
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
final res = decode<QueryChannelsResponse>(
|
||||||
final response = await get(
|
response.data,
|
||||||
'/channels',
|
QueryChannelsResponse.fromJson,
|
||||||
queryParameters: {
|
);
|
||||||
'payload': jsonEncode(payload),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
final res = decode<QueryChannelsResponse>(
|
if ((res.channels ?? []).isEmpty && (paginationParams?.offset ?? 0) == 0) {
|
||||||
response.data,
|
logger.warning('''We could not find any channel for this query.
|
||||||
QueryChannelsResponse.fromJson,
|
|
||||||
);
|
|
||||||
|
|
||||||
final users = res.channels
|
|
||||||
?.expand((channel) => channel.members.map((member) => member.user))
|
|
||||||
?.toList();
|
|
||||||
|
|
||||||
if (users != null) {
|
|
||||||
state._updateUsers(users);
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info('Got ${res.channels?.length} channels from api');
|
|
||||||
|
|
||||||
if (res.channels?.isEmpty != false &&
|
|
||||||
(paginationParams?.offset ?? 0) == 0) {
|
|
||||||
logger.warning('''We could not find any channel for this query.
|
|
||||||
Please make sure to take a look at the Flutter tutorial: https://getstream.io/chat/flutter/tutorial
|
Please make sure to take a look at the Flutter tutorial: https://getstream.io/chat/flutter/tutorial
|
||||||
If your application already has users and channels, you might need to adjust your query channel as explained in the docs https://getstream.io/chat/docs/query_channels/?language=dart''');
|
If your application already has users and channels, you might need to adjust your query channel as explained in the docs https://getstream.io/chat/docs/query_channels/?language=dart''');
|
||||||
}
|
return <Channel>[];
|
||||||
|
|
||||||
final newChannels = Map<String, Channel>.from(state.channels ?? {});
|
|
||||||
final channels = <Channel>[];
|
|
||||||
|
|
||||||
if (res.channels != null) {
|
|
||||||
for (final channelState in res.channels) {
|
|
||||||
final channel = newChannels[channelState.channel.cid];
|
|
||||||
if (channel != null) {
|
|
||||||
channel.state?.updateChannelState(channelState);
|
|
||||||
channels.add(channel);
|
|
||||||
} else {
|
|
||||||
final newChannel = Channel.fromState(this, channelState);
|
|
||||||
await chatPersistenceClient
|
|
||||||
?.updateChannelState(newChannel.state.channelState);
|
|
||||||
newChannel.state?.updateChannelState(channelState);
|
|
||||||
newChannels[newChannel.cid] = newChannel;
|
|
||||||
channels.add(newChannel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
state.channels = newChannels;
|
|
||||||
|
|
||||||
await chatPersistenceClient?.updateChannelQueries(
|
|
||||||
filter,
|
|
||||||
res.channels.map((c) => c.channel.cid).toList(),
|
|
||||||
paginationParams?.offset == null || paginationParams.offset == 0,
|
|
||||||
);
|
|
||||||
|
|
||||||
return channels;
|
|
||||||
} catch (e) {
|
|
||||||
if (!persistenceEnabled) {
|
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
return _queryChannelsOffline(
|
|
||||||
filter: filter,
|
|
||||||
sort: sort,
|
|
||||||
paginationParams: paginationParams,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final channels = res.channels;
|
||||||
|
|
||||||
|
final users = channels
|
||||||
|
.expand((it) => it.members)
|
||||||
|
.map((it) => it.user)
|
||||||
|
.toList(growable: false);
|
||||||
|
|
||||||
|
state._updateUsers(users);
|
||||||
|
|
||||||
|
logger.info('Got ${res.channels?.length} channels from api');
|
||||||
|
|
||||||
|
final updateData = _mapChannelStateToChannel(channels);
|
||||||
|
|
||||||
|
await chatPersistenceClient?.updateChannelQueries(
|
||||||
|
filter,
|
||||||
|
channels.map((c) => c.channel.cid).toList(),
|
||||||
|
paginationParams?.offset == null || paginationParams.offset == 0,
|
||||||
|
);
|
||||||
|
|
||||||
|
state.channels = updateData.key;
|
||||||
|
return updateData.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
dynamic _parseError(DioError error) {
|
/// Requests channels with a given query from the Persistence client.
|
||||||
|
Future<List<Channel>> queryChannelsOffline({
|
||||||
|
@required Map<String, dynamic> filter,
|
||||||
|
@required List<SortOption<ChannelModel>> sort,
|
||||||
|
PaginationParams paginationParams = const PaginationParams(limit: 10),
|
||||||
|
}) async {
|
||||||
|
final offlineChannels = await chatPersistenceClient?.getChannelStates(
|
||||||
|
filter: filter,
|
||||||
|
sort: sort,
|
||||||
|
paginationParams: paginationParams,
|
||||||
|
);
|
||||||
|
final updatedData = _mapChannelStateToChannel(offlineChannels);
|
||||||
|
state.channels = updatedData.key;
|
||||||
|
return updatedData.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
MapEntry<Map<String, Channel>, List<Channel>> _mapChannelStateToChannel(
|
||||||
|
List<ChannelState> channelStates,
|
||||||
|
) {
|
||||||
|
final channels = {...state.channels ?? {}};
|
||||||
|
final newChannels = <Channel>[];
|
||||||
|
if (channelStates != null) {
|
||||||
|
for (final channelState in channelStates) {
|
||||||
|
final channel = channels[channelState.channel.cid];
|
||||||
|
if (channel != null) {
|
||||||
|
channel.state?.updateChannelState(channelState);
|
||||||
|
newChannels.add(channel);
|
||||||
|
} else {
|
||||||
|
final newChannel = Channel.fromState(this, channelState);
|
||||||
|
channels[newChannel.cid] = newChannel;
|
||||||
|
newChannels.add(newChannel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return MapEntry(channels, newChannels);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object _parseError(DioError error) {
|
||||||
if (error.type == DioErrorType.RESPONSE) {
|
if (error.type == DioErrorType.RESPONSE) {
|
||||||
final apiError =
|
final apiError =
|
||||||
ApiError(error.response?.data, error.response?.statusCode);
|
ApiError(error.response?.data, error.response?.statusCode);
|
||||||
@@ -750,39 +757,6 @@ class StreamChatClient {
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<Channel>> _queryChannelsOffline({
|
|
||||||
@required Map<String, dynamic> filter,
|
|
||||||
@required List<SortOption> sort,
|
|
||||||
PaginationParams paginationParams = const PaginationParams(limit: 10),
|
|
||||||
}) async {
|
|
||||||
final offlineChannels = await chatPersistenceClient?.getChannelStates(
|
|
||||||
filter: filter,
|
|
||||||
sort: sort,
|
|
||||||
paginationParams: paginationParams,
|
|
||||||
) ??
|
|
||||||
[];
|
|
||||||
final newChannels = Map<String, Channel>.from(state.channels ?? {});
|
|
||||||
logger.info('Got ${offlineChannels.length} channels from storage');
|
|
||||||
final channels = offlineChannels.map((channelState) {
|
|
||||||
final channel = newChannels[channelState.channel.cid];
|
|
||||||
if (channel != null) {
|
|
||||||
channel.state?.updateChannelState(channelState);
|
|
||||||
return channel;
|
|
||||||
} else {
|
|
||||||
final newChannel = Channel.fromState(this, channelState);
|
|
||||||
chatPersistenceClient
|
|
||||||
?.updateChannelState(newChannel.state.channelState);
|
|
||||||
newChannels[newChannel.cid] = newChannel;
|
|
||||||
return newChannel;
|
|
||||||
}
|
|
||||||
}).toList();
|
|
||||||
|
|
||||||
if (channels.isNotEmpty) {
|
|
||||||
state.channels = newChannels;
|
|
||||||
}
|
|
||||||
return channels;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Handy method to make http GET request with error parsing.
|
/// Handy method to make http GET request with error parsing.
|
||||||
Future<Response<String>> get(
|
Future<Response<String>> get(
|
||||||
String path, {
|
String path, {
|
||||||
@@ -888,9 +862,8 @@ class StreamChatClient {
|
|||||||
|
|
||||||
String get _authType => _anonymous ? 'anonymous' : 'jwt';
|
String get _authType => _anonymous ? 'anonymous' : 'jwt';
|
||||||
|
|
||||||
// TODO: get the right version of the lib from the build toolchain
|
|
||||||
String get _userAgent =>
|
String get _userAgent =>
|
||||||
'stream-chat-dart-client-${PACKAGE_VERSION.split('+')[0]}';
|
'stream-chat-dart-client-${CurrentPlatform.name}-${PACKAGE_VERSION.split('+')[0]}';
|
||||||
|
|
||||||
Map<String, String> get _commonQueryParams => {
|
Map<String, String> get _commonQueryParams => {
|
||||||
'user_id': state.user?.id,
|
'user_id': state.user?.id,
|
||||||
@@ -1448,18 +1421,16 @@ class ClientState {
|
|||||||
_userController.add(user);
|
_userController.add(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updateUsers(List<User> users) {
|
void _updateUsers(List<User> userList) {
|
||||||
users?.forEach(_updateUser);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _updateUser(User user) {
|
|
||||||
final newUsers = {
|
final newUsers = {
|
||||||
...users ?? {},
|
...users ?? {},
|
||||||
user.id: user,
|
for (var user in userList) user.id: user,
|
||||||
};
|
};
|
||||||
_usersController.add(newUsers);
|
_usersController.add(newUsers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _updateUser(User user) => _updateUsers([user]);
|
||||||
|
|
||||||
/// The current user
|
/// The current user
|
||||||
OwnUser get user => _userController.value;
|
OwnUser get user => _userController.value;
|
||||||
|
|
||||||
|
|||||||
@@ -56,23 +56,31 @@ abstract class ChatPersistenceClient {
|
|||||||
PaginationParams messagePagination,
|
PaginationParams messagePagination,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// Get stored pinned [Message]s by providing channel [cid]
|
||||||
|
Future<List<Message>> getPinnedMessagesByCid(
|
||||||
|
String cid, {
|
||||||
|
PaginationParams messagePagination,
|
||||||
|
});
|
||||||
|
|
||||||
/// Get [ChannelState] data by providing channel [cid]
|
/// Get [ChannelState] data by providing channel [cid]
|
||||||
Future<ChannelState> getChannelStateByCid(
|
Future<ChannelState> getChannelStateByCid(
|
||||||
String cid, {
|
String cid, {
|
||||||
PaginationParams messagePagination,
|
PaginationParams messagePagination,
|
||||||
|
PaginationParams pinnedMessagePagination,
|
||||||
}) async {
|
}) async {
|
||||||
final members = await getMembersByCid(cid);
|
final data = await Future.wait([
|
||||||
final reads = await getReadsByCid(cid);
|
getMembersByCid(cid),
|
||||||
final channel = await getChannelByCid(cid);
|
getReadsByCid(cid),
|
||||||
final messages = await getMessagesByCid(
|
getChannelByCid(cid),
|
||||||
cid,
|
getMessagesByCid(cid, messagePagination: messagePagination),
|
||||||
messagePagination: messagePagination,
|
getPinnedMessagesByCid(cid, messagePagination: pinnedMessagePagination),
|
||||||
);
|
]);
|
||||||
return ChannelState(
|
return ChannelState(
|
||||||
members: members,
|
members: data[0],
|
||||||
read: reads,
|
read: data[1],
|
||||||
messages: messages,
|
channel: data[2],
|
||||||
channel: channel,
|
messages: data[3],
|
||||||
|
pinnedMessages: data[4],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,7 +90,7 @@ abstract class ChatPersistenceClient {
|
|||||||
/// for filtering out states.
|
/// for filtering out states.
|
||||||
Future<List<ChannelState>> getChannelStates({
|
Future<List<ChannelState>> getChannelStates({
|
||||||
Map<String, dynamic> filter,
|
Map<String, dynamic> filter,
|
||||||
List<SortOption> sort = const [],
|
List<SortOption<ChannelModel>> sort = const [],
|
||||||
PaginationParams paginationParams,
|
PaginationParams paginationParams,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -101,17 +109,33 @@ abstract class ChatPersistenceClient {
|
|||||||
return deleteMessageByIds([messageId]);
|
return deleteMessageByIds([messageId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Remove a pinned message by [messageId]
|
||||||
|
Future<void> deletePinnedMessageById(String messageId) {
|
||||||
|
return deletePinnedMessageByIds([messageId]);
|
||||||
|
}
|
||||||
|
|
||||||
/// Remove a message by [messageIds]
|
/// Remove a message by [messageIds]
|
||||||
Future<void> deleteMessageByIds(List<String> messageIds);
|
Future<void> deleteMessageByIds(List<String> messageIds);
|
||||||
|
|
||||||
|
/// Remove a pinned message by [messageIds]
|
||||||
|
Future<void> deletePinnedMessageByIds(List<String> messageIds);
|
||||||
|
|
||||||
/// Remove a message by channel [cid]
|
/// Remove a message by channel [cid]
|
||||||
Future<void> deleteMessageByCid(String cid) {
|
Future<void> deleteMessageByCid(String cid) {
|
||||||
return deleteMessageByCids([cid]);
|
return deleteMessageByCids([cid]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Remove a pinned message by channel [cid]
|
||||||
|
Future<void> deletePinnedMessageByCid(String cid) {
|
||||||
|
return deletePinnedMessageByCids([cid]);
|
||||||
|
}
|
||||||
|
|
||||||
/// Remove a message by message [cids]
|
/// Remove a message by message [cids]
|
||||||
Future<void> deleteMessageByCids(List<String> cids);
|
Future<void> deleteMessageByCids(List<String> cids);
|
||||||
|
|
||||||
|
/// Remove a pinned message by message [cids]
|
||||||
|
Future<void> deletePinnedMessageByCids(List<String> cids);
|
||||||
|
|
||||||
/// Remove a channel by [cid]
|
/// Remove a channel by [cid]
|
||||||
Future<void> deleteChannels(List<String> cids);
|
Future<void> deleteChannels(List<String> cids);
|
||||||
|
|
||||||
@@ -119,6 +143,10 @@ abstract class ChatPersistenceClient {
|
|||||||
/// the new [messages] data
|
/// the new [messages] data
|
||||||
Future<void> updateMessages(String cid, List<Message> messages);
|
Future<void> updateMessages(String cid, List<Message> messages);
|
||||||
|
|
||||||
|
/// Updates the pinned message data of a particular channel [cid] with
|
||||||
|
/// the new [messages] data
|
||||||
|
Future<void> updatePinnedMessages(String cid, List<Message> messages);
|
||||||
|
|
||||||
/// Returns all the threads by parent message of a particular channel by
|
/// Returns all the threads by parent message of a particular channel by
|
||||||
/// providing channel [cid]
|
/// providing channel [cid]
|
||||||
Future<Map<String, List<Message>>> getChannelThreads(String cid);
|
Future<Map<String, List<Message>>> getChannelThreads(String cid);
|
||||||
@@ -204,6 +232,12 @@ abstract class ChatPersistenceClient {
|
|||||||
return updateMessages(cid, messages.toList(growable: false));
|
return updateMessages(cid, messages.toList(growable: false));
|
||||||
}).toList(growable: false);
|
}).toList(growable: false);
|
||||||
|
|
||||||
|
final updatePinnedMessagesFuture = channelStates.map((it) {
|
||||||
|
final cid = it.channel.cid;
|
||||||
|
final messages = it.pinnedMessages.where((it) => it != null);
|
||||||
|
return updatePinnedMessages(cid, messages.toList(growable: false));
|
||||||
|
}).toList(growable: false);
|
||||||
|
|
||||||
final updateReadsFuture = channelStates.map((it) {
|
final updateReadsFuture = channelStates.map((it) {
|
||||||
final cid = it.channel.cid;
|
final cid = it.channel.cid;
|
||||||
final reads = it.read?.where((it) => it != null) ?? [];
|
final reads = it.read?.where((it) => it != null) ?? [];
|
||||||
@@ -218,6 +252,7 @@ abstract class ChatPersistenceClient {
|
|||||||
|
|
||||||
await Future.wait([
|
await Future.wait([
|
||||||
...updateMessagesFuture,
|
...updateMessagesFuture,
|
||||||
|
...updatePinnedMessagesFuture,
|
||||||
...updateReadsFuture,
|
...updateReadsFuture,
|
||||||
...updateMembersFuture,
|
...updateMembersFuture,
|
||||||
updateUsers(users.toList(growable: false)),
|
updateUsers(users.toList(growable: false)),
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ class Attachment {
|
|||||||
final AttachmentFile file;
|
final AttachmentFile file;
|
||||||
|
|
||||||
/// The current upload state of the attachment
|
/// The current upload state of the attachment
|
||||||
final UploadState uploadState;
|
UploadState uploadState;
|
||||||
|
|
||||||
/// Map of custom channel extraData
|
/// Map of custom channel extraData
|
||||||
@JsonKey(includeIfNull: false)
|
@JsonKey(includeIfNull: false)
|
||||||
@@ -123,10 +123,15 @@ class Attachment {
|
|||||||
this.actions,
|
this.actions,
|
||||||
this.extraData,
|
this.extraData,
|
||||||
this.file,
|
this.file,
|
||||||
this.uploadState,
|
UploadState uploadState,
|
||||||
}) : id = id ?? Uuid().v4(),
|
}) : id = id ?? Uuid().v4(),
|
||||||
title = title ?? file?.name,
|
title = title ?? file?.name,
|
||||||
localUri = file?.path != null ? Uri.parse(file.path) : null;
|
localUri = file?.path != null ? Uri.parse(file.path) : null {
|
||||||
|
this.uploadState = uploadState ??
|
||||||
|
((assetUrl != null || imageUrl != null)
|
||||||
|
? UploadState.success()
|
||||||
|
: UploadState.preparing());
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new instance from a json
|
/// Create a new instance from a json
|
||||||
factory Attachment.fromJson(Map<String, dynamic> json) {
|
factory Attachment.fromJson(Map<String, dynamic> json) {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import 'message.dart';
|
|||||||
|
|
||||||
part 'channel_state.g.dart';
|
part 'channel_state.g.dart';
|
||||||
|
|
||||||
/// The class that contains the information about a command
|
/// The class that contains the information about a channel
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class ChannelState {
|
class ChannelState {
|
||||||
/// The channel to which this state belongs
|
/// The channel to which this state belongs
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
import 'platform_detector_stub.dart'
|
||||||
|
if (dart.library.html) 'platform_detector_web.dart'
|
||||||
|
if (dart.library.io) 'platform_detector_io.dart';
|
||||||
|
|
||||||
|
/// Possible platforms
|
||||||
|
enum PlatformType {
|
||||||
|
///
|
||||||
|
Android,
|
||||||
|
|
||||||
|
///
|
||||||
|
Ios,
|
||||||
|
|
||||||
|
///
|
||||||
|
Web,
|
||||||
|
|
||||||
|
///
|
||||||
|
MacOS,
|
||||||
|
|
||||||
|
///
|
||||||
|
Windows,
|
||||||
|
|
||||||
|
///
|
||||||
|
Linux,
|
||||||
|
|
||||||
|
///
|
||||||
|
Fuchsia,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Utility class that provides information on the current platform
|
||||||
|
class CurrentPlatform {
|
||||||
|
CurrentPlatform._();
|
||||||
|
|
||||||
|
/// True if the app is running on android
|
||||||
|
static bool get isAndroid => type == PlatformType.Android;
|
||||||
|
|
||||||
|
/// True if the app is running on ios
|
||||||
|
static bool get isIos => type == PlatformType.Ios;
|
||||||
|
|
||||||
|
/// True if the app is running on web
|
||||||
|
static bool get isWeb => type == PlatformType.Web;
|
||||||
|
|
||||||
|
/// True if the app is running on macos
|
||||||
|
static bool get isMacOS => type == PlatformType.MacOS;
|
||||||
|
|
||||||
|
/// True if the app is running on windows
|
||||||
|
static bool get isWindows => type == PlatformType.Windows;
|
||||||
|
|
||||||
|
/// True if the app is running on linux
|
||||||
|
static bool get isLinux => type == PlatformType.Linux;
|
||||||
|
|
||||||
|
/// True if the app is running on fuchsia
|
||||||
|
static bool get isFuchsia => type == PlatformType.Fuchsia;
|
||||||
|
|
||||||
|
/// Returns a string version of the platform
|
||||||
|
static String get name {
|
||||||
|
switch (type) {
|
||||||
|
case PlatformType.Android:
|
||||||
|
return 'android';
|
||||||
|
case PlatformType.Ios:
|
||||||
|
return 'ios';
|
||||||
|
case PlatformType.Web:
|
||||||
|
return 'web';
|
||||||
|
case PlatformType.MacOS:
|
||||||
|
return 'macos';
|
||||||
|
case PlatformType.Windows:
|
||||||
|
return 'windows';
|
||||||
|
case PlatformType.Linux:
|
||||||
|
return 'linux';
|
||||||
|
case PlatformType.Fuchsia:
|
||||||
|
return 'fuchsia';
|
||||||
|
default:
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get current platform type
|
||||||
|
static PlatformType get type => currentPlatform;
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
import 'platform_detector.dart';
|
||||||
|
|
||||||
|
/// Version running on native systems
|
||||||
|
PlatformType get currentPlatform {
|
||||||
|
if (Platform.isWindows) return PlatformType.Windows;
|
||||||
|
if (Platform.isFuchsia) return PlatformType.Fuchsia;
|
||||||
|
if (Platform.isMacOS) return PlatformType.MacOS;
|
||||||
|
if (Platform.isLinux) return PlatformType.Linux;
|
||||||
|
if (Platform.isIOS) return PlatformType.Ios;
|
||||||
|
return PlatformType.Android;
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import 'platform_detector.dart';
|
||||||
|
|
||||||
|
/// Stub implementation
|
||||||
|
PlatformType get currentPlatform {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import 'platform_detector.dart';
|
||||||
|
|
||||||
|
/// Version running on web
|
||||||
|
PlatformType get currentPlatform => PlatformType.Web;
|
||||||
@@ -2,4 +2,4 @@ import 'package:stream_chat/src/client.dart';
|
|||||||
|
|
||||||
/// Current package version
|
/// Current package version
|
||||||
/// Used in [StreamChatClient] to build the `x-stream-client` header
|
/// Used in [StreamChatClient] to build the `x-stream-client` header
|
||||||
const PACKAGE_VERSION = '1.2.0-beta';
|
const PACKAGE_VERSION = '1.3.0-beta';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
name: stream_chat
|
name: stream_chat
|
||||||
homepage: https://getstream.io/
|
homepage: https://getstream.io/
|
||||||
description: The official Dart client for Stream Chat, a service for building chat applications.
|
description: The official Dart client for Stream Chat, a service for building chat applications.
|
||||||
version: 1.2.0-beta
|
version: 1.3.0-beta
|
||||||
repository: https://github.com/GetStream/stream-chat-flutter
|
repository: https://github.com/GetStream/stream-chat-flutter
|
||||||
issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues
|
issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ void main() {
|
|||||||
test('PaginationParams', () {
|
test('PaginationParams', () {
|
||||||
final option = PaginationParams();
|
final option = PaginationParams();
|
||||||
final j = option.toJson();
|
final j = option.toJson();
|
||||||
expect(j, {'limit': 10});
|
expect(j, {'limit': 10, 'offset': 0});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import 'package:stream_chat/src/client.dart';
|
|||||||
import 'package:stream_chat/src/exceptions.dart';
|
import 'package:stream_chat/src/exceptions.dart';
|
||||||
import 'package:stream_chat/src/models/message.dart';
|
import 'package:stream_chat/src/models/message.dart';
|
||||||
import 'package:stream_chat/src/models/user.dart';
|
import 'package:stream_chat/src/models/user.dart';
|
||||||
|
import 'package:stream_chat/src/models/channel_model.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
class MockDio extends Mock implements DioForNative {}
|
class MockDio extends Mock implements DioForNative {}
|
||||||
@@ -86,7 +87,7 @@ void main() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
group('queryChannels', () {
|
group('queryChannelsOnline', () {
|
||||||
test('should pass right default parameters', () async {
|
test('should pass right default parameters', () async {
|
||||||
final mockDio = MockDio();
|
final mockDio = MockDio();
|
||||||
|
|
||||||
@@ -106,13 +107,14 @@ void main() {
|
|||||||
"watch": true,
|
"watch": true,
|
||||||
"presence": false,
|
"presence": false,
|
||||||
"limit": 10,
|
"limit": 10,
|
||||||
|
"offset": 0,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
when(mockDio.get<String>('/channels', queryParameters: queryParams))
|
when(mockDio.get<String>('/channels', queryParameters: queryParams))
|
||||||
.thenAnswer((_) async => Response(data: '{}', statusCode: 200));
|
.thenAnswer((_) async => Response(data: '{}', statusCode: 200));
|
||||||
|
|
||||||
await client.queryChannels(waitForConnect: false);
|
await client.queryChannelsOnline(filter: null, waitForConnect: false);
|
||||||
|
|
||||||
verify(mockDio.get<String>('/channels', queryParameters: queryParams))
|
verify(mockDio.get<String>('/channels', queryParameters: queryParams))
|
||||||
.called(1);
|
.called(1);
|
||||||
@@ -134,7 +136,7 @@ void main() {
|
|||||||
"\$in": ["test"],
|
"\$in": ["test"],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
final sortOptions = <SortOption>[];
|
final sortOptions = <SortOption<ChannelModel>>[];
|
||||||
final options = {"state": false, "watch": false, "presence": true};
|
final options = {"state": false, "watch": false, "presence": true};
|
||||||
final paginationParams = PaginationParams(
|
final paginationParams = PaginationParams(
|
||||||
limit: 10,
|
limit: 10,
|
||||||
@@ -152,10 +154,10 @@ void main() {
|
|||||||
|
|
||||||
when(mockDio.get<String>('/channels', queryParameters: queryParams))
|
when(mockDio.get<String>('/channels', queryParameters: queryParams))
|
||||||
.thenAnswer((_) async {
|
.thenAnswer((_) async {
|
||||||
return Response(data: '{}', statusCode: 200);
|
return Response(data: '{"channels":[]}', statusCode: 200);
|
||||||
});
|
});
|
||||||
|
|
||||||
await client.queryChannels(
|
await client.queryChannelsOnline(
|
||||||
filter: queryFilter,
|
filter: queryFilter,
|
||||||
sort: sortOptions,
|
sort: sortOptions,
|
||||||
options: options,
|
options: options,
|
||||||
@@ -229,6 +231,7 @@ void main() {
|
|||||||
'query': query,
|
'query': query,
|
||||||
'sort': sortOptions,
|
'sort': sortOptions,
|
||||||
'limit': 10,
|
'limit': 10,
|
||||||
|
'offset': 0,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -346,7 +349,8 @@ void main() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
when(mockDio.get<String>('/users', queryParameters: queryParams))
|
when(mockDio.get<String>('/users', queryParameters: queryParams))
|
||||||
.thenAnswer((_) async => Response(data: '{}', statusCode: 200));
|
.thenAnswer(
|
||||||
|
(_) async => Response(data: '{"users":[]}', statusCode: 200));
|
||||||
|
|
||||||
await client.queryUsers();
|
await client.queryUsers();
|
||||||
|
|
||||||
@@ -382,7 +386,7 @@ void main() {
|
|||||||
|
|
||||||
when(mockDio.get<String>('/users', queryParameters: queryParams))
|
when(mockDio.get<String>('/users', queryParameters: queryParams))
|
||||||
.thenAnswer((_) async {
|
.thenAnswer((_) async {
|
||||||
return Response(data: '{}', statusCode: 200);
|
return Response(data: '{"users":[]}', statusCode: 200);
|
||||||
});
|
});
|
||||||
|
|
||||||
await client.queryUsers(
|
await client.queryUsers(
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:rxdart/rxdart.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
import 'package:stream_chat/version.dart';
|
import 'package:stream_chat/version.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,16 @@
|
|||||||
|
## 1.3.0-beta
|
||||||
|
|
||||||
|
- Added `MessageInputTheme`
|
||||||
|
- Fixed overflow in `MessageInput` animation
|
||||||
|
- Delete only image on imagegallery
|
||||||
|
- Close keyboard after sending a command
|
||||||
|
- Exposed `customAttachmentBuilders` through `MessageListView`
|
||||||
|
- Update `stream_chat_core` dependency
|
||||||
|
|
||||||
## 1.2.0-beta
|
## 1.2.0-beta
|
||||||
|
|
||||||
- Minor fixes
|
- Minor fixes
|
||||||
- Update stream_chat_core dependency
|
- Update `stream_chat_core` dependency
|
||||||
|
|
||||||
## 1.1.1-beta
|
## 1.1.1-beta
|
||||||
|
|
||||||
|
|||||||
@@ -25,8 +25,7 @@ dependencies:
|
|||||||
sdk: flutter
|
sdk: flutter
|
||||||
stream_chat_flutter:
|
stream_chat_flutter:
|
||||||
path: ../
|
path: ../
|
||||||
stream_chat_persistence:
|
stream_chat_persistence: ^1.3.0-beta
|
||||||
path: ../../stream_chat_persistence
|
|
||||||
|
|
||||||
# The following adds the Cupertino Icons font to your application.
|
# The following adds the Cupertino Icons font to your application.
|
||||||
# Use with the CupertinoIcons class for iOS style icons.
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
|
|||||||
+3
-1
@@ -27,7 +27,9 @@ class AttachmentUploadStateBuilder extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (attachment.uploadState == null) return Offstage();
|
if (message.status == null || message.status == MessageSendingStatus.sent) {
|
||||||
|
return Offstage();
|
||||||
|
}
|
||||||
|
|
||||||
final messageId = message.id;
|
final messageId = message.id;
|
||||||
final attachmentId = attachment.id;
|
final attachmentId = attachment.id;
|
||||||
|
|||||||
@@ -227,6 +227,20 @@ class FileAttachment extends AttachmentWidget {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (message != null &&
|
||||||
|
(message.status == null ||
|
||||||
|
message.status == MessageSendingStatus.sent)) {
|
||||||
|
trailingWidget = IconButton(
|
||||||
|
icon: StreamSvgIcon.cloudDownload(color: theme.colorTheme.black),
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
splashRadius: 16,
|
||||||
|
onPressed: () {
|
||||||
|
launchURL(context, attachment.assetUrl);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return Material(
|
return Material(
|
||||||
type: MaterialType.transparency,
|
type: MaterialType.transparency,
|
||||||
child: trailingWidget,
|
child: trailingWidget,
|
||||||
@@ -241,6 +255,9 @@ class FileAttachment extends AttachmentWidget {
|
|||||||
);
|
);
|
||||||
return attachment.uploadState?.when(
|
return attachment.uploadState?.when(
|
||||||
preparing: () {
|
preparing: () {
|
||||||
|
if (message == null) {
|
||||||
|
return Text('${fileSize(size, 2)}', style: textStyle);
|
||||||
|
}
|
||||||
return UploadProgressIndicator(
|
return UploadProgressIndicator(
|
||||||
uploaded: 0,
|
uploaded: 0,
|
||||||
total: double.maxFinite.toInt(),
|
total: double.maxFinite.toInt(),
|
||||||
|
|||||||
@@ -49,8 +49,11 @@ class ChannelInfo extends StatelessWidget {
|
|||||||
var alternativeWidget;
|
var alternativeWidget;
|
||||||
|
|
||||||
if (channel.memberCount != null && channel.memberCount > 2) {
|
if (channel.memberCount != null && channel.memberCount > 2) {
|
||||||
|
var text = '${channel.memberCount} Members';
|
||||||
|
final watcherCount = channel.state.watcherCount ?? 0;
|
||||||
|
if (watcherCount > 0) text += ' $watcherCount Online';
|
||||||
alternativeWidget = Text(
|
alternativeWidget = Text(
|
||||||
'${channel.memberCount} Members, ${channel.state.watcherCount} Online',
|
text,
|
||||||
style: StreamChatTheme.of(context)
|
style: StreamChatTheme.of(context)
|
||||||
.channelTheme
|
.channelTheme
|
||||||
.channelHeaderTheme
|
.channelHeaderTheme
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||||
@@ -99,7 +96,7 @@ class ChannelListView extends StatefulWidget {
|
|||||||
/// Sorting is based on field and direction, multiple sorting options can be provided.
|
/// Sorting is based on field and direction, multiple sorting options can be provided.
|
||||||
/// You can sort based on last_updated, last_message_at, updated_at, created_at or member_count.
|
/// You can sort based on last_updated, last_message_at, updated_at, created_at or member_count.
|
||||||
/// Direction can be ascending or descending.
|
/// Direction can be ascending or descending.
|
||||||
final List<SortOption> sort;
|
final List<SortOption<ChannelModel>> sort;
|
||||||
|
|
||||||
/// Pagination parameters
|
/// Pagination parameters
|
||||||
/// limit: the number of channels to return (max is 30)
|
/// limit: the number of channels to return (max is 30)
|
||||||
@@ -159,54 +156,40 @@ class ChannelListView extends StatefulWidget {
|
|||||||
_ChannelListViewState createState() => _ChannelListViewState();
|
_ChannelListViewState createState() => _ChannelListViewState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ChannelListViewState extends State<ChannelListView>
|
class _ChannelListViewState extends State<ChannelListView> {
|
||||||
with WidgetsBindingObserver {
|
final _slideController = SlidableController();
|
||||||
final ScrollController _scrollController = ScrollController();
|
|
||||||
final SlidableController _slideController = SlidableController();
|
final _channelListController = ChannelListController();
|
||||||
final ChannelListController _channelListController = ChannelListController();
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var child = ChannelListCore(
|
Widget child = ChannelListCore(
|
||||||
channelListController: _channelListController,
|
|
||||||
listBuilder: widget.listBuilder ??
|
|
||||||
(context, list) {
|
|
||||||
return _buildListView(list);
|
|
||||||
},
|
|
||||||
emptyBuilder: widget.emptyBuilder ??
|
|
||||||
(BuildContext context) {
|
|
||||||
return _buildEmptyWidget();
|
|
||||||
},
|
|
||||||
errorBuilder: widget.errorBuilder ??
|
|
||||||
(BuildContext context, dynamic error) {
|
|
||||||
return _buildErrorWidget(context);
|
|
||||||
},
|
|
||||||
loadingBuilder: widget.loadingBuilder ??
|
|
||||||
(BuildContext context) {
|
|
||||||
return _buildLoadingWidget();
|
|
||||||
},
|
|
||||||
pagination: widget.pagination,
|
pagination: widget.pagination,
|
||||||
options: widget.options,
|
options: widget.options,
|
||||||
sort: widget.sort,
|
sort: widget.sort,
|
||||||
filter: widget.filter,
|
filter: widget.filter,
|
||||||
|
channelListController: _channelListController,
|
||||||
|
listBuilder: widget.listBuilder ?? _buildListView,
|
||||||
|
emptyBuilder: widget.emptyBuilder ?? _buildEmptyWidget,
|
||||||
|
errorBuilder: widget.errorBuilder ?? _buildErrorWidget,
|
||||||
|
loadingBuilder: widget.loadingBuilder ?? _buildLoadingWidget,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!widget.pullToRefresh) {
|
if (widget.pullToRefresh) {
|
||||||
return child;
|
child = RefreshIndicator(
|
||||||
} else {
|
onRefresh: () => _channelListController.loadData(),
|
||||||
return RefreshIndicator(
|
|
||||||
onRefresh: () async {
|
|
||||||
_channelListController.loadData();
|
|
||||||
},
|
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return LazyLoadScrollView(
|
||||||
|
onEndOfPage: () => _channelListController.paginateData(),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildListView(
|
Widget _buildListView(BuildContext context, List<Channel> channels) {
|
||||||
List<Channel> channels,
|
Widget child;
|
||||||
) {
|
|
||||||
var child;
|
|
||||||
|
|
||||||
if (channels.isNotEmpty) {
|
if (channels.isNotEmpty) {
|
||||||
if (widget.crossAxisCount > 1) {
|
if (widget.crossAxisCount > 1) {
|
||||||
@@ -216,7 +199,6 @@ class _ChannelListViewState extends State<ChannelListView>
|
|||||||
crossAxisCount: widget.crossAxisCount),
|
crossAxisCount: widget.crossAxisCount),
|
||||||
itemCount: channels.length,
|
itemCount: channels.length,
|
||||||
physics: AlwaysScrollableScrollPhysics(),
|
physics: AlwaysScrollableScrollPhysics(),
|
||||||
controller: _scrollController,
|
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
return _gridItemBuilder(context, index, channels);
|
return _gridItemBuilder(context, index, channels);
|
||||||
},
|
},
|
||||||
@@ -236,18 +218,17 @@ class _ChannelListViewState extends State<ChannelListView>
|
|||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
return _listItemBuilder(context, index, channels);
|
return _listItemBuilder(context, index, channels);
|
||||||
},
|
},
|
||||||
controller: _scrollController,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return AnimatedSwitcher(
|
return AnimatedSwitcher(
|
||||||
child: child,
|
child: child,
|
||||||
duration: Duration(milliseconds: 500),
|
duration: const Duration(milliseconds: 500),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildEmptyWidget() {
|
Widget _buildEmptyWidget(BuildContext context) {
|
||||||
return LayoutBuilder(
|
return LayoutBuilder(
|
||||||
builder: (context, viewportConstraints) {
|
builder: (context, viewportConstraints) {
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
@@ -326,7 +307,7 @@ class _ChannelListViewState extends State<ChannelListView>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildLoadingWidget() {
|
Widget _buildLoadingWidget(BuildContext context) {
|
||||||
return ListView(
|
return ListView(
|
||||||
padding: widget.padding,
|
padding: widget.padding,
|
||||||
physics: AlwaysScrollableScrollPhysics(),
|
physics: AlwaysScrollableScrollPhysics(),
|
||||||
@@ -341,13 +322,13 @@ class _ChannelListViewState extends State<ChannelListView>
|
|||||||
return _separatorBuilder(context, i);
|
return _separatorBuilder(context, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return _buildLoadingItem();
|
return _buildLoadingItem(context);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Shimmer _buildLoadingItem() {
|
Shimmer _buildLoadingItem(BuildContext context) {
|
||||||
if (widget.crossAxisCount > 1) {
|
if (widget.crossAxisCount > 1) {
|
||||||
return Shimmer.fromColors(
|
return Shimmer.fromColors(
|
||||||
baseColor: StreamChatTheme.of(context).colorTheme.greyGainsboro,
|
baseColor: StreamChatTheme.of(context).colorTheme.greyGainsboro,
|
||||||
@@ -443,9 +424,7 @@ class _ChannelListViewState extends State<ChannelListView>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildErrorWidget(
|
Widget _buildErrorWidget(BuildContext context, Object error) {
|
||||||
BuildContext context,
|
|
||||||
) {
|
|
||||||
return Center(
|
return Center(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
@@ -467,9 +446,7 @@ class _ChannelListViewState extends State<ChannelListView>
|
|||||||
style: Theme.of(context).textTheme.headline6,
|
style: Theme.of(context).textTheme.headline6,
|
||||||
),
|
),
|
||||||
FlatButton(
|
FlatButton(
|
||||||
onPressed: () {
|
onPressed: () => _channelListController.loadData(),
|
||||||
_channelListController.loadData();
|
|
||||||
},
|
|
||||||
child: Text('Retry'),
|
child: Text('Retry'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -572,23 +549,13 @@ class _ChannelListViewState extends State<ChannelListView>
|
|||||||
],
|
],
|
||||||
child: Container(
|
child: Container(
|
||||||
color: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
color: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
||||||
child: widget.channelPreviewBuilder != null
|
child: widget.channelPreviewBuilder?.call(context, channel) ??
|
||||||
? widget.channelPreviewBuilder(
|
ChannelPreview(
|
||||||
context,
|
onLongPress: widget.onChannelLongPress,
|
||||||
channel,
|
channel: channel,
|
||||||
)
|
onImageTap: widget.onImageTap?.call(channel),
|
||||||
: ChannelPreview(
|
onTap: (channel) => onTap(channel, widget.channelWidget),
|
||||||
onLongPress: widget.onChannelLongPress,
|
),
|
||||||
channel: channel,
|
|
||||||
onImageTap: widget.onImageTap != null
|
|
||||||
? () {
|
|
||||||
widget.onImageTap(channel);
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
onTap: (channel) {
|
|
||||||
onTap(channel, widget.channelWidget);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -618,9 +585,7 @@ class _ChannelListViewState extends State<ChannelListView>
|
|||||||
width: 64,
|
width: 64,
|
||||||
height: 64,
|
height: 64,
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () => widget.onChannelTap(channel, null),
|
||||||
widget.onChannelTap(channel, null);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
SizedBox(height: 7),
|
SizedBox(height: 7),
|
||||||
Padding(
|
Padding(
|
||||||
@@ -680,70 +645,4 @@ class _ChannelListViewState extends State<ChannelListView>
|
|||||||
color: effect.color.withOpacity(effect.alpha ?? 1.0),
|
color: effect.color.withOpacity(effect.alpha ?? 1.0),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _listenChannelPagination(ChannelsBlocState channelsProvider) {
|
|
||||||
if (_scrollController.position.maxScrollExtent ==
|
|
||||||
_scrollController.offset &&
|
|
||||||
_scrollController.offset != 0) {
|
|
||||||
_channelListController.paginateData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StreamSubscription _subscription;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
|
|
||||||
WidgetsBinding.instance.addObserver(this);
|
|
||||||
|
|
||||||
final channelsBloc = ChannelsBloc.of(context);
|
|
||||||
channelsBloc.queryChannels(
|
|
||||||
filter: widget.filter,
|
|
||||||
sortOptions: widget.sort,
|
|
||||||
paginationParams: widget.pagination,
|
|
||||||
options: widget.options,
|
|
||||||
);
|
|
||||||
|
|
||||||
_scrollController.addListener(() {
|
|
||||||
channelsBloc.queryChannelsLoading.first.then((loading) {
|
|
||||||
if (!loading) {
|
|
||||||
_listenChannelPagination(channelsBloc);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
final client = StreamChat.of(context).client;
|
|
||||||
|
|
||||||
_subscription = client
|
|
||||||
.on(
|
|
||||||
EventType.connectionRecovered,
|
|
||||||
EventType.notificationAddedToChannel,
|
|
||||||
EventType.notificationMessageNew,
|
|
||||||
EventType.channelVisible,
|
|
||||||
)
|
|
||||||
.listen((event) {
|
|
||||||
_channelListController.loadData();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void didUpdateWidget(ChannelListView oldWidget) {
|
|
||||||
super.didUpdateWidget(oldWidget);
|
|
||||||
|
|
||||||
if (widget.filter?.toString() != oldWidget.filter?.toString() ||
|
|
||||||
jsonEncode(widget.sort) != jsonEncode(oldWidget.sort) ||
|
|
||||||
widget.pagination?.toJson()?.toString() !=
|
|
||||||
oldWidget.pagination?.toJson()?.toString() ||
|
|
||||||
widget.options?.toString() != oldWidget.options?.toString()) {
|
|
||||||
_channelListController.loadData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_subscription.cancel();
|
|
||||||
WidgetsBinding.instance.removeObserver(this);
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,11 +103,22 @@ class ImageActionsModal extends StatelessWidget {
|
|||||||
color: StreamChatTheme.of(context).colorTheme.accentRed,
|
color: StreamChatTheme.of(context).colorTheme.accentRed,
|
||||||
),
|
),
|
||||||
() {
|
() {
|
||||||
Navigator.pop(context);
|
final channel = StreamChannel.of(context).channel;
|
||||||
Navigator.pop(context);
|
if (message.attachments.length > 1 ||
|
||||||
StreamChannel.of(context)
|
message.text.isNotEmpty) {
|
||||||
.channel
|
final remainingAttachments = [...message.attachments]
|
||||||
.deleteMessage(message);
|
..removeAt(currentIndex);
|
||||||
|
channel.updateMessage(message.copyWith(
|
||||||
|
attachments: remainingAttachments,
|
||||||
|
));
|
||||||
|
Navigator.pop(context);
|
||||||
|
Navigator.pop(context);
|
||||||
|
} else {
|
||||||
|
channel.deleteMessage(message).then((value) {
|
||||||
|
Navigator.pop(context);
|
||||||
|
Navigator.pop(context);
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
color: StreamChatTheme.of(context).colorTheme.accentRed,
|
color: StreamChatTheme.of(context).colorTheme.accentRed,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -596,7 +596,8 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
|||||||
elevation: 2,
|
elevation: 2,
|
||||||
clipBehavior: Clip.hardEdge,
|
clipBehavior: Clip.hardEdge,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
backgroundColor: StreamChatTheme.of(context).channelTheme.inputBackground,
|
backgroundColor:
|
||||||
|
StreamChatTheme.of(context).messageInputTheme.inputBackground,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.only(
|
borderRadius: BorderRadius.only(
|
||||||
topLeft: Radius.circular(16),
|
topLeft: Radius.circular(16),
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Widget child = Container(
|
Widget child = Container(
|
||||||
color: StreamChatTheme.of(context).channelTheme.inputBackground,
|
color: StreamChatTheme.of(context).messageInputTheme.inputBackground,
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onPanUpdate: (details) {
|
onPanUpdate: (details) {
|
||||||
@@ -392,7 +392,8 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
: CrossFadeState.showSecond,
|
: CrossFadeState.showSecond,
|
||||||
firstChild: _buildSendButton(context),
|
firstChild: _buildSendButton(context),
|
||||||
secondChild: _buildIdleSendButton(context),
|
secondChild: _buildIdleSendButton(context),
|
||||||
duration: Duration(milliseconds: 300),
|
duration:
|
||||||
|
StreamChatTheme.of(context).messageInputTheme.sendAnimationDuration,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -417,19 +418,21 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
),
|
),
|
||||||
splashRadius: 24,
|
splashRadius: 24,
|
||||||
),
|
),
|
||||||
secondChild: Row(
|
secondChild: FittedBox(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
child: Row(
|
||||||
children: [
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
if (!widget.disableAttachments) _buildAttachmentButton(),
|
children: [
|
||||||
if (widget.editMessage == null &&
|
if (!widget.disableAttachments) _buildAttachmentButton(),
|
||||||
StreamChannel.of(context)
|
if (widget.editMessage == null &&
|
||||||
.channel
|
StreamChannel.of(context)
|
||||||
?.config
|
.channel
|
||||||
?.commands
|
?.config
|
||||||
?.isNotEmpty ==
|
?.commands
|
||||||
true)
|
?.isNotEmpty ==
|
||||||
_buildCommandButton(),
|
true)
|
||||||
].insertBetween(const SizedBox(width: 8)),
|
_buildCommandButton(),
|
||||||
|
].insertBetween(const SizedBox(width: 8)),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
duration: Duration(milliseconds: 300),
|
duration: Duration(milliseconds: 300),
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
@@ -1683,8 +1686,10 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
return IconButton(
|
return IconButton(
|
||||||
icon: StreamSvgIcon.lightning(
|
icon: StreamSvgIcon.lightning(
|
||||||
color: _commandsOverlay != null
|
color: _commandsOverlay != null
|
||||||
? StreamChatTheme.of(context).channelTheme.actionButtonColor
|
? StreamChatTheme.of(context).messageInputTheme.actionButtonColor
|
||||||
: StreamChatTheme.of(context).channelTheme.actionButtonIdleColor,
|
: StreamChatTheme.of(context)
|
||||||
|
.messageInputTheme
|
||||||
|
.actionButtonIdleColor,
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.all(0),
|
padding: const EdgeInsets.all(0),
|
||||||
constraints: BoxConstraints.tightFor(
|
constraints: BoxConstraints.tightFor(
|
||||||
@@ -1721,8 +1726,10 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
return IconButton(
|
return IconButton(
|
||||||
icon: StreamSvgIcon.attach(
|
icon: StreamSvgIcon.attach(
|
||||||
color: _openFilePickerSection
|
color: _openFilePickerSection
|
||||||
? StreamChatTheme.of(context).channelTheme.actionButtonColor
|
? StreamChatTheme.of(context).messageInputTheme.actionButtonColor
|
||||||
: StreamChatTheme.of(context).channelTheme.actionButtonIdleColor,
|
: StreamChatTheme.of(context)
|
||||||
|
.messageInputTheme
|
||||||
|
.actionButtonIdleColor,
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.all(0),
|
padding: const EdgeInsets.all(0),
|
||||||
constraints: BoxConstraints.tightFor(
|
constraints: BoxConstraints.tightFor(
|
||||||
@@ -1961,7 +1968,7 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
Widget _buildIdleSendButton(BuildContext context) {
|
Widget _buildIdleSendButton(BuildContext context) {
|
||||||
return StreamSvgIcon(
|
return StreamSvgIcon(
|
||||||
assetName: _getIdleSendIcon(),
|
assetName: _getIdleSendIcon(),
|
||||||
color: StreamChatTheme.of(context).channelTheme.sendButtonIdleColor,
|
color: StreamChatTheme.of(context).messageInputTheme.sendButtonIdleColor,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1976,7 +1983,7 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
),
|
),
|
||||||
icon: StreamSvgIcon(
|
icon: StreamSvgIcon(
|
||||||
assetName: _getSendIcon(),
|
assetName: _getSendIcon(),
|
||||||
color: StreamChatTheme.of(context).channelTheme.sendButtonColor,
|
color: StreamChatTheme.of(context).messageInputTheme.sendButtonColor,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -2008,6 +2015,7 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
|
|
||||||
if (_commandEnabled) {
|
if (_commandEnabled) {
|
||||||
text = '/${_chosenCommand.name} ' + text;
|
text = '/${_chosenCommand.name} ' + text;
|
||||||
|
FocusScope.of(context).unfocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
final attachments = [..._attachments.values];
|
final attachments = [..._attachments.values];
|
||||||
|
|||||||
@@ -130,6 +130,7 @@ class MessageListView extends StatefulWidget {
|
|||||||
this.emptyBuilder,
|
this.emptyBuilder,
|
||||||
this.messageListBuilder,
|
this.messageListBuilder,
|
||||||
this.errorWidgetBuilder,
|
this.errorWidgetBuilder,
|
||||||
|
this.customAttachmentBuilders,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
/// Function used to build a custom message widget
|
/// Function used to build a custom message widget
|
||||||
@@ -203,6 +204,10 @@ class MessageListView extends StatefulWidget {
|
|||||||
/// of a connection failure.
|
/// of a connection failure.
|
||||||
final ErrorBuilder errorWidgetBuilder;
|
final ErrorBuilder errorWidgetBuilder;
|
||||||
|
|
||||||
|
/// Attachment builders for the default message widget
|
||||||
|
/// Please change this in the [MessageWidget] if you are using a custom implementation
|
||||||
|
final Map<String, AttachmentBuilder> customAttachmentBuilders;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_MessageListViewState createState() => _MessageListViewState();
|
_MessageListViewState createState() => _MessageListViewState();
|
||||||
}
|
}
|
||||||
@@ -798,6 +803,7 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
customAttachmentBuilders: widget.customAttachmentBuilders,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -958,6 +964,7 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
customAttachmentBuilders: widget.customAttachmentBuilders,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!message.isDeleted &&
|
if (!message.isDeleted &&
|
||||||
|
|||||||
@@ -60,6 +60,9 @@ class StreamChatThemeData {
|
|||||||
/// Theme of other users messages
|
/// Theme of other users messages
|
||||||
final MessageTheme otherMessageTheme;
|
final MessageTheme otherMessageTheme;
|
||||||
|
|
||||||
|
/// Theme of other users messages
|
||||||
|
final MessageInputTheme messageInputTheme;
|
||||||
|
|
||||||
/// The widget that will be built when the channel image is unavailable
|
/// The widget that will be built when the channel image is unavailable
|
||||||
final Widget Function(BuildContext, Channel) defaultChannelImage;
|
final Widget Function(BuildContext, Channel) defaultChannelImage;
|
||||||
|
|
||||||
@@ -80,6 +83,7 @@ class StreamChatThemeData {
|
|||||||
this.channelTheme,
|
this.channelTheme,
|
||||||
this.otherMessageTheme,
|
this.otherMessageTheme,
|
||||||
this.ownMessageTheme,
|
this.ownMessageTheme,
|
||||||
|
this.messageInputTheme,
|
||||||
this.defaultChannelImage,
|
this.defaultChannelImage,
|
||||||
this.defaultUserImage,
|
this.defaultUserImage,
|
||||||
this.primaryIconTheme,
|
this.primaryIconTheme,
|
||||||
@@ -108,6 +112,7 @@ class StreamChatThemeData {
|
|||||||
ChannelTheme channelTheme,
|
ChannelTheme channelTheme,
|
||||||
MessageTheme ownMessageTheme,
|
MessageTheme ownMessageTheme,
|
||||||
MessageTheme otherMessageTheme,
|
MessageTheme otherMessageTheme,
|
||||||
|
MessageInputTheme messageInputTheme,
|
||||||
Widget Function(BuildContext, Channel) defaultChannelImage,
|
Widget Function(BuildContext, Channel) defaultChannelImage,
|
||||||
Widget Function(BuildContext, User) defaultUserImage,
|
Widget Function(BuildContext, User) defaultUserImage,
|
||||||
IconThemeData primaryIconTheme,
|
IconThemeData primaryIconTheme,
|
||||||
@@ -123,6 +128,7 @@ class StreamChatThemeData {
|
|||||||
channelTheme: channelTheme ?? this.channelTheme,
|
channelTheme: channelTheme ?? this.channelTheme,
|
||||||
ownMessageTheme: ownMessageTheme ?? this.ownMessageTheme,
|
ownMessageTheme: ownMessageTheme ?? this.ownMessageTheme,
|
||||||
otherMessageTheme: otherMessageTheme ?? this.otherMessageTheme,
|
otherMessageTheme: otherMessageTheme ?? this.otherMessageTheme,
|
||||||
|
messageInputTheme: messageInputTheme ?? this.messageInputTheme,
|
||||||
reactionIcons: reactionIcons ?? this.reactionIcons,
|
reactionIcons: reactionIcons ?? this.reactionIcons,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -143,6 +149,8 @@ class StreamChatThemeData {
|
|||||||
other.ownMessageTheme,
|
other.ownMessageTheme,
|
||||||
otherMessageTheme: otherMessageTheme?.merge(other.otherMessageTheme) ??
|
otherMessageTheme: otherMessageTheme?.merge(other.otherMessageTheme) ??
|
||||||
other.otherMessageTheme,
|
other.otherMessageTheme,
|
||||||
|
messageInputTheme: messageInputTheme?.merge(other.messageInputTheme) ??
|
||||||
|
other.messageInputTheme,
|
||||||
reactionIcons: other.reactionIcons,
|
reactionIcons: other.reactionIcons,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -182,10 +190,6 @@ class StreamChatThemeData {
|
|||||||
),
|
),
|
||||||
indicatorIconSize: 16.0),
|
indicatorIconSize: 16.0),
|
||||||
channelTheme: ChannelTheme(
|
channelTheme: ChannelTheme(
|
||||||
actionButtonColor: colorTheme.accentBlue,
|
|
||||||
actionButtonIdleColor: colorTheme.grey,
|
|
||||||
sendButtonColor: colorTheme.accentBlue,
|
|
||||||
sendButtonIdleColor: colorTheme.greyGainsboro,
|
|
||||||
channelHeaderTheme: ChannelHeaderTheme(
|
channelHeaderTheme: ChannelHeaderTheme(
|
||||||
avatarTheme: AvatarTheme(
|
avatarTheme: AvatarTheme(
|
||||||
borderRadius: BorderRadius.circular(20),
|
borderRadius: BorderRadius.circular(20),
|
||||||
@@ -195,16 +199,12 @@ class StreamChatThemeData {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
color: colorTheme.white,
|
color: colorTheme.white,
|
||||||
title: TextStyle(
|
title: textTheme.headlineBold,
|
||||||
fontSize: 14,
|
|
||||||
color: colorTheme.black,
|
|
||||||
),
|
|
||||||
lastMessageAt: TextStyle(
|
lastMessageAt: TextStyle(
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
color: colorTheme.black.withOpacity(.5),
|
color: colorTheme.black.withOpacity(.5),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
inputBackground: colorTheme.white.withAlpha(12),
|
|
||||||
),
|
),
|
||||||
ownMessageTheme: MessageTheme(
|
ownMessageTheme: MessageTheme(
|
||||||
messageAuthor: textTheme.footnote.copyWith(color: colorTheme.grey),
|
messageAuthor: textTheme.footnote.copyWith(color: colorTheme.grey),
|
||||||
@@ -248,6 +248,14 @@ class StreamChatThemeData {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
messageInputTheme: MessageInputTheme(
|
||||||
|
sendAnimationDuration: Duration(milliseconds: 300),
|
||||||
|
actionButtonColor: colorTheme.accentBlue,
|
||||||
|
actionButtonIdleColor: colorTheme.grey,
|
||||||
|
sendButtonColor: colorTheme.accentBlue,
|
||||||
|
sendButtonIdleColor: colorTheme.greyGainsboro,
|
||||||
|
inputBackground: colorTheme.white,
|
||||||
|
),
|
||||||
reactionIcons: [
|
reactionIcons: [
|
||||||
ReactionIcon(
|
ReactionIcon(
|
||||||
type: 'love',
|
type: 'love',
|
||||||
@@ -628,47 +636,16 @@ class ChannelTheme {
|
|||||||
/// Theme of the [ChannelHeader] widget
|
/// Theme of the [ChannelHeader] widget
|
||||||
final ChannelHeaderTheme channelHeaderTheme;
|
final ChannelHeaderTheme channelHeaderTheme;
|
||||||
|
|
||||||
/// Background color of [MessageInput] send button
|
|
||||||
final Color sendButtonColor;
|
|
||||||
|
|
||||||
/// Background color of [MessageInput] action buttons
|
|
||||||
final Color actionButtonColor;
|
|
||||||
|
|
||||||
/// Background color of [MessageInput] send button
|
|
||||||
final Color sendButtonIdleColor;
|
|
||||||
|
|
||||||
/// Background color of [MessageInput] action buttons
|
|
||||||
final Color actionButtonIdleColor;
|
|
||||||
|
|
||||||
/// Background color of [MessageInput]
|
|
||||||
final Color inputBackground;
|
|
||||||
|
|
||||||
ChannelTheme({
|
ChannelTheme({
|
||||||
this.channelHeaderTheme,
|
this.channelHeaderTheme,
|
||||||
this.actionButtonColor,
|
|
||||||
this.sendButtonColor,
|
|
||||||
this.actionButtonIdleColor,
|
|
||||||
this.sendButtonIdleColor,
|
|
||||||
this.inputBackground,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Creates a copy of [ChannelTheme] with specified attributes overridden.
|
/// Creates a copy of [ChannelTheme] with specified attributes overridden.
|
||||||
ChannelTheme copyWith({
|
ChannelTheme copyWith({
|
||||||
ChannelHeaderTheme channelHeaderTheme,
|
ChannelHeaderTheme channelHeaderTheme,
|
||||||
Color inputBackground,
|
|
||||||
Color actionButtonColor,
|
|
||||||
Color sendButtonColor,
|
|
||||||
Color actionButtonIdleColor,
|
|
||||||
Color sendButtonIdleColor,
|
|
||||||
}) =>
|
}) =>
|
||||||
ChannelTheme(
|
ChannelTheme(
|
||||||
channelHeaderTheme: channelHeaderTheme ?? this.channelHeaderTheme,
|
channelHeaderTheme: channelHeaderTheme ?? this.channelHeaderTheme,
|
||||||
inputBackground: inputBackground ?? this.inputBackground,
|
|
||||||
actionButtonColor: actionButtonColor ?? this.actionButtonColor,
|
|
||||||
sendButtonColor: sendButtonColor ?? this.sendButtonColor,
|
|
||||||
actionButtonIdleColor:
|
|
||||||
actionButtonIdleColor ?? this.actionButtonIdleColor,
|
|
||||||
sendButtonIdleColor: sendButtonIdleColor ?? this.sendButtonIdleColor,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
ChannelTheme merge(ChannelTheme other) {
|
ChannelTheme merge(ChannelTheme other) {
|
||||||
@@ -676,11 +653,6 @@ class ChannelTheme {
|
|||||||
return copyWith(
|
return copyWith(
|
||||||
channelHeaderTheme: channelHeaderTheme?.merge(other.channelHeaderTheme) ??
|
channelHeaderTheme: channelHeaderTheme?.merge(other.channelHeaderTheme) ??
|
||||||
other.channelHeaderTheme,
|
other.channelHeaderTheme,
|
||||||
inputBackground: other.inputBackground,
|
|
||||||
actionButtonColor: other.actionButtonColor,
|
|
||||||
actionButtonIdleColor: other.actionButtonIdleColor,
|
|
||||||
sendButtonColor: other.sendButtonColor,
|
|
||||||
sendButtonIdleColor: other.sendButtonIdleColor,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -873,6 +845,70 @@ class ChannelHeaderTheme {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Defines the theme dedicated to the [MessageInput] widget
|
||||||
|
class MessageInputTheme {
|
||||||
|
/// Duration of the [MessageInput] send button animation
|
||||||
|
final Duration sendAnimationDuration;
|
||||||
|
|
||||||
|
/// Background color of [MessageInput] send button
|
||||||
|
final Color sendButtonColor;
|
||||||
|
|
||||||
|
/// Background color of [MessageInput] action buttons
|
||||||
|
final Color actionButtonColor;
|
||||||
|
|
||||||
|
/// Background color of [MessageInput] send button
|
||||||
|
final Color sendButtonIdleColor;
|
||||||
|
|
||||||
|
/// Background color of [MessageInput] action buttons
|
||||||
|
final Color actionButtonIdleColor;
|
||||||
|
|
||||||
|
/// Background color of [MessageInput]
|
||||||
|
final Color inputBackground;
|
||||||
|
|
||||||
|
/// Returns a new [MessageInputTheme]
|
||||||
|
const MessageInputTheme({
|
||||||
|
this.sendAnimationDuration,
|
||||||
|
this.actionButtonColor,
|
||||||
|
this.sendButtonColor,
|
||||||
|
this.actionButtonIdleColor,
|
||||||
|
this.sendButtonIdleColor,
|
||||||
|
this.inputBackground,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Returns a new [MessageInputTheme] replacing some of its properties
|
||||||
|
MessageInputTheme copyWith({
|
||||||
|
Duration sendAnimationDuration,
|
||||||
|
Color inputBackground,
|
||||||
|
Color actionButtonColor,
|
||||||
|
Color sendButtonColor,
|
||||||
|
Color actionButtonIdleColor,
|
||||||
|
Color sendButtonIdleColor,
|
||||||
|
}) =>
|
||||||
|
MessageInputTheme(
|
||||||
|
sendAnimationDuration:
|
||||||
|
sendAnimationDuration ?? this.sendAnimationDuration,
|
||||||
|
inputBackground: inputBackground ?? this.inputBackground,
|
||||||
|
actionButtonColor: actionButtonColor ?? this.actionButtonColor,
|
||||||
|
sendButtonColor: sendButtonColor ?? this.sendButtonColor,
|
||||||
|
actionButtonIdleColor:
|
||||||
|
actionButtonIdleColor ?? this.actionButtonIdleColor,
|
||||||
|
sendButtonIdleColor: sendButtonIdleColor ?? this.sendButtonIdleColor,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Merges [this] [MessageInputTheme] with the [other]
|
||||||
|
MessageInputTheme merge(MessageInputTheme other) {
|
||||||
|
if (other == null) return this;
|
||||||
|
return copyWith(
|
||||||
|
sendAnimationDuration: other.sendAnimationDuration,
|
||||||
|
inputBackground: other.inputBackground,
|
||||||
|
actionButtonColor: other.actionButtonColor,
|
||||||
|
actionButtonIdleColor: other.actionButtonIdleColor,
|
||||||
|
sendButtonColor: other.sendButtonColor,
|
||||||
|
sendButtonIdleColor: other.sendButtonIdleColor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class Effect {
|
class Effect {
|
||||||
final double sigmaX;
|
final double sigmaX;
|
||||||
final double sigmaY;
|
final double sigmaY;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
name: stream_chat_flutter
|
name: stream_chat_flutter
|
||||||
homepage: https://github.com/GetStream/stream-chat-flutter
|
homepage: https://github.com/GetStream/stream-chat-flutter
|
||||||
description: Stream Chat official Flutter SDK. Build your own chat experience using Dart and Flutter.
|
description: Stream Chat official Flutter SDK. Build your own chat experience using Dart and Flutter.
|
||||||
version: 1.2.0-beta
|
version: 1.3.0-beta
|
||||||
repository: https://github.com/GetStream/stream-chat-flutter
|
repository: https://github.com/GetStream/stream-chat-flutter
|
||||||
issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues
|
issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues
|
||||||
|
|
||||||
@@ -11,7 +11,7 @@ environment:
|
|||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
stream_chat_flutter_core: ^1.2.0-beta
|
stream_chat_flutter_core: ^1.3.0-beta
|
||||||
flutter_app_badger: ^1.1.2
|
flutter_app_badger: ^1.1.2
|
||||||
photo_view: ^0.10.3
|
photo_view: ^0.10.3
|
||||||
rxdart: ^0.25.0
|
rxdart: ^0.25.0
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
## 1.3.0-beta
|
||||||
|
|
||||||
|
* Update llc dependency
|
||||||
|
* Minor fixes
|
||||||
|
|
||||||
## 1.2.0-beta
|
## 1.2.0-beta
|
||||||
|
|
||||||
* Update llc dependency
|
* Update llc dependency
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
> The official Flutter core components for Stream Chat, a service for
|
> The official Flutter core components for Stream Chat, a service for
|
||||||
> building chat applications.
|
> building chat applications.
|
||||||
|
|
||||||
[](https://pub.dartlang.org/packages/stream_chat_flutter)
|
[](https://pub.dartlang.org/packages/stream_chat_flutter_core)
|
||||||

|

|
||||||
[](https://gitter.im/GetStream/stream-chat-flutter?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
[](https://gitter.im/GetStream/stream-chat-flutter?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||||

|

|
||||||
|
|||||||
@@ -106,7 +106,7 @@ class ChannelListCore extends StatefulWidget {
|
|||||||
/// Sorting is based on field and direction, multiple sorting options can be provided.
|
/// Sorting is based on field and direction, multiple sorting options can be provided.
|
||||||
/// You can sort based on last_updated, last_message_at, updated_at, created_at or member_count.
|
/// You can sort based on last_updated, last_message_at, updated_at, created_at or member_count.
|
||||||
/// Direction can be ascending or descending.
|
/// Direction can be ascending or descending.
|
||||||
final List<SortOption> sort;
|
final List<SortOption<ChannelModel>> sort;
|
||||||
|
|
||||||
/// Pagination parameters
|
/// Pagination parameters
|
||||||
/// limit: the number of channels to return (max is 30)
|
/// limit: the number of channels to return (max is 30)
|
||||||
@@ -118,8 +118,7 @@ class ChannelListCore extends StatefulWidget {
|
|||||||
_ChannelListCoreState createState() => _ChannelListCoreState();
|
_ChannelListCoreState createState() => _ChannelListCoreState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ChannelListCoreState extends State<ChannelListCore>
|
class _ChannelListCoreState extends State<ChannelListCore> {
|
||||||
with WidgetsBindingObserver {
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final channelsBloc = ChannelsBloc.of(context);
|
final channelsBloc = ChannelsBloc.of(context);
|
||||||
@@ -133,34 +132,21 @@ class _ChannelListCoreState extends State<ChannelListCore>
|
|||||||
return StreamBuilder<List<Channel>>(
|
return StreamBuilder<List<Channel>>(
|
||||||
stream: channelsBlocState.channelsStream,
|
stream: channelsBlocState.channelsStream,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
var child;
|
|
||||||
if (snapshot.hasError) {
|
if (snapshot.hasError) {
|
||||||
child = _buildErrorWidget(
|
return _buildErrorWidget(snapshot, context, channelsBlocState);
|
||||||
snapshot,
|
|
||||||
context,
|
|
||||||
channelsBlocState,
|
|
||||||
);
|
|
||||||
} else if (!snapshot.hasData) {
|
|
||||||
child = _buildLoadingWidget();
|
|
||||||
} else {
|
|
||||||
final channels = snapshot.data;
|
|
||||||
|
|
||||||
child = widget.emptyBuilder(context);
|
|
||||||
|
|
||||||
if (channels.isNotEmpty) {
|
|
||||||
return widget.listBuilder(context, channels);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (!snapshot.hasData) {
|
||||||
return child;
|
return widget.loadingBuilder(context);
|
||||||
|
}
|
||||||
|
final channels = snapshot.data;
|
||||||
|
if (channels.isEmpty) {
|
||||||
|
return widget.emptyBuilder(context);
|
||||||
|
}
|
||||||
|
return widget.listBuilder(context, channels);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildLoadingWidget() {
|
|
||||||
return widget.loadingBuilder(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildErrorWidget(
|
Widget _buildErrorWidget(
|
||||||
AsyncSnapshot<List<Channel>> snapshot,
|
AsyncSnapshot<List<Channel>> snapshot,
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
@@ -173,10 +159,9 @@ class _ChannelListCoreState extends State<ChannelListCore>
|
|||||||
return widget.errorBuilder(context, snapshot.error);
|
return widget.errorBuilder(context, snapshot.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadData() {
|
Future<void> loadData() {
|
||||||
final channelsBloc = ChannelsBloc.of(context);
|
final channelsBloc = ChannelsBloc.of(context);
|
||||||
|
return channelsBloc.queryChannels(
|
||||||
channelsBloc.queryChannels(
|
|
||||||
filter: widget.filter,
|
filter: widget.filter,
|
||||||
sortOptions: widget.sort,
|
sortOptions: widget.sort,
|
||||||
paginationParams: widget.pagination,
|
paginationParams: widget.pagination,
|
||||||
@@ -184,10 +169,9 @@ class _ChannelListCoreState extends State<ChannelListCore>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void paginateData() {
|
Future<void> paginateData() {
|
||||||
final channelsBloc = ChannelsBloc.of(context);
|
final channelsBloc = ChannelsBloc.of(context);
|
||||||
|
return channelsBloc.queryChannels(
|
||||||
channelsBloc.queryChannels(
|
|
||||||
filter: widget.filter,
|
filter: widget.filter,
|
||||||
sortOptions: widget.sort,
|
sortOptions: widget.sort,
|
||||||
paginationParams: widget.pagination.copyWith(
|
paginationParams: widget.pagination.copyWith(
|
||||||
@@ -197,39 +181,21 @@ class _ChannelListCoreState extends State<ChannelListCore>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamSubscription _subscription;
|
StreamSubscription<Event> _subscription;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
loadData();
|
||||||
WidgetsBinding.instance.addObserver(this);
|
|
||||||
|
|
||||||
final channelsBloc = ChannelsBloc.of(context);
|
|
||||||
channelsBloc.queryChannels(
|
|
||||||
filter: widget.filter,
|
|
||||||
sortOptions: widget.sort,
|
|
||||||
paginationParams: widget.pagination,
|
|
||||||
options: widget.options,
|
|
||||||
);
|
|
||||||
|
|
||||||
final client = StreamChatCore.of(context).client;
|
final client = StreamChatCore.of(context).client;
|
||||||
|
|
||||||
_subscription = client
|
_subscription = client
|
||||||
.on(
|
.on(
|
||||||
EventType.connectionRecovered,
|
EventType.connectionRecovered,
|
||||||
EventType.notificationAddedToChannel,
|
EventType.notificationAddedToChannel,
|
||||||
EventType.notificationMessageNew,
|
EventType.notificationMessageNew,
|
||||||
EventType.channelVisible,
|
EventType.channelVisible,
|
||||||
)
|
)
|
||||||
.listen((event) {
|
.listen((event) => loadData());
|
||||||
channelsBloc.queryChannels(
|
|
||||||
filter: widget.filter,
|
|
||||||
sortOptions: widget.sort,
|
|
||||||
paginationParams: widget.pagination,
|
|
||||||
options: widget.options,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (widget.channelListController != null) {
|
if (widget.channelListController != null) {
|
||||||
widget.channelListController.loadData = loadData;
|
widget.channelListController.loadData = loadData;
|
||||||
@@ -246,20 +212,13 @@ class _ChannelListCoreState extends State<ChannelListCore>
|
|||||||
widget.pagination?.toJson()?.toString() !=
|
widget.pagination?.toJson()?.toString() !=
|
||||||
oldWidget.pagination?.toJson()?.toString() ||
|
oldWidget.pagination?.toJson()?.toString() ||
|
||||||
widget.options?.toString() != oldWidget.options?.toString()) {
|
widget.options?.toString() != oldWidget.options?.toString()) {
|
||||||
final channelsBloc = ChannelsBloc.of(context);
|
loadData();
|
||||||
channelsBloc.queryChannels(
|
|
||||||
filter: widget.filter,
|
|
||||||
sortOptions: widget.sort,
|
|
||||||
paginationParams: widget.pagination,
|
|
||||||
options: widget.options,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_subscription.cancel();
|
_subscription.cancel();
|
||||||
WidgetsBinding.instance.removeObserver(this);
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -268,10 +227,10 @@ class _ChannelListCoreState extends State<ChannelListCore>
|
|||||||
class ChannelListController {
|
class ChannelListController {
|
||||||
/// This function calls Stream's servers to load a list of channels. If there is existing data,
|
/// This function calls Stream's servers to load a list of channels. If there is existing data,
|
||||||
/// calling this function causes a reload.
|
/// calling this function causes a reload.
|
||||||
VoidCallback loadData;
|
AsyncCallback loadData;
|
||||||
|
|
||||||
/// This function is used to load another page of data. Note, [loadData] should be
|
/// This function is used to load another page of data. Note, [loadData] should be
|
||||||
/// used to populate the initial page of data. Calling [paginateData] performs a query
|
/// used to populate the initial page of data. Calling [paginateData] performs a query
|
||||||
/// to load subsequent pages.
|
/// to load subsequent pages.
|
||||||
VoidCallback paginateData;
|
AsyncCallback paginateData;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ class ChannelsBlocState extends State<ChannelsBloc>
|
|||||||
/// Calls [client.queryChannels] updating [queryChannelsLoading] stream
|
/// Calls [client.queryChannels] updating [queryChannelsLoading] stream
|
||||||
Future<void> queryChannels({
|
Future<void> queryChannels({
|
||||||
Map<String, dynamic> filter,
|
Map<String, dynamic> filter,
|
||||||
List<SortOption> sortOptions,
|
List<SortOption<ChannelModel>> sortOptions,
|
||||||
PaginationParams paginationParams,
|
PaginationParams paginationParams,
|
||||||
Map<String, dynamic> options,
|
Map<String, dynamic> options,
|
||||||
bool onlyOffline = false,
|
bool onlyOffline = false,
|
||||||
@@ -102,21 +102,21 @@ class ChannelsBlocState extends State<ChannelsBloc>
|
|||||||
paginationParams.offset == null ||
|
paginationParams.offset == null ||
|
||||||
paginationParams.offset == 0;
|
paginationParams.offset == 0;
|
||||||
final oldChannels = List<Channel>.from(channels ?? []);
|
final oldChannels = List<Channel>.from(channels ?? []);
|
||||||
final _channels = await client.queryChannels(
|
await for (final channels in client.queryChannels(
|
||||||
filter: filter,
|
filter: filter,
|
||||||
sort: sortOptions,
|
sort: sortOptions,
|
||||||
options: options,
|
options: options,
|
||||||
paginationParams: paginationParams,
|
paginationParams: paginationParams,
|
||||||
onlyOffline: onlyOffline,
|
preferOffline: onlyOffline,
|
||||||
);
|
)) {
|
||||||
|
if (clear) {
|
||||||
if (clear) {
|
_channelsController.add(channels);
|
||||||
_channelsController.add(_channels);
|
} else {
|
||||||
} else {
|
final l = oldChannels + channels;
|
||||||
final l = oldChannels + _channels;
|
_channelsController.add(l);
|
||||||
_channelsController.add(l);
|
}
|
||||||
|
_queryChannelsLoadingController.sink.add(false);
|
||||||
}
|
}
|
||||||
_queryChannelsLoadingController.sink.add(false);
|
|
||||||
} catch (err, stackTrace) {
|
} catch (err, stackTrace) {
|
||||||
print(err);
|
print(err);
|
||||||
print(stackTrace);
|
print(stackTrace);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
name: stream_chat_flutter_core
|
name: stream_chat_flutter_core
|
||||||
homepage: https://github.com/GetStream/stream-chat-flutter
|
homepage: https://github.com/GetStream/stream-chat-flutter
|
||||||
description: Stream Chat official Flutter SDK Core. Build your own chat experience using Dart and Flutter.
|
description: Stream Chat official Flutter SDK Core. Build your own chat experience using Dart and Flutter.
|
||||||
version: 1.2.0-beta
|
version: 1.3.0-beta
|
||||||
repository: https://github.com/GetStream/stream-chat-flutter
|
repository: https://github.com/GetStream/stream-chat-flutter
|
||||||
issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues
|
issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@ environment:
|
|||||||
flutter: ">=1.17.0"
|
flutter: ">=1.17.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
stream_chat: ^1.2.0-beta
|
stream_chat: ^1.3.0-beta
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
rxdart: ^0.25.0
|
rxdart: ^0.25.0
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
## 1.3.0-beta
|
||||||
|
|
||||||
|
* Update llc dependency
|
||||||
|
|
||||||
## 1.2.0-beta
|
## 1.2.0-beta
|
||||||
|
|
||||||
* Update llc dependency
|
* Update llc dependency
|
||||||
|
|||||||
@@ -0,0 +1,563 @@
|
|||||||
|
// !$*UTF8*$!
|
||||||
|
{
|
||||||
|
archiveVersion = 1;
|
||||||
|
classes = {
|
||||||
|
};
|
||||||
|
objectVersion = 46;
|
||||||
|
objects = {
|
||||||
|
|
||||||
|
/* Begin PBXBuildFile section */
|
||||||
|
0E9B23A7BA08E142FA5000CF /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D76F8024ABE1070895D659BA /* Pods_Runner.framework */; };
|
||||||
|
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
||||||
|
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||||
|
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
||||||
|
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||||
|
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||||
|
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||||
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
|
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = "";
|
||||||
|
dstSubfolderSpec = 10;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
name = "Embed Frameworks";
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXFileReference section */
|
||||||
|
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
||||||
|
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||||
|
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||||
|
3F6A054EEDAF06BCF649C130 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
6FE1ECA061EBB001F6BCE8B7 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
|
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
|
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||||
|
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
||||||
|
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
||||||
|
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||||
|
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
|
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
|
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
D76F8024ABE1070895D659BA /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
EC2A45E9198C1011BED23834 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
97C146EB1CF9000F007C117D /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
0E9B23A7BA08E142FA5000CF /* Pods_Runner.framework in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXGroup section */
|
||||||
|
04AAB960E493BD92262BBF82 /* Frameworks */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
D76F8024ABE1070895D659BA /* Pods_Runner.framework */,
|
||||||
|
);
|
||||||
|
name = Frameworks;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
8559384DCD98ED6067CEF8CB /* Pods */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
3F6A054EEDAF06BCF649C130 /* Pods-Runner.debug.xcconfig */,
|
||||||
|
EC2A45E9198C1011BED23834 /* Pods-Runner.release.xcconfig */,
|
||||||
|
6FE1ECA061EBB001F6BCE8B7 /* Pods-Runner.profile.xcconfig */,
|
||||||
|
);
|
||||||
|
name = Pods;
|
||||||
|
path = Pods;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
9740EEB11CF90186004384FC /* Flutter */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
|
||||||
|
9740EEB21CF90195004384FC /* Debug.xcconfig */,
|
||||||
|
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
|
||||||
|
9740EEB31CF90195004384FC /* Generated.xcconfig */,
|
||||||
|
);
|
||||||
|
name = Flutter;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
97C146E51CF9000F007C117D = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
9740EEB11CF90186004384FC /* Flutter */,
|
||||||
|
97C146F01CF9000F007C117D /* Runner */,
|
||||||
|
97C146EF1CF9000F007C117D /* Products */,
|
||||||
|
8559384DCD98ED6067CEF8CB /* Pods */,
|
||||||
|
04AAB960E493BD92262BBF82 /* Frameworks */,
|
||||||
|
);
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
97C146EF1CF9000F007C117D /* Products */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
97C146EE1CF9000F007C117D /* Runner.app */,
|
||||||
|
);
|
||||||
|
name = Products;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
97C146F01CF9000F007C117D /* Runner */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
97C146FA1CF9000F007C117D /* Main.storyboard */,
|
||||||
|
97C146FD1CF9000F007C117D /* Assets.xcassets */,
|
||||||
|
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
|
||||||
|
97C147021CF9000F007C117D /* Info.plist */,
|
||||||
|
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
|
||||||
|
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
|
||||||
|
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
|
||||||
|
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
|
||||||
|
);
|
||||||
|
path = Runner;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
/* End PBXGroup section */
|
||||||
|
|
||||||
|
/* Begin PBXNativeTarget section */
|
||||||
|
97C146ED1CF9000F007C117D /* Runner */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||||
|
buildPhases = (
|
||||||
|
C1EE41B94EADE099F7AF3A1C /* [CP] Check Pods Manifest.lock */,
|
||||||
|
9740EEB61CF901F6004384FC /* Run Script */,
|
||||||
|
97C146EA1CF9000F007C117D /* Sources */,
|
||||||
|
97C146EB1CF9000F007C117D /* Frameworks */,
|
||||||
|
97C146EC1CF9000F007C117D /* Resources */,
|
||||||
|
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||||
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||||
|
BD38DACC9A0AD429D9ED9939 /* [CP] Embed Pods Frameworks */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = Runner;
|
||||||
|
productName = Runner;
|
||||||
|
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
|
||||||
|
productType = "com.apple.product-type.application";
|
||||||
|
};
|
||||||
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
|
/* Begin PBXProject section */
|
||||||
|
97C146E61CF9000F007C117D /* Project object */ = {
|
||||||
|
isa = PBXProject;
|
||||||
|
attributes = {
|
||||||
|
LastUpgradeCheck = 1020;
|
||||||
|
ORGANIZATIONNAME = "";
|
||||||
|
TargetAttributes = {
|
||||||
|
97C146ED1CF9000F007C117D = {
|
||||||
|
CreatedOnToolsVersion = 7.3.1;
|
||||||
|
LastSwiftMigration = 1100;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
|
||||||
|
compatibilityVersion = "Xcode 9.3";
|
||||||
|
developmentRegion = en;
|
||||||
|
hasScannedForEncodings = 0;
|
||||||
|
knownRegions = (
|
||||||
|
en,
|
||||||
|
Base,
|
||||||
|
);
|
||||||
|
mainGroup = 97C146E51CF9000F007C117D;
|
||||||
|
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
|
||||||
|
projectDirPath = "";
|
||||||
|
projectRoot = "";
|
||||||
|
targets = (
|
||||||
|
97C146ED1CF9000F007C117D /* Runner */,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
/* End PBXProject section */
|
||||||
|
|
||||||
|
/* Begin PBXResourcesBuildPhase section */
|
||||||
|
97C146EC1CF9000F007C117D /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
|
||||||
|
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
|
||||||
|
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
|
||||||
|
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
|
name = "Thin Binary";
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
|
||||||
|
};
|
||||||
|
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
|
name = "Run Script";
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
||||||
|
};
|
||||||
|
BD38DACC9A0AD429D9ED9939 /* [CP] Embed Pods Frameworks */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
|
);
|
||||||
|
name = "[CP] Embed Pods Frameworks";
|
||||||
|
outputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
|
C1EE41B94EADE099F7AF3A1C /* [CP] Check Pods Manifest.lock */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
|
);
|
||||||
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
97C146EA1CF9000F007C117D /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
|
||||||
|
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXVariantGroup section */
|
||||||
|
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
|
||||||
|
isa = PBXVariantGroup;
|
||||||
|
children = (
|
||||||
|
97C146FB1CF9000F007C117D /* Base */,
|
||||||
|
);
|
||||||
|
name = Main.storyboard;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
|
||||||
|
isa = PBXVariantGroup;
|
||||||
|
children = (
|
||||||
|
97C147001CF9000F007C117D /* Base */,
|
||||||
|
);
|
||||||
|
name = LaunchScreen.storyboard;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
/* End PBXVariantGroup section */
|
||||||
|
|
||||||
|
/* Begin XCBuildConfiguration section */
|
||||||
|
249021D3217E4FDB00AE95B9 /* Profile */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SUPPORTED_PLATFORMS = iphoneos;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
VALIDATE_PRODUCT = YES;
|
||||||
|
};
|
||||||
|
name = Profile;
|
||||||
|
};
|
||||||
|
249021D4217E4FDB00AE95B9 /* Profile */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
|
ENABLE_BITCODE = NO;
|
||||||
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(PROJECT_DIR)/Flutter",
|
||||||
|
);
|
||||||
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||||
|
LIBRARY_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(PROJECT_DIR)/Flutter",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
|
};
|
||||||
|
name = Profile;
|
||||||
|
};
|
||||||
|
97C147031CF9000F007C117D /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_TESTABILITY = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
|
"DEBUG=1",
|
||||||
|
"$(inherited)",
|
||||||
|
);
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
97C147041CF9000F007C117D /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SUPPORTED_PLATFORMS = iphoneos;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
VALIDATE_PRODUCT = YES;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
97C147061CF9000F007C117D /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
|
ENABLE_BITCODE = NO;
|
||||||
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(PROJECT_DIR)/Flutter",
|
||||||
|
);
|
||||||
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||||
|
LIBRARY_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(PROJECT_DIR)/Flutter",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
97C147071CF9000F007C117D /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
|
ENABLE_BITCODE = NO;
|
||||||
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(PROJECT_DIR)/Flutter",
|
||||||
|
);
|
||||||
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||||
|
LIBRARY_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(PROJECT_DIR)/Flutter",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
|
/* Begin XCConfigurationList section */
|
||||||
|
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
97C147031CF9000F007C117D /* Debug */,
|
||||||
|
97C147041CF9000F007C117D /* Release */,
|
||||||
|
249021D3217E4FDB00AE95B9 /* Profile */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
97C147061CF9000F007C117D /* Debug */,
|
||||||
|
97C147071CF9000F007C117D /* Release */,
|
||||||
|
249021D4217E4FDB00AE95B9 /* Profile */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
/* End XCConfigurationList section */
|
||||||
|
};
|
||||||
|
rootObject = 97C146E61CF9000F007C117D /* Project object */;
|
||||||
|
}
|
||||||
+3
@@ -4,4 +4,7 @@
|
|||||||
<FileRef
|
<FileRef
|
||||||
location = "group:Runner.xcodeproj">
|
location = "group:Runner.xcodeproj">
|
||||||
</FileRef>
|
</FileRef>
|
||||||
|
<FileRef
|
||||||
|
location = "group:Pods/Pods.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
</Workspace>
|
</Workspace>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ dependencies:
|
|||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
cupertino_icons: ^1.0.0
|
cupertino_icons: ^1.0.0
|
||||||
stream_chat: ^1.1.0-beta
|
stream_chat: ^1.3.0
|
||||||
stream_chat_persistence:
|
stream_chat_persistence:
|
||||||
path: ../
|
path: ../
|
||||||
|
|
||||||
|
|||||||
@@ -15,9 +15,7 @@ part 'channel_query_dao.g.dart';
|
|||||||
class ChannelQueryDao extends DatabaseAccessor<MoorChatDatabase>
|
class ChannelQueryDao extends DatabaseAccessor<MoorChatDatabase>
|
||||||
with _$ChannelQueryDaoMixin {
|
with _$ChannelQueryDaoMixin {
|
||||||
/// Creates a new channel query dao instance
|
/// Creates a new channel query dao instance
|
||||||
ChannelQueryDao(this._db) : super(_db);
|
ChannelQueryDao(MoorChatDatabase db) : super(db);
|
||||||
|
|
||||||
final MoorChatDatabase _db;
|
|
||||||
|
|
||||||
String _computeHash(Map<String, dynamic> filter) {
|
String _computeHash(Map<String, dynamic> filter) {
|
||||||
if (filter == null) {
|
if (filter == null) {
|
||||||
@@ -37,19 +35,19 @@ class ChannelQueryDao extends DatabaseAccessor<MoorChatDatabase>
|
|||||||
) async {
|
) async {
|
||||||
final hash = _computeHash(filter);
|
final hash = _computeHash(filter);
|
||||||
if (clearQueryCache) {
|
if (clearQueryCache) {
|
||||||
await (delete(channelQueries)
|
await batch((it) {
|
||||||
..where((query) => query.queryHash.equals(hash)))
|
it.deleteWhere<ChannelQueries, ChannelQueryEntity>(
|
||||||
.go();
|
channelQueries,
|
||||||
|
(c) => c.queryHash.equals(hash),
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return batch((batch) {
|
return batch((it) {
|
||||||
batch.insertAll(
|
it.insertAll(
|
||||||
channelQueries,
|
channelQueries,
|
||||||
cids.map((cid) {
|
cids.map((cid) {
|
||||||
return ChannelQueryEntity(
|
return ChannelQueryEntity(queryHash: hash, channelCid: cid);
|
||||||
queryHash: hash,
|
|
||||||
channelCid: cid,
|
|
||||||
);
|
|
||||||
}).toList(),
|
}).toList(),
|
||||||
mode: InsertMode.insertOrReplace,
|
mode: InsertMode.insertOrReplace,
|
||||||
);
|
);
|
||||||
@@ -57,68 +55,74 @@ class ChannelQueryDao extends DatabaseAccessor<MoorChatDatabase>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get list of channels by filter, sort and paginationParams
|
/// Get list of channels by filter, sort and paginationParams
|
||||||
Future<List<ChannelState>> getChannelStates({
|
Future<List<ChannelModel>> getChannels({
|
||||||
Map<String, dynamic> filter,
|
Map<String, dynamic> filter,
|
||||||
List<SortOption> sort = const [],
|
List<SortOption<ChannelModel>> sort = const [],
|
||||||
PaginationParams paginationParams,
|
PaginationParams paginationParams,
|
||||||
}) async {
|
}) async {
|
||||||
|
assert(() {
|
||||||
|
if (sort != null && sort.any((it) => it.comparator == null)) {
|
||||||
|
throw ArgumentError(
|
||||||
|
'SortOption requires a comparator in order to sort',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}());
|
||||||
|
|
||||||
final hash = _computeHash(filter);
|
final hash = _computeHash(filter);
|
||||||
final cachedChannels = await Future.wait(await (select(channelQueries)
|
final cachedChannelCids = await (select(channelQueries)
|
||||||
..where((c) => c.queryHash.equals(hash)))
|
..where((c) => c.queryHash.equals(hash)))
|
||||||
.get()
|
.map((c) => c.channelCid)
|
||||||
.then((channelQueries) {
|
.get();
|
||||||
final cids = channelQueries.map((c) => c.channelCid).toList();
|
|
||||||
final query = select(channels)..where((c) => c.cid.isIn(cids));
|
|
||||||
|
|
||||||
sort = sort
|
final query = select(channels)..where((c) => c.cid.isIn(cachedChannelCids));
|
||||||
?.where((s) => ChannelModel.topLevelFields.contains(s.field))
|
|
||||||
?.toList();
|
|
||||||
|
|
||||||
if (sort != null && sort.isNotEmpty) {
|
final cachedChannels = await (query.join([
|
||||||
query.orderBy(sort.map((s) {
|
leftOuterJoin(users, channels.createdById.equalsExp(users.id)),
|
||||||
final orderExpression = CustomExpression('channels.${s.field}');
|
]).map((row) {
|
||||||
return (c) => OrderingTerm(
|
final createdByEntity = row.readTable(users);
|
||||||
expression: orderExpression,
|
final channelEntity = row.readTable(channels);
|
||||||
mode: s.direction == 1 ? OrderingMode.asc : OrderingMode.desc,
|
return channelEntity.toChannelModel(createdBy: createdByEntity?.toUser());
|
||||||
);
|
})).get();
|
||||||
}).toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (paginationParams != null) {
|
final possibleSortingFields = cachedChannels.fold<List<String>>(
|
||||||
query.limit(
|
ChannelModel.topLevelFields, (previousValue, element) {
|
||||||
paginationParams.limit ?? 10,
|
return {...previousValue, ...element.extraData.keys}.toList();
|
||||||
offset: paginationParams.offset,
|
});
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return query.join([
|
sort = sort
|
||||||
leftOuterJoin(users, channels.createdById.equalsExp(users.id)),
|
?.where((s) => possibleSortingFields.contains(s.field))
|
||||||
]).map((row) async {
|
?.toList(growable: false);
|
||||||
final userEntity = row.readTable(users);
|
|
||||||
final channelEntity = row.readTable(channels);
|
|
||||||
|
|
||||||
final cid = channelEntity.cid;
|
Comparator<ChannelModel> chainedComparator = (a, b) {
|
||||||
final members = await _db.memberDao.getMembersByCid(cid);
|
final dateA = a.lastMessageAt ?? a.createdAt;
|
||||||
final reads = await _db.readDao.getReadsByCid(cid);
|
final dateB = b.lastMessageAt ?? b.createdAt;
|
||||||
final messages = await _db.messageDao.getMessagesByCid(cid);
|
return dateB.compareTo(dateA);
|
||||||
|
};
|
||||||
|
|
||||||
return channelEntity.toChannelState(
|
if (sort != null && sort.isNotEmpty) {
|
||||||
createdBy: userEntity?.toUser(),
|
chainedComparator = (a, b) {
|
||||||
members: members,
|
int result;
|
||||||
reads: reads,
|
for (final comparator in sort.map((it) => it.comparator)) {
|
||||||
messages: messages,
|
try {
|
||||||
);
|
result = comparator(a, b);
|
||||||
}).get();
|
} catch (e) {
|
||||||
}));
|
result = 0;
|
||||||
|
}
|
||||||
|
if (result != 0) return result;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (sort?.isEmpty != false && cachedChannels?.isNotEmpty == true) {
|
cachedChannels.sort(chainedComparator);
|
||||||
cachedChannels
|
|
||||||
.sort((a, b) => b.channel.updatedAt.compareTo(a.channel.updatedAt));
|
if (paginationParams?.offset != null) {
|
||||||
cachedChannels.sort((a, b) {
|
cachedChannels.removeRange(0, paginationParams.offset);
|
||||||
final dateA = a.channel.lastMessageAt ?? a.channel.createdAt;
|
}
|
||||||
final dateB = b.channel.lastMessageAt ?? b.channel.createdAt;
|
|
||||||
return dateB.compareTo(dateA);
|
if (paginationParams?.limit != null) {
|
||||||
});
|
return cachedChannels.take(paginationParams.limit).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
return cachedChannels;
|
return cachedChannels;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
export 'user_dao.dart';
|
export 'user_dao.dart';
|
||||||
export 'channel_dao.dart';
|
export 'channel_dao.dart';
|
||||||
export 'message_dao.dart';
|
export 'message_dao.dart';
|
||||||
|
export 'pinned_message_dao.dart';
|
||||||
export 'member_dao.dart';
|
export 'member_dao.dart';
|
||||||
export 'connection_event_dao.dart';
|
export 'connection_event_dao.dart';
|
||||||
export 'reaction_dao.dart';
|
export 'reaction_dao.dart';
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ class MessageDao extends DatabaseAccessor<MoorChatDatabase>
|
|||||||
|
|
||||||
final MoorChatDatabase _db;
|
final MoorChatDatabase _db;
|
||||||
|
|
||||||
|
$UsersTable get _users => alias(users, 'users');
|
||||||
|
|
||||||
|
$UsersTable get _pinnedByUsers => alias(users, 'pinnedByUsers');
|
||||||
|
|
||||||
/// Removes all the messages by matching [Messages.id] in [messageIds]
|
/// Removes all the messages by matching [Messages.id] in [messageIds]
|
||||||
///
|
///
|
||||||
/// This will automatically delete the following linked records
|
/// This will automatically delete the following linked records
|
||||||
@@ -34,7 +38,8 @@ class MessageDao extends DatabaseAccessor<MoorChatDatabase>
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<Message> _messageFromJoinRow(TypedResult rows) async {
|
Future<Message> _messageFromJoinRow(TypedResult rows) async {
|
||||||
final userEntity = rows.readTable(users);
|
final userEntity = rows.readTable(_users);
|
||||||
|
final pinnedByEntity = rows.readTable(_pinnedByUsers);
|
||||||
final msgEntity = rows.readTable(messages);
|
final msgEntity = rows.readTable(messages);
|
||||||
final latestReactions = await _db.reactionDao.getReactions(msgEntity.id);
|
final latestReactions = await _db.reactionDao.getReactions(msgEntity.id);
|
||||||
final ownReactions = await _db.reactionDao.getReactionsByUserId(
|
final ownReactions = await _db.reactionDao.getReactionsByUserId(
|
||||||
@@ -47,6 +52,7 @@ class MessageDao extends DatabaseAccessor<MoorChatDatabase>
|
|||||||
}
|
}
|
||||||
return msgEntity.toMessage(
|
return msgEntity.toMessage(
|
||||||
user: userEntity?.toUser(),
|
user: userEntity?.toUser(),
|
||||||
|
pinnedBy: pinnedByEntity?.toUser(),
|
||||||
latestReactions: latestReactions,
|
latestReactions: latestReactions,
|
||||||
ownReactions: ownReactions,
|
ownReactions: ownReactions,
|
||||||
quotedMessage: quotedMessage,
|
quotedMessage: quotedMessage,
|
||||||
@@ -56,7 +62,9 @@ class MessageDao extends DatabaseAccessor<MoorChatDatabase>
|
|||||||
/// Returns a single message by matching the [Messages.id] with [id]
|
/// Returns a single message by matching the [Messages.id] with [id]
|
||||||
Future<Message> getMessageById(String id) async {
|
Future<Message> getMessageById(String id) async {
|
||||||
return await (select(messages).join([
|
return await (select(messages).join([
|
||||||
leftOuterJoin(users, messages.userId.equalsExp(users.id)),
|
leftOuterJoin(_users, messages.userId.equalsExp(_users.id)),
|
||||||
|
leftOuterJoin(
|
||||||
|
_pinnedByUsers, messages.pinnedByUserId.equalsExp(_pinnedByUsers.id)),
|
||||||
])
|
])
|
||||||
..where(messages.id.equals(id)))
|
..where(messages.id.equals(id)))
|
||||||
.map(_messageFromJoinRow)
|
.map(_messageFromJoinRow)
|
||||||
@@ -67,7 +75,9 @@ class MessageDao extends DatabaseAccessor<MoorChatDatabase>
|
|||||||
/// [Messages.channelCid] with [cid]
|
/// [Messages.channelCid] with [cid]
|
||||||
Future<List<Message>> getThreadMessages(String cid) async {
|
Future<List<Message>> getThreadMessages(String cid) async {
|
||||||
return Future.wait(await (select(messages).join([
|
return Future.wait(await (select(messages).join([
|
||||||
leftOuterJoin(users, messages.userId.equalsExp(users.id)),
|
leftOuterJoin(users, messages.userId.equalsExp(_users.id)),
|
||||||
|
leftOuterJoin(
|
||||||
|
_pinnedByUsers, messages.pinnedByUserId.equalsExp(_pinnedByUsers.id)),
|
||||||
])
|
])
|
||||||
..where(messages.channelCid.equals(cid))
|
..where(messages.channelCid.equals(cid))
|
||||||
..where(isNotNull(messages.parentId))
|
..where(isNotNull(messages.parentId))
|
||||||
@@ -83,7 +93,9 @@ class MessageDao extends DatabaseAccessor<MoorChatDatabase>
|
|||||||
PaginationParams options,
|
PaginationParams options,
|
||||||
}) async {
|
}) async {
|
||||||
final msgList = await Future.wait(await (select(messages).join([
|
final msgList = await Future.wait(await (select(messages).join([
|
||||||
innerJoin(users, messages.userId.equalsExp(users.id)),
|
innerJoin(_users, messages.userId.equalsExp(_users.id)),
|
||||||
|
innerJoin(
|
||||||
|
_pinnedByUsers, messages.pinnedByUserId.equalsExp(_pinnedByUsers.id)),
|
||||||
])
|
])
|
||||||
..where(messages.parentId.equals(parentId))
|
..where(messages.parentId.equals(parentId))
|
||||||
..orderBy([OrderingTerm.asc(messages.createdAt)]))
|
..orderBy([OrderingTerm.asc(messages.createdAt)]))
|
||||||
@@ -104,7 +116,9 @@ class MessageDao extends DatabaseAccessor<MoorChatDatabase>
|
|||||||
PaginationParams messagePagination,
|
PaginationParams messagePagination,
|
||||||
}) async {
|
}) async {
|
||||||
final msgList = await Future.wait(await (select(messages).join([
|
final msgList = await Future.wait(await (select(messages).join([
|
||||||
leftOuterJoin(users, messages.userId.equalsExp(users.id)),
|
leftOuterJoin(_users, messages.userId.equalsExp(_users.id)),
|
||||||
|
leftOuterJoin(
|
||||||
|
_pinnedByUsers, messages.pinnedByUserId.equalsExp(_pinnedByUsers.id)),
|
||||||
])
|
])
|
||||||
..where(messages.channelCid.equals(cid))
|
..where(messages.channelCid.equals(cid))
|
||||||
..where(
|
..where(
|
||||||
|
|||||||
@@ -0,0 +1,169 @@
|
|||||||
|
import 'package:moor/moor.dart';
|
||||||
|
import 'package:stream_chat/stream_chat.dart';
|
||||||
|
import 'package:stream_chat_persistence/src/db/moor_chat_database.dart';
|
||||||
|
import 'package:stream_chat_persistence/src/entity/pinned_messages.dart';
|
||||||
|
import 'package:stream_chat_persistence/src/entity/users.dart';
|
||||||
|
|
||||||
|
import '../mapper/mapper.dart';
|
||||||
|
|
||||||
|
part 'pinned_message_dao.g.dart';
|
||||||
|
|
||||||
|
/// The Data Access Object for operations in [Messages] table.
|
||||||
|
@UseDao(tables: [PinnedMessages, Users])
|
||||||
|
class PinnedMessageDao extends DatabaseAccessor<MoorChatDatabase>
|
||||||
|
with _$PinnedMessageDaoMixin {
|
||||||
|
/// Creates a new message dao instance
|
||||||
|
PinnedMessageDao(this._db) : super(_db);
|
||||||
|
|
||||||
|
final MoorChatDatabase _db;
|
||||||
|
|
||||||
|
$UsersTable get _users => alias(users, 'users');
|
||||||
|
|
||||||
|
$UsersTable get _pinnedByUsers => alias(users, 'pinnedByUsers');
|
||||||
|
|
||||||
|
/// Removes all the messages by matching [PinnedMessages.id] in [messageIds]
|
||||||
|
///
|
||||||
|
/// This will automatically delete the following linked records
|
||||||
|
/// 1. Message Reactions
|
||||||
|
Future<void> deleteMessageByIds(List<String> messageIds) {
|
||||||
|
return (delete(pinnedMessages)..where((tbl) => tbl.id.isIn(messageIds)))
|
||||||
|
.go();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes all the messages by matching [PinnedMessages.channelCid] in [cids]
|
||||||
|
///
|
||||||
|
/// This will automatically delete the following linked records
|
||||||
|
/// 1. Message Reactions
|
||||||
|
Future<void> deleteMessageByCids(List<String> cids) async {
|
||||||
|
return (delete(pinnedMessages)..where((tbl) => tbl.channelCid.isIn(cids)))
|
||||||
|
.go();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Message> _messageFromJoinRow(TypedResult rows) async {
|
||||||
|
final userEntity = rows.readTable(users);
|
||||||
|
final pinnedByEntity = rows.readTable(_pinnedByUsers);
|
||||||
|
final msgEntity = rows.readTable(pinnedMessages);
|
||||||
|
final latestReactions = await _db.reactionDao.getReactions(msgEntity.id);
|
||||||
|
final ownReactions = await _db.reactionDao.getReactionsByUserId(
|
||||||
|
msgEntity.id,
|
||||||
|
_db.userId,
|
||||||
|
);
|
||||||
|
Message quotedMessage;
|
||||||
|
if (msgEntity.quotedMessageId != null) {
|
||||||
|
quotedMessage = await getMessageById(msgEntity.quotedMessageId);
|
||||||
|
}
|
||||||
|
return msgEntity.toMessage(
|
||||||
|
user: userEntity?.toUser(),
|
||||||
|
pinnedBy: pinnedByEntity?.toUser(),
|
||||||
|
latestReactions: latestReactions,
|
||||||
|
ownReactions: ownReactions,
|
||||||
|
quotedMessage: quotedMessage,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a single message by matching the [PinnedMessages.id] with [id]
|
||||||
|
Future<Message> getMessageById(String id) async {
|
||||||
|
return await (select(pinnedMessages).join([
|
||||||
|
leftOuterJoin(_users, pinnedMessages.userId.equalsExp(_users.id)),
|
||||||
|
leftOuterJoin(_pinnedByUsers,
|
||||||
|
pinnedMessages.pinnedByUserId.equalsExp(_pinnedByUsers.id)),
|
||||||
|
])
|
||||||
|
..where(pinnedMessages.id.equals(id)))
|
||||||
|
.map(_messageFromJoinRow)
|
||||||
|
.getSingle();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns all the messages of a particular thread by matching
|
||||||
|
/// [PinnedMessages.channelCid] with [cid]
|
||||||
|
Future<List<Message>> getThreadMessages(String cid) async {
|
||||||
|
return Future.wait(await (select(pinnedMessages).join([
|
||||||
|
leftOuterJoin(_users, pinnedMessages.userId.equalsExp(_users.id)),
|
||||||
|
leftOuterJoin(_pinnedByUsers,
|
||||||
|
pinnedMessages.pinnedByUserId.equalsExp(_pinnedByUsers.id)),
|
||||||
|
])
|
||||||
|
..where(pinnedMessages.channelCid.equals(cid))
|
||||||
|
..where(isNotNull(pinnedMessages.parentId))
|
||||||
|
..orderBy([OrderingTerm.asc(pinnedMessages.createdAt)]))
|
||||||
|
.map(_messageFromJoinRow)
|
||||||
|
.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns all the messages of a particular thread by matching
|
||||||
|
/// [PinnedMessages.parentId] with [parentId]
|
||||||
|
Future<List<Message>> getThreadMessagesByParentId(
|
||||||
|
String parentId, {
|
||||||
|
PaginationParams options,
|
||||||
|
}) async {
|
||||||
|
final msgList = await Future.wait(await (select(pinnedMessages).join([
|
||||||
|
innerJoin(_users, pinnedMessages.userId.equalsExp(_users.id)),
|
||||||
|
innerJoin(_pinnedByUsers,
|
||||||
|
pinnedMessages.pinnedByUserId.equalsExp(_pinnedByUsers.id)),
|
||||||
|
])
|
||||||
|
..where(pinnedMessages.parentId.equals(parentId))
|
||||||
|
..orderBy([OrderingTerm.asc(pinnedMessages.createdAt)]))
|
||||||
|
.map(_messageFromJoinRow)
|
||||||
|
.get());
|
||||||
|
|
||||||
|
if (options?.lessThan != null) {
|
||||||
|
final lessThanIndex = msgList.indexWhere((m) => m.id == options.lessThan);
|
||||||
|
msgList.removeRange(lessThanIndex, msgList.length);
|
||||||
|
}
|
||||||
|
return msgList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns all the messages of a channel by matching
|
||||||
|
/// [PinnedMessages.channelCid] with [parentId]
|
||||||
|
Future<List<Message>> getMessagesByCid(
|
||||||
|
String cid, {
|
||||||
|
PaginationParams messagePagination,
|
||||||
|
}) async {
|
||||||
|
final msgList = await Future.wait(await (select(pinnedMessages).join([
|
||||||
|
leftOuterJoin(_users, pinnedMessages.userId.equalsExp(_users.id)),
|
||||||
|
leftOuterJoin(_pinnedByUsers,
|
||||||
|
pinnedMessages.pinnedByUserId.equalsExp(_pinnedByUsers.id)),
|
||||||
|
])
|
||||||
|
..where(pinnedMessages.channelCid.equals(cid))
|
||||||
|
..where(isNull(pinnedMessages.parentId) |
|
||||||
|
pinnedMessages.showInChannel.equals(true))
|
||||||
|
..orderBy([OrderingTerm.asc(pinnedMessages.createdAt)]))
|
||||||
|
.map(_messageFromJoinRow)
|
||||||
|
.get());
|
||||||
|
|
||||||
|
if (messagePagination?.lessThan != null) {
|
||||||
|
final lessThanIndex = msgList.indexWhere(
|
||||||
|
(m) => m.id == messagePagination.lessThan,
|
||||||
|
);
|
||||||
|
if (lessThanIndex != -1) {
|
||||||
|
msgList.removeRange(lessThanIndex, msgList.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (messagePagination?.greaterThanOrEqual != null) {
|
||||||
|
final greaterThanIndex = msgList.indexWhere(
|
||||||
|
(m) => m.id == messagePagination.greaterThanOrEqual,
|
||||||
|
);
|
||||||
|
if (greaterThanIndex != -1) {
|
||||||
|
msgList.removeRange(0, greaterThanIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (messagePagination?.limit != null) {
|
||||||
|
return msgList.take(messagePagination.limit).toList();
|
||||||
|
}
|
||||||
|
return msgList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Updates the message data of a particular channel with
|
||||||
|
/// the new [messageList] data
|
||||||
|
Future<void> updateMessages(String cid, List<Message> messageList) async {
|
||||||
|
if (messageList == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return batch((batch) {
|
||||||
|
batch.insertAll(
|
||||||
|
pinnedMessages,
|
||||||
|
messageList.map((it) => it.toPinnedEntity(cid: cid)).toList(),
|
||||||
|
mode: InsertMode.insertOrReplace,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'pinned_message_dao.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// DaoGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
mixin _$PinnedMessageDaoMixin on DatabaseAccessor<MoorChatDatabase> {
|
||||||
|
$PinnedMessagesTable get pinnedMessages => attachedDatabase.pinnedMessages;
|
||||||
|
$UsersTable get users => attachedDatabase.users;
|
||||||
|
}
|
||||||
@@ -25,6 +25,7 @@ LazyDatabase _openConnection(
|
|||||||
@UseMoor(tables: [
|
@UseMoor(tables: [
|
||||||
Channels,
|
Channels,
|
||||||
Messages,
|
Messages,
|
||||||
|
PinnedMessages,
|
||||||
Reactions,
|
Reactions,
|
||||||
Users,
|
Users,
|
||||||
Members,
|
Members,
|
||||||
@@ -35,6 +36,7 @@ LazyDatabase _openConnection(
|
|||||||
UserDao,
|
UserDao,
|
||||||
ChannelDao,
|
ChannelDao,
|
||||||
MessageDao,
|
MessageDao,
|
||||||
|
PinnedMessageDao,
|
||||||
MemberDao,
|
MemberDao,
|
||||||
ReactionDao,
|
ReactionDao,
|
||||||
ReadDao,
|
ReadDao,
|
||||||
@@ -67,7 +69,7 @@ class MoorChatDatabase extends _$MoorChatDatabase {
|
|||||||
|
|
||||||
// you should bump this number whenever you change or add a table definition.
|
// you should bump this number whenever you change or add a table definition.
|
||||||
@override
|
@override
|
||||||
int get schemaVersion => 1;
|
int get schemaVersion => 2;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
MigrationStrategy get migration => MigrationStrategy(
|
MigrationStrategy get migration => MigrationStrategy(
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,6 @@
|
|||||||
export 'channels.dart';
|
export 'channels.dart';
|
||||||
export 'messages.dart';
|
export 'messages.dart';
|
||||||
|
export 'pinned_messages.dart';
|
||||||
export 'reactions.dart';
|
export 'reactions.dart';
|
||||||
export 'users.dart';
|
export 'users.dart';
|
||||||
export 'members.dart';
|
export 'members.dart';
|
||||||
|
|||||||
@@ -64,6 +64,18 @@ class Messages extends Table {
|
|||||||
/// Id of the User who sent the message
|
/// Id of the User who sent the message
|
||||||
TextColumn get userId => text().nullable()();
|
TextColumn get userId => text().nullable()();
|
||||||
|
|
||||||
|
/// Whether the message is pinned or not
|
||||||
|
BoolColumn get pinned => boolean().withDefault(const Constant(false))();
|
||||||
|
|
||||||
|
/// The DateTime at which the message was pinned
|
||||||
|
DateTimeColumn get pinnedAt => dateTime().nullable()();
|
||||||
|
|
||||||
|
/// The DateTime on which the message pin expires
|
||||||
|
DateTimeColumn get pinExpires => dateTime().nullable()();
|
||||||
|
|
||||||
|
/// Id of the User who pinned the message
|
||||||
|
TextColumn get pinnedByUserId => text().nullable()();
|
||||||
|
|
||||||
/// The channel cid of which this message is part of
|
/// The channel cid of which this message is part of
|
||||||
TextColumn get channelCid => text().nullable().customConstraint(
|
TextColumn get channelCid => text().nullable().customConstraint(
|
||||||
'NULLABLE REFERENCES channels(cid) ON DELETE CASCADE')();
|
'NULLABLE REFERENCES channels(cid) ON DELETE CASCADE')();
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import 'package:moor/moor.dart';
|
||||||
|
|
||||||
|
import 'messages.dart';
|
||||||
|
|
||||||
|
/// Represents a [PinnedMessages] table in [MoorChatDatabase].
|
||||||
|
@DataClassName('PinnedMessageEntity')
|
||||||
|
class PinnedMessages extends Messages {}
|
||||||
@@ -28,11 +28,13 @@ extension ChannelEntityX on ChannelEntity {
|
|||||||
List<Member> members,
|
List<Member> members,
|
||||||
List<Read> reads,
|
List<Read> reads,
|
||||||
List<Message> messages,
|
List<Message> messages,
|
||||||
|
List<Message> pinnedMessages,
|
||||||
}) {
|
}) {
|
||||||
return ChannelState(
|
return ChannelState(
|
||||||
members: members,
|
members: members,
|
||||||
read: reads,
|
read: reads,
|
||||||
messages: messages,
|
messages: messages,
|
||||||
|
pinnedMessages: pinnedMessages,
|
||||||
channel: toChannelModel(createdBy: createdBy),
|
channel: toChannelModel(createdBy: createdBy),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,3 +5,4 @@ export 'event_mapper.dart';
|
|||||||
export 'member_mapper.dart';
|
export 'member_mapper.dart';
|
||||||
export 'read_mapper.dart';
|
export 'read_mapper.dart';
|
||||||
export 'message_mapper.dart';
|
export 'message_mapper.dart';
|
||||||
|
export 'pinned_message_mapper.dart';
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ extension MessageEntityX on MessageEntity {
|
|||||||
/// Maps a [MessageEntity] into [Message]
|
/// Maps a [MessageEntity] into [Message]
|
||||||
Message toMessage({
|
Message toMessage({
|
||||||
User user,
|
User user,
|
||||||
|
User pinnedBy,
|
||||||
List<Reaction> latestReactions,
|
List<Reaction> latestReactions,
|
||||||
List<Reaction> ownReactions,
|
List<Reaction> ownReactions,
|
||||||
Message quotedMessage,
|
Message quotedMessage,
|
||||||
@@ -37,6 +38,10 @@ extension MessageEntityX on MessageEntity {
|
|||||||
text: messageText,
|
text: messageText,
|
||||||
user: user,
|
user: user,
|
||||||
deletedAt: deletedAt,
|
deletedAt: deletedAt,
|
||||||
|
pinned: pinned,
|
||||||
|
pinnedAt: pinnedAt,
|
||||||
|
pinExpires: pinExpires,
|
||||||
|
pinnedBy: pinnedBy,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -67,6 +72,10 @@ extension MessageX on Message {
|
|||||||
userId: user?.id,
|
userId: user?.id,
|
||||||
deletedAt: deletedAt,
|
deletedAt: deletedAt,
|
||||||
messageText: text,
|
messageText: text,
|
||||||
|
pinned: pinned,
|
||||||
|
pinnedAt: pinnedAt,
|
||||||
|
pinExpires: pinExpires,
|
||||||
|
pinnedByUserId: pinnedBy?.id,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:stream_chat/stream_chat.dart';
|
||||||
|
import 'package:stream_chat_persistence/src/db/moor_chat_database.dart';
|
||||||
|
|
||||||
|
/// Useful mapping functions for [PinnedMessageEntity]
|
||||||
|
extension PinnedMessageEntityX on PinnedMessageEntity {
|
||||||
|
/// Maps a [PinnedMessageEntity] into [Message]
|
||||||
|
Message toMessage({
|
||||||
|
User user,
|
||||||
|
User pinnedBy,
|
||||||
|
List<Reaction> latestReactions,
|
||||||
|
List<Reaction> ownReactions,
|
||||||
|
Message quotedMessage,
|
||||||
|
}) {
|
||||||
|
return Message(
|
||||||
|
shadowed: shadowed,
|
||||||
|
latestReactions: latestReactions,
|
||||||
|
ownReactions: ownReactions,
|
||||||
|
attachments: attachments?.map((it) {
|
||||||
|
final json = jsonDecode(it);
|
||||||
|
return Attachment.fromData(json);
|
||||||
|
})?.toList(),
|
||||||
|
createdAt: createdAt,
|
||||||
|
extraData: extraData,
|
||||||
|
updatedAt: updatedAt,
|
||||||
|
id: id,
|
||||||
|
type: type,
|
||||||
|
status: status,
|
||||||
|
command: command,
|
||||||
|
parentId: parentId,
|
||||||
|
quotedMessageId: quotedMessageId,
|
||||||
|
quotedMessage: quotedMessage,
|
||||||
|
reactionCounts: reactionCounts,
|
||||||
|
reactionScores: reactionScores,
|
||||||
|
replyCount: replyCount,
|
||||||
|
showInChannel: showInChannel,
|
||||||
|
text: messageText,
|
||||||
|
user: user,
|
||||||
|
deletedAt: deletedAt,
|
||||||
|
pinned: pinned,
|
||||||
|
pinnedAt: pinnedAt,
|
||||||
|
pinExpires: pinExpires,
|
||||||
|
pinnedBy: pinnedBy,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Useful mapping functions for [Message]
|
||||||
|
extension PMessageX on Message {
|
||||||
|
/// Maps a [Message] into [PinnedMessageEntity]
|
||||||
|
PinnedMessageEntity toPinnedEntity({String cid}) {
|
||||||
|
return PinnedMessageEntity(
|
||||||
|
id: id,
|
||||||
|
attachments: attachments?.map((it) {
|
||||||
|
return jsonEncode(it.toData());
|
||||||
|
})?.toList(),
|
||||||
|
channelCid: cid,
|
||||||
|
type: type,
|
||||||
|
parentId: parentId,
|
||||||
|
quotedMessageId: quotedMessageId,
|
||||||
|
command: command,
|
||||||
|
createdAt: createdAt,
|
||||||
|
shadowed: shadowed,
|
||||||
|
showInChannel: showInChannel,
|
||||||
|
replyCount: replyCount,
|
||||||
|
reactionScores: reactionScores,
|
||||||
|
reactionCounts: reactionCounts,
|
||||||
|
status: status,
|
||||||
|
updatedAt: updatedAt,
|
||||||
|
extraData: extraData,
|
||||||
|
userId: user?.id,
|
||||||
|
deletedAt: deletedAt,
|
||||||
|
messageText: text,
|
||||||
|
pinned: pinned,
|
||||||
|
pinnedAt: pinnedAt,
|
||||||
|
pinExpires: pinExpires,
|
||||||
|
pinnedByUserId: pinnedBy?.id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -81,11 +81,21 @@ class StreamChatPersistenceClient extends ChatPersistenceClient {
|
|||||||
return _db.messageDao.deleteMessageByIds(messageIds);
|
return _db.messageDao.deleteMessageByIds(messageIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> deletePinnedMessageByIds(List<String> messageIds) {
|
||||||
|
return _db.pinnedMessageDao.deleteMessageByIds(messageIds);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> deleteMessageByCids(List<String> cids) {
|
Future<void> deleteMessageByCids(List<String> cids) {
|
||||||
return _db.messageDao.deleteMessageByCids(cids);
|
return _db.messageDao.deleteMessageByCids(cids);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> deletePinnedMessageByCids(List<String> cids) {
|
||||||
|
return _db.pinnedMessageDao.deleteMessageByCids(cids);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<Member>> getMembersByCid(String cid) {
|
Future<List<Member>> getMembersByCid(String cid) {
|
||||||
return _db.memberDao.getMembersByCid(cid);
|
return _db.memberDao.getMembersByCid(cid);
|
||||||
@@ -107,6 +117,17 @@ class StreamChatPersistenceClient extends ChatPersistenceClient {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<Message>> getPinnedMessagesByCid(
|
||||||
|
String cid, {
|
||||||
|
PaginationParams messagePagination,
|
||||||
|
}) {
|
||||||
|
return _db.pinnedMessageDao.getMessagesByCid(
|
||||||
|
cid,
|
||||||
|
messagePagination: messagePagination,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<Read>> getReadsByCid(String cid) {
|
Future<List<Read>> getReadsByCid(String cid) {
|
||||||
return _db.readDao.getReadsByCid(cid);
|
return _db.readDao.getReadsByCid(cid);
|
||||||
@@ -140,14 +161,15 @@ class StreamChatPersistenceClient extends ChatPersistenceClient {
|
|||||||
@override
|
@override
|
||||||
Future<List<ChannelState>> getChannelStates({
|
Future<List<ChannelState>> getChannelStates({
|
||||||
Map<String, dynamic> filter,
|
Map<String, dynamic> filter,
|
||||||
List<SortOption> sort = const [],
|
List<SortOption<ChannelModel>> sort = const [],
|
||||||
PaginationParams paginationParams,
|
PaginationParams paginationParams,
|
||||||
}) {
|
}) async {
|
||||||
return _db.channelQueryDao.getChannelStates(
|
final channels = await _db.channelQueryDao.getChannels(
|
||||||
filter: filter,
|
filter: filter,
|
||||||
sort: sort,
|
sort: sort,
|
||||||
paginationParams: paginationParams,
|
paginationParams: paginationParams,
|
||||||
);
|
);
|
||||||
|
return Future.wait(channels.map((e) => getChannelStateByCid(e.cid)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -178,6 +200,11 @@ class StreamChatPersistenceClient extends ChatPersistenceClient {
|
|||||||
return _db.messageDao.updateMessages(cid, messages);
|
return _db.messageDao.updateMessages(cid, messages);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> updatePinnedMessages(String cid, List<Message> messages) {
|
||||||
|
return _db.pinnedMessageDao.updateMessages(cid, messages);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> updateReactions(List<Reaction> reactions) {
|
Future<void> updateReactions(List<Reaction> reactions) {
|
||||||
return _db.reactionDao.updateReactions(reactions);
|
return _db.reactionDao.updateReactions(reactions);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
name: stream_chat_persistence
|
name: stream_chat_persistence
|
||||||
homepage: https://github.com/GetStream/stream-chat-flutter
|
homepage: https://github.com/GetStream/stream-chat-flutter
|
||||||
description: Official Stream Chat Persistence library. Build your own chat experience using Dart and Flutter.
|
description: Official Stream Chat Persistence library. Build your own chat experience using Dart and Flutter.
|
||||||
version: 1.2.0-beta
|
version: 1.3.0-beta
|
||||||
repository: https://github.com/GetStream/stream-chat-flutter
|
repository: https://github.com/GetStream/stream-chat-flutter
|
||||||
issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues
|
issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ dependencies:
|
|||||||
path: ^1.7.0
|
path: ^1.7.0
|
||||||
path_provider: ^1.6.27
|
path_provider: ^1.6.27
|
||||||
sqlite3_flutter_libs: ^0.4.0+1
|
sqlite3_flutter_libs: ^0.4.0+1
|
||||||
stream_chat: ^1.2.0-beta
|
stream_chat: ^1.3.0-beta
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
test: ^1.15.7
|
test: ^1.15.7
|
||||||
|
|||||||
Reference in New Issue
Block a user