Compare commits
57
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
022e7078cd | ||
|
|
189b4a8ca1 | ||
|
|
70dd3dde7f | ||
|
|
4e9ac3ed36 | ||
|
|
3ecaf1b8ca | ||
|
|
9745a83ffb | ||
|
|
fa78d70016 | ||
|
|
43c4f8527a | ||
|
|
b411b562b2 | ||
|
|
cdc3aa2a00 | ||
|
|
c7fb55e702 | ||
|
|
33fb72f931 | ||
|
|
f2ebaa8381 | ||
|
|
e0ab743008 | ||
|
|
b543806f62 | ||
|
|
da294f319e | ||
|
|
a387b8f44e | ||
|
|
e5183e0152 | ||
|
|
f74524fe66 | ||
|
|
4d6a94994e | ||
|
|
845d61433f | ||
|
|
992357499c | ||
|
|
0d1923e6d0 | ||
|
|
cacfb76b23 | ||
|
|
8168d224d3 | ||
|
|
aca296030c | ||
|
|
f5a61d9a8b | ||
|
|
3c1c8a120d | ||
|
|
3925de58b1 | ||
|
|
0f6022a731 | ||
|
|
a1c1be8f8a | ||
|
|
01eae4da9c | ||
|
|
77d0084467 | ||
|
|
5907edccc8 | ||
|
|
add00a5609 | ||
|
|
f17f57cc5e | ||
|
|
187d147dc7 | ||
|
|
b378d88aaf | ||
|
|
3b69e5873e | ||
|
|
5e5ed0a11e | ||
|
|
bca2258508 | ||
|
|
7b739dff45 | ||
|
|
ab1dd7e794 | ||
|
|
0967582639 | ||
|
|
91b237d3d3 | ||
|
|
69303cd7d1 | ||
|
|
b774ecbcb4 | ||
|
|
b492ed2969 | ||
|
|
6d557d735a | ||
|
|
f29ed58445 | ||
|
|
1941591110 | ||
|
|
a91e9dfb38 | ||
|
|
c423aa7970 | ||
|
|
910bafc5f4 | ||
|
|
2ae57f7b21 | ||
|
|
14a5edb24b | ||
|
|
0ff6fb25f4 |
@@ -272,7 +272,7 @@ run-android: export TARGET := android
|
||||
run-android: ##@run Build Android APK and start it on the device
|
||||
npx react-native run-android --appIdSuffix debug
|
||||
|
||||
SIMULATOR=
|
||||
SIMULATOR=iPhone 13
|
||||
run-ios: export TARGET := ios
|
||||
run-ios: ##@run Build iOS app and start it in a simulator/device
|
||||
ifneq ("$(SIMULATOR)", "")
|
||||
@@ -287,6 +287,7 @@ endif
|
||||
|
||||
lint: export TARGET := clojure
|
||||
lint: ##@test Run code style checks
|
||||
sh scripts/lint-re-frame-in-quo-components.sh && \
|
||||
clj-kondo --config .clj-kondo/config.edn --cache false --lint src && \
|
||||
TARGETS=$$(git diff --diff-filter=d --cached --name-only src && echo src) && \
|
||||
clojure -Scp "$$CLASS_PATH" -m cljfmt.main check --indents indentation.edn $$TARGETS
|
||||
|
||||
Binary file not shown.
@@ -62,7 +62,7 @@ pipeline {
|
||||
--rerun_count=2 \
|
||||
--testrail_report=True \
|
||||
-m testrail_id \
|
||||
-m \"not upgrade\" \
|
||||
-m \"new_ui_critical\" \
|
||||
-k \"${params.KEYWORD_EXPRESSION}\" \
|
||||
--apk=${params.APK_NAME}
|
||||
"""
|
||||
|
||||
@@ -14,7 +14,7 @@ pipeline {
|
||||
string(
|
||||
name: 'TEST_MARKERS',
|
||||
description: 'Marker expression for matching tests to run.',
|
||||
defaultValue: 'critical',
|
||||
defaultValue: 'new_ui_critical',
|
||||
) */
|
||||
string(
|
||||
name: 'APK_NAME',
|
||||
|
||||
+1
-1
@@ -640,7 +640,7 @@ SPEC CHECKSUMS:
|
||||
FBLazyVector: 3bb422f41b18121b71783a905c10e58606f7dc3e
|
||||
FBReactNativeSpec: f2c97f2529dd79c083355182cc158c9f98f4bd6e
|
||||
Folly: b73c3869541e86821df3c387eb0af5f65addfab4
|
||||
glog: 36ce0530c6d2c3a5a4326885ef4069564887a1db
|
||||
glog: 997518ea2aa2d8cd5df9797b641b758d52ecf2bc
|
||||
HMSegmentedControl: 34c1f54d822d8308e7b24f5d901ec674dfa31352
|
||||
Keycard: ac6df4d91525c3c82635ac24d4ddd9a80aca5fc8
|
||||
libwebp: 60305b2e989864154bd9be3d772730f08fc6a59c
|
||||
|
||||
@@ -41,6 +41,8 @@
|
||||
65F6941A25780A4F00A45E76 /* Bridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65F6941825780A4F00A45E76 /* Bridge.swift */; };
|
||||
65F6941B25780A4F00A45E76 /* Bridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65F6941825780A4F00A45E76 /* Bridge.swift */; };
|
||||
70ADBB5ECF934DCF8A0E4919 /* Inter-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = 1426DF592BA248FC81D955CB /* Inter-Regular.otf */; };
|
||||
715D8132290BE850006F5C88 /* UbuntuMono-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 715D8131290BE850006F5C88 /* UbuntuMono-Regular.ttf */; };
|
||||
715D8133290BE850006F5C88 /* UbuntuMono-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 715D8131290BE850006F5C88 /* UbuntuMono-Regular.ttf */; };
|
||||
74B758FC20D7C00B003343C3 /* launch-image-universal.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 74B758FB20D7C00B003343C3 /* launch-image-universal.storyboard */; };
|
||||
8391E8E0E93C41A98AAA6631 /* Inter-SemiBoldItalic.otf in Resources */ = {isa = PBXBuildFile; fileRef = A4F2BBE8D4DD4140A6CCAC39 /* Inter-SemiBoldItalic.otf */; };
|
||||
A0093AB50DA85E1D01818CCA /* libPods-Status-StatusIm-StatusImTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1E88D313E5D854BE6BC81FC1 /* libPods-Status-StatusIm-StatusImTests.a */; };
|
||||
@@ -121,6 +123,7 @@
|
||||
65F6941725780A4E00A45E76 /* StatusImTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "StatusImTests-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
65F6941825780A4F00A45E76 /* Bridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bridge.swift; sourceTree = "<group>"; };
|
||||
693A62DB37BC4CD5A30E5C96 /* Inter-SemiBold.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Inter-SemiBold.otf"; path = "../resources/fonts/Inter-SemiBold.otf"; sourceTree = "<group>"; };
|
||||
715D8131290BE850006F5C88 /* UbuntuMono-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "UbuntuMono-Regular.ttf"; path = "../resources/fonts/UbuntuMono-Regular.ttf"; sourceTree = "<group>"; };
|
||||
74B758FB20D7C00B003343C3 /* launch-image-universal.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = "launch-image-universal.storyboard"; sourceTree = "<group>"; };
|
||||
7AFBE28450C0239119FBDBA2 /* Pods-Status-StatusIm-StatusImTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Status-StatusIm-StatusImTests.debug.xcconfig"; path = "Target Support Files/Pods-Status-StatusIm-StatusImTests/Pods-Status-StatusIm-StatusImTests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
8B9A886A2CB448B1ABA0EB62 /* libc++.tbd */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; };
|
||||
@@ -229,6 +232,7 @@
|
||||
693A62DB37BC4CD5A30E5C96 /* Inter-SemiBold.otf */,
|
||||
A4F2BBE8D4DD4140A6CCAC39 /* Inter-SemiBoldItalic.otf */,
|
||||
9C76AF5A418D4D65A4CAD1D9 /* InterStatus-Regular.otf */,
|
||||
715D8131290BE850006F5C88 /* UbuntuMono-Regular.ttf */,
|
||||
);
|
||||
name = Resources;
|
||||
sourceTree = "<group>";
|
||||
@@ -475,6 +479,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
74B758FC20D7C00B003343C3 /* launch-image-universal.storyboard in Resources */,
|
||||
715D8132290BE850006F5C88 /* UbuntuMono-Regular.ttf in Resources */,
|
||||
B2F2D1BC1D9D531B00B7B453 /* Images.xcassets in Resources */,
|
||||
D84616FB563A48EBB1678699 /* Inter-Bold.otf in Resources */,
|
||||
D99C50E5E18942A39C8DDF61 /* Inter-BoldItalic.otf in Resources */,
|
||||
@@ -493,6 +498,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
3AAD2ACA24A3A60E0075D594 /* launch-image-universal.storyboard in Resources */,
|
||||
715D8133290BE850006F5C88 /* UbuntuMono-Regular.ttf in Resources */,
|
||||
3AAD2ACB24A3A60E0075D594 /* Images.xcassets in Resources */,
|
||||
3AAD2ACC24A3A60E0075D594 /* Inter-Bold.otf in Resources */,
|
||||
3AAD2ACD24A3A60E0075D594 /* Inter-BoldItalic.otf in Resources */,
|
||||
|
||||
@@ -105,6 +105,7 @@
|
||||
<string>Inter-Thin-BETA.otf</string>
|
||||
<string>Inter-ThinItalic-BETA.otf</string>
|
||||
<string>InterStatus-Regular.otf</string>
|
||||
<string>UbuntuMono-Regular.ttf</string>
|
||||
</array>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
|
||||
@@ -108,6 +108,7 @@
|
||||
<string>Inter-Thin-BETA.otf</string>
|
||||
<string>Inter-ThinItalic-BETA.otf</string>
|
||||
<string>InterStatus-Regular.otf</string>
|
||||
<string>UbuntuMono-Regular.ttf</string>
|
||||
</array>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
|
||||
@@ -20,7 +20,11 @@ buildGoPackage {
|
||||
# Build the Go library
|
||||
buildPhase = ''
|
||||
runHook preBuild
|
||||
go build -buildmode=c-archive -o $out/libstatus.a $NIX_BUILD_TOP/main.go
|
||||
go build \
|
||||
-buildmode='c-archive' \
|
||||
-tags='gowaku_skip_migrations' \
|
||||
-o "$out/libstatus.a" \
|
||||
$NIX_BUILD_TOP/main.go
|
||||
runHook postBuild
|
||||
'';
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
|
||||
let
|
||||
inherit (lib) concatStringsSep optionalString optional;
|
||||
isIOS = platform == "ios";
|
||||
isAndroid = platform == "android";
|
||||
|
||||
in buildGoPackage {
|
||||
pname = source.repo;
|
||||
version = "${source.cleanVersion}-${source.shortRev}-${platform}";
|
||||
@@ -20,30 +23,32 @@ in buildGoPackage {
|
||||
|
||||
# Sandbox causes Xcode issues on MacOS. Requires sandbox=relaxed.
|
||||
# https://github.com/status-im/status-mobile/pull/13912
|
||||
__noChroot = (platform == "ios");
|
||||
__noChroot = isIOS;
|
||||
|
||||
extraSrcPaths = [ gomobile ];
|
||||
nativeBuildInputs = [ gomobile removeReferencesTo ]
|
||||
++ optional (platform == "android") openjdk
|
||||
++ optional (platform == "ios") xcodeWrapper;
|
||||
++ optional isAndroid openjdk
|
||||
++ optional isIOS xcodeWrapper;
|
||||
|
||||
ldflags = concatStringsSep " " goBuildLdFlags;
|
||||
|
||||
ANDROID_HOME = optionalString (platform == "android") androidPkgs.sdk;
|
||||
ANDROID_HOME = optionalString isAndroid androidPkgs.sdk;
|
||||
|
||||
# Ensure XCode is present for iOS, instead of failing at the end of the build.
|
||||
preConfigure = optionalString (platform == "ios") utils.enforceXCodeAvailable;
|
||||
preConfigure = optionalString isIOS utils.enforceXCodeAvailable;
|
||||
|
||||
buildPhase = ''
|
||||
runHook preBuild
|
||||
echo -e "\nBuilding $pname for: ${concatStringsSep "," targets}"
|
||||
|
||||
set -x
|
||||
gomobile bind \
|
||||
${concatStringsSep " " goBuildFlags} \
|
||||
-ldflags="$ldflags" \
|
||||
-target=${concatStringsSep "," targets} \
|
||||
${optionalString (platform == "android") "-androidapi=${platformVersion}"} \
|
||||
${optionalString (platform == "ios") "-iosversion=${platformVersion}"} \
|
||||
${optionalString isAndroid "-androidapi=${platformVersion}" } \
|
||||
${optionalString isIOS "-iosversion=${platformVersion}" } \
|
||||
-tags='${optionalString isIOS "nowatchdog"} gowaku_skip_migrations' \
|
||||
-o ${outputFileName} \
|
||||
${source.goPackagePath}/mobile
|
||||
|
||||
@@ -58,7 +63,7 @@ in buildGoPackage {
|
||||
# Drop govers from disallowedReferences.
|
||||
dontRenameImports = true;
|
||||
# Replace hardcoded paths to go package in /nix/store.
|
||||
preFixup = optionalString (platform == "ios") ''
|
||||
preFixup = optionalString isIOS ''
|
||||
find $out -type f -exec \
|
||||
remove-references-to -t $disallowedReferences '{}' + || true
|
||||
'';
|
||||
|
||||
+5
-1
@@ -12,6 +12,9 @@
|
||||
"app:packager": "react-native start --host 0.0.0.0 --port 8081",
|
||||
"app:android": "react-native run-android"
|
||||
},
|
||||
"resolutions": {
|
||||
"react-devtools-core": "^4.26.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/preset-typescript": "^7.17.12",
|
||||
"@react-native-async-storage/async-storage": "^1.17.9",
|
||||
@@ -37,7 +40,7 @@
|
||||
"qrcode": "^1.4.1",
|
||||
"react": "16.13.1",
|
||||
"react-dom": "^16.4.2",
|
||||
"react-native": "0.63.4",
|
||||
"react-native": "0.63.5",
|
||||
"react-native-background-timer": "^2.1.1",
|
||||
"react-native-blob-util": "^0.13.18",
|
||||
"react-native-camera-kit": "^8.0.4",
|
||||
@@ -70,6 +73,7 @@
|
||||
"react-native-svg": "^9.8.4",
|
||||
"react-native-touch-id": "^4.4.1",
|
||||
"react-native-webview": "git+https://github.com/status-im/react-native-webview.git#refs/tags/v11.16.0-status",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"rn-emoji-keyboard": "git+https://github.com/status-im/rn-emoji-keyboard.git#refs/tags/v0.4.3",
|
||||
"tdigest": "^0.1.1"
|
||||
},
|
||||
|
||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 706 B After Width: | Height: | Size: 246 B |
Binary file not shown.
|
Before Width: | Height: | Size: 972 B After Width: | Height: | Size: 286 B |
Binary file not shown.
|
After Width: | Height: | Size: 286 B |
Binary file not shown.
|
After Width: | Height: | Size: 396 B |
Executable
+12
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
CHANGES=$(git diff --no-ext-diff --diff-filter=d --cached --unified=0 --no-prefix --minimal --exit-code src/quo src/quo2 | grep '^+' || echo '')
|
||||
|
||||
INVALID_CHANGES=$(echo "$CHANGES" | grep -E '(re-frame/dispatch|rf/dispatch|re-frame/subscribe|rf/subscribe|rf/sub|<sub|>evt)')
|
||||
|
||||
if test -n "$INVALID_CHANGES"; then
|
||||
echo "re-frame dispatch/subscribe is not allowed in quo/quo2 components"
|
||||
echo ''
|
||||
echo "$INVALID_CHANGES"
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useDerivedValue } from 'react-native-reanimated';
|
||||
import { useDerivedValue, withTiming, withSequence, withDelay, Easing } from 'react-native-reanimated';
|
||||
|
||||
// Generic Worklets
|
||||
|
||||
@@ -32,38 +32,99 @@ export function applyAnimationsToStyle(animations, style) {
|
||||
|
||||
// Switcher Worklets
|
||||
|
||||
export function switcherCloseButtonOpacity (switcherButtonOpacity) {
|
||||
export function stackOpacity (stackId, selectedStackId) {
|
||||
return useDerivedValue(
|
||||
function () {
|
||||
'worklet'
|
||||
return 1 - switcherButtonOpacity.value;
|
||||
return selectedStackId.value == stackId ? 1 : 0;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function switcherScreenRadius (switcherScreenSize) {
|
||||
export function stackPointer (stackId, selectedStackId) {
|
||||
return useDerivedValue(
|
||||
function () {
|
||||
'worklet'
|
||||
return switcherScreenSize.value/2;
|
||||
return selectedStackId.value == stackId ? "auto" : "none";
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function switcherScreenBottomPosition (switcherScreenRadius, switcherPressedRadius, initalPosition) {
|
||||
export function bottomTabIconColor (stackId, selectedStackId, passThrough, selectedTabColor, defaultColor, passThroughColor) {
|
||||
return useDerivedValue(
|
||||
function () {
|
||||
'worklet'
|
||||
return initalPosition + switcherPressedRadius - switcherScreenRadius.value;
|
||||
if (selectedStackId.value == stackId){
|
||||
return selectedTabColor;
|
||||
}
|
||||
else if (passThrough.value){
|
||||
return passThroughColor;
|
||||
}
|
||||
else {
|
||||
return defaultColor;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function switcherContainerBottomPosition (switcherScreenBottom, heightOffset) {
|
||||
|
||||
// Home Stack
|
||||
|
||||
const defaultDurationAndEasing = {
|
||||
duration: 300,
|
||||
easing: Easing.bezier(0, 0, 1, 1),
|
||||
}
|
||||
|
||||
export function homeStackOpacity (homeStackOpen) {
|
||||
return useDerivedValue(
|
||||
function () {
|
||||
'worklet'
|
||||
return - (switcherScreenBottom.value + heightOffset);
|
||||
return withTiming(homeStackOpen.value ? 1 : 0, defaultDurationAndEasing);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function homeStackTop (homeStackOpen, top) {
|
||||
return useDerivedValue(
|
||||
function () {
|
||||
'worklet'
|
||||
return withTiming(homeStackOpen.value ? 0 : top, defaultDurationAndEasing);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function homeStackLeft (selectedStackId, animateHomeStackLeft, homeStackOpen, left) {
|
||||
return useDerivedValue(
|
||||
function () {
|
||||
'worklet'
|
||||
if (animateHomeStackLeft.value) {
|
||||
var leftValue = left[selectedStackId.value];
|
||||
if (homeStackOpen.value) {
|
||||
return withSequence(withTiming(leftValue, {duration: 0}), withTiming(0, defaultDurationAndEasing))
|
||||
} else {
|
||||
return withTiming(leftValue, defaultDurationAndEasing);
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function homeStackPointer (homeStackOpen) {
|
||||
return useDerivedValue(
|
||||
function () {
|
||||
'worklet'
|
||||
return homeStackOpen.value ? "auto" : "none";
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function homeStackScale (homeStackOpen, minimizeScale) {
|
||||
return useDerivedValue(
|
||||
function () {
|
||||
'worklet'
|
||||
return withTiming(homeStackOpen.value ? 1 : minimizeScale, defaultDurationAndEasing);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
(+ 50 keyboard-height-android-delta) ;; TODO : remove 50 when react-native-navigation v8 will be implemented https://github.com/wix/react-native-navigation/issues/7225
|
||||
0))
|
||||
min-height (+ (* styles/vertical-padding 2) (:bottom safe-area))
|
||||
max-height (- window-height (:top safe-area) styles/margin-top)
|
||||
max-height (- window-height (:top safe-area))
|
||||
visible (react/state false)
|
||||
|
||||
master-translation-y (animated/use-value 0)
|
||||
@@ -202,17 +202,15 @@
|
||||
:wait-for master-ref
|
||||
:enabled (and (not disable-drag?)
|
||||
(not= sheet-height max-height))})
|
||||
[animated/view {:height sheet-height}
|
||||
[animated/scroll-view {:bounces false
|
||||
:flex 1
|
||||
:scroll-enabled (= sheet-height max-height)}
|
||||
[animated/view {:style {:padding-top styles/vertical-padding
|
||||
:padding-bottom (+ styles/vertical-padding
|
||||
(if (and platform/ios? keyboard-shown)
|
||||
keyboard-height
|
||||
(:bottom safe-area)))}
|
||||
:on-layout #(reset! height (.-nativeEvent.layout.height ^js %))}
|
||||
(into [:<>]
|
||||
(react/get-children children))]]]]]])))
|
||||
[animated/view {:height sheet-height
|
||||
:flex 1}
|
||||
[animated/view {:style {:padding-top styles/vertical-padding
|
||||
:padding-bottom (+ styles/vertical-padding
|
||||
(if (and platform/ios? keyboard-shown)
|
||||
keyboard-height
|
||||
(:bottom safe-area)))}
|
||||
:on-layout #(reset! height (.-nativeEvent.layout.height ^js %))}
|
||||
(into [:<>]
|
||||
(react/get-children children))]]]]])))
|
||||
|
||||
(def bottom-sheet (reagent/adapt-react-class (react/memo bottom-sheet-hooks)))
|
||||
(def bottom-sheet (reagent/adapt-react-class (react/memo bottom-sheet-hooks)))
|
||||
|
||||
@@ -56,6 +56,8 @@
|
||||
{:keyboardVerticalOffset (+ 44 (:status-bar-height @navigation-const))})]
|
||||
(reagent/children this))))
|
||||
|
||||
(def status-bar (.-StatusBar ^js rn))
|
||||
|
||||
(def keyboard (.-Keyboard ^js rn))
|
||||
|
||||
(def dismiss-keyboard! #(.dismiss ^js keyboard))
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
(ns quo2.components.avatars.account-avatar
|
||||
(:require [quo.react-native :as rn]
|
||||
(:require [react-native.core :as rn]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[status-im.ui.components.icons.icons :as icons]
|
||||
[quo.theme :as theme]))
|
||||
[quo2.components.icon :as icons]
|
||||
[quo2.theme :as theme]))
|
||||
|
||||
(def icon-color-value
|
||||
{:dark (get-in colors/customization [:dark :purple])
|
||||
{:dark (get-in colors/customization [:dark :purple])
|
||||
:light (get-in colors/customization [:light :purple])})
|
||||
|
||||
(defn get-border-radius [size]
|
||||
@@ -28,11 +28,11 @@
|
||||
[{:keys [size icon]
|
||||
:or {size 80
|
||||
icon :main-icons/placeholder}}]
|
||||
(let [icon-color (if (theme/dark?)
|
||||
(:dark icon-color-value)
|
||||
(:light icon-color-value))
|
||||
(let [icon-color (if (theme/dark?)
|
||||
(:dark icon-color-value)
|
||||
(:light icon-color-value))
|
||||
avatar-border-radius (get-border-radius size)
|
||||
inner-icon-size (get-inner-icon-sizes size)]
|
||||
inner-icon-size (get-inner-icon-sizes size)]
|
||||
[rn/view {:style {:width size
|
||||
:background-color icon-color
|
||||
:height size
|
||||
@@ -40,6 +40,5 @@
|
||||
:justify-content :center
|
||||
:align-items :center}}
|
||||
[icons/icon icon
|
||||
{:no-color true
|
||||
:container-style {:width inner-icon-size
|
||||
:height inner-icon-size}}]]))
|
||||
{:no-color true
|
||||
:size inner-icon-size}]]))
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
(ns quo2.components.avatars.channel-avatar
|
||||
(:require [quo2.foundations.colors :as colors]
|
||||
[quo.react-native :as rn]
|
||||
[react-native.core :as rn]
|
||||
[quo2.components.icon :as icons]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo.theme :as theme]))
|
||||
[quo2.theme :as theme]))
|
||||
|
||||
(defn channel-avatar [{:keys [big? locked? emoji-background-color emoji]}]
|
||||
(let [lock-exists? (some? locked?)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
(ns quo2.components.avatars.group-avatar
|
||||
(:require [quo2.foundations.colors :as colors]
|
||||
[quo2.components.icon :as icon]
|
||||
[quo.react-native :as rn]))
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(def sizes
|
||||
{:icon {:small 12
|
||||
|
||||
@@ -1,29 +1,28 @@
|
||||
(ns quo2.components.avatars.icon-avatar
|
||||
(:require [quo.react-native :as rn]
|
||||
(:require [react-native.core :as rn]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[status-im.ui.components.icons.icons :as icons]))
|
||||
[quo2.components.icon :as icons]))
|
||||
|
||||
(def sizes
|
||||
{:big 48
|
||||
{:big 48
|
||||
:medium 32
|
||||
:small 20})
|
||||
:small 20})
|
||||
|
||||
(defn icon-avatar
|
||||
[{:keys [size icon color opacity]
|
||||
:or {opacity 20}}]
|
||||
(let [component-size (size sizes)
|
||||
circle-color (colors/custom-color color 50 opacity)
|
||||
icon-color (colors/custom-color-by-theme color 50 60)
|
||||
icon-size (case size
|
||||
:big 20
|
||||
:medium 16
|
||||
:small 12)]
|
||||
circle-color (colors/custom-color color 50 opacity)
|
||||
icon-color (colors/custom-color-by-theme color 50 60)
|
||||
icon-size (case size
|
||||
:big 20
|
||||
:medium 16
|
||||
:small 12)]
|
||||
[rn/view {:style {:width component-size
|
||||
:height component-size
|
||||
:border-radius component-size
|
||||
:background-color circle-color
|
||||
:justify-content :center
|
||||
:align-items :center}}
|
||||
[icons/icon icon {:container-style {:width icon-size
|
||||
:height icon-size}
|
||||
:color icon-color}]]))
|
||||
[icons/icon icon {:size icon-size
|
||||
:color icon-color}]]))
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
(ns quo2.components.avatars.user-avatar
|
||||
(:require [quo.react-native :as rn]
|
||||
(:require [react-native.core :as rn]
|
||||
[react-native.fast-image :as fast-image]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.components.icon :as icons]
|
||||
[clojure.string :refer [upper-case split blank?]]
|
||||
[quo.theme :refer [dark?]]))
|
||||
[quo2.theme :refer [dark?]]
|
||||
[clojure.string :refer [upper-case split blank?]]))
|
||||
|
||||
(def sizes {:big {:outer 80
|
||||
:inner 72
|
||||
@@ -131,8 +132,8 @@
|
||||
:no-color true}])
|
||||
(if profile-picture
|
||||
;; display image
|
||||
[rn/image {:style (container-styling inner-dimensions outer-dimensions)
|
||||
:source profile-picture}]
|
||||
[fast-image/fast-image {:source {:uri profile-picture}
|
||||
:style (container-styling inner-dimensions outer-dimensions)}]
|
||||
;; else display initials
|
||||
[container inner-dimensions outer-dimensions
|
||||
[text/text {:weight :semi-bold
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
(ns quo2.components.avatars.wallet-user-avatar
|
||||
(:require [quo.react-native :as rn]
|
||||
(:require [react-native.core :as rn]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[clojure.string :as clojure-string]))
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
(ns quo2.components.buttons.button
|
||||
(:require [quo.react-native :as rn]
|
||||
(:require [react-native.core :as rn]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo.theme :as theme]
|
||||
[quo2.theme :as theme]
|
||||
[reagent.core :as reagent]
|
||||
[quo2.components.icon :as quo2.icons]))
|
||||
|
||||
@@ -44,6 +44,11 @@
|
||||
:background-color {:default colors/danger-50
|
||||
:pressed colors/danger-60
|
||||
:disabled colors/danger-50}}
|
||||
:positive {:icon-color colors/white
|
||||
:label {:style {:color colors/white}}
|
||||
:background-color {:default colors/success-50
|
||||
:pressed colors/success-60
|
||||
:disabled colors/success-50-opa-30}}
|
||||
:photo-bg {:icon-color colors/neutral-100
|
||||
:icon-secondary-color colors/neutral-80-opa-40
|
||||
:label {:style {:color colors/neutral-100}}
|
||||
@@ -104,6 +109,11 @@
|
||||
:background-color {:default colors/danger-60
|
||||
:pressed colors/danger-50
|
||||
:disabled colors/danger-60}}
|
||||
:positive {:icon-color colors/white
|
||||
:label {:style {:color colors/white}}
|
||||
:background-color {:default colors/success-60
|
||||
:pressed colors/success-50
|
||||
:disabled colors/success-60-opa-30}}
|
||||
:photo-bg {:icon-color colors/white
|
||||
:icon-secondary-color colors/neutral-30
|
||||
:label {:style {:color colors/white}}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
(ns quo2.components.buttons.dynamic-button
|
||||
(:require [quo.react-native :as rn]
|
||||
(:require [react-native.core :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[quo2.components.icon :as icon]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.components.markdown.text :as text]))
|
||||
@@ -60,11 +59,12 @@
|
||||
:customization-color customize jump-to and mention button color}"
|
||||
[_]
|
||||
(let [pressed? (reagent/atom false)]
|
||||
(fn [{:keys [type on-press count customization-color style]}]
|
||||
(fn [{:keys [type label on-press count customization-color style]}]
|
||||
[rn/touchable-without-feedback
|
||||
{:on-press-in #(reset! pressed? true)
|
||||
:on-press-out #(reset! pressed? false)
|
||||
:on-press on-press}
|
||||
{:on-press-in #(reset! pressed? true)
|
||||
:on-press-out #(reset! pressed? false)
|
||||
:on-press on-press
|
||||
:accessibility-label type}
|
||||
[rn/view {:style (merge
|
||||
{:flex-direction :row
|
||||
:height 24
|
||||
@@ -92,8 +92,8 @@
|
||||
:notification-up 0
|
||||
:search-with-label 8)}}
|
||||
(case type
|
||||
:jump-to (i18n/label :t/jump-to)
|
||||
:search-with-label (i18n/label :t/back)
|
||||
:jump-to label
|
||||
:search-with-label label
|
||||
(:mention :notification-down :notification-up) (str count))])
|
||||
(when (#{:jump-to :notification-down :notification-up} type)
|
||||
[icon-view type])]])))
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
(ns quo2.components.code.snippet
|
||||
(:require ["react-native" :as react-native]
|
||||
[cljs-bean.core :as bean]
|
||||
[clojure.string :as str]
|
||||
[oops.core :as oops]
|
||||
[react-native.core :as rn]
|
||||
[quo2.theme :as theme]
|
||||
[quo2.components.buttons.button :as button]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[reagent.core :as reagent]
|
||||
[react-native.linear-gradient :as linear-gradient]
|
||||
[react-native.masked-view :as masked-view]
|
||||
[react-native.syntax-highlighter :as highlighter]))
|
||||
|
||||
;; Example themes:
|
||||
;; https://github.com/react-syntax-highlighter/react-syntax-highlighter/tree/master/src/styles/hljs
|
||||
(def ^:private themes
|
||||
{:light {:hljs-comment {:color colors/neutral-40},
|
||||
:hljs-title {:color (colors/custom-color :blue 50)},
|
||||
:hljs-keyword {:color (colors/custom-color :green 50)},
|
||||
:hljs-string {:color (colors/custom-color :turquoise 50)},
|
||||
:hljs-literal {:color (colors/custom-color :turquoise 50)}
|
||||
:hljs-number {:color (colors/custom-color :turquoise 50)}
|
||||
:line-number {:color colors/neutral-40}}
|
||||
:dark {:hljs-comment {:color colors/neutral-60},
|
||||
:hljs-title {:color (colors/custom-color :blue 60)},
|
||||
:hljs-keyword {:color (colors/custom-color :green 60)},
|
||||
:hljs-string {:color (colors/custom-color :turquoise 60)},
|
||||
:hljs-literal {:color (colors/custom-color :turquoise 60)}
|
||||
:hljs-number {:color (colors/custom-color :turquoise 60)}
|
||||
:line-number {:color colors/neutral-40}}})
|
||||
|
||||
(defn- text-style [class-names]
|
||||
(->> class-names
|
||||
(map keyword)
|
||||
(reduce #(merge %1 (get-in themes [(theme/get-theme) %2]))
|
||||
{:flex-shrink 1
|
||||
;; Round to a nearest whole number to achieve consistent
|
||||
;; spacing (also important for calculating `max-text-height`).
|
||||
;; Line height seems to be inconsistent between text being
|
||||
;; wrapped and text being rendered on a newline using Flexbox
|
||||
;; layout.
|
||||
:line-height 18})))
|
||||
|
||||
(defn- render-nodes [nodes]
|
||||
(map (fn [{:keys [children value] :as node}]
|
||||
;; Node can have :children or a :value.
|
||||
(if children
|
||||
(into [text/text {:weight :code
|
||||
:size :paragraph-2
|
||||
:style (text-style (get-in node [:properties :className]))}]
|
||||
(render-nodes children))
|
||||
;; Remove newlines as we already render each line separately.
|
||||
(-> value str/trim-newline)))
|
||||
nodes))
|
||||
|
||||
(defn- code-block [{:keys [rows line-number-width]}]
|
||||
[into [:<>]
|
||||
(->> rows
|
||||
(render-nodes)
|
||||
;; Line numbers
|
||||
(map-indexed (fn [idx row]
|
||||
(conj [rn/view {:style {:flex-direction :row}}
|
||||
[rn/view {:style {:width line-number-width
|
||||
;; 8+12 margin
|
||||
:margin-right 20}}
|
||||
[text/text {:weight :code
|
||||
:size :paragraph-2
|
||||
:style (text-style ["line-number"])}
|
||||
(inc idx)]]]
|
||||
row))))])
|
||||
|
||||
(defn- native-renderer []
|
||||
(let [text-height (reagent/atom nil)]
|
||||
(fn [{:keys [rows max-lines on-copy-press]}]
|
||||
(let [background-color (colors/theme-colors
|
||||
colors/white
|
||||
colors/neutral-80-opa-40)
|
||||
background-color-left (colors/theme-colors
|
||||
colors/neutral-5
|
||||
colors/neutral-80)
|
||||
border-color (colors/theme-colors
|
||||
colors/neutral-20
|
||||
colors/neutral-80)
|
||||
rows (bean/->clj rows)
|
||||
font-scale (:font-scale (rn/use-window-dimensions))
|
||||
max-rows (or max-lines (count rows)) ;; Cut down on rows to process.
|
||||
max-line-digits (-> rows count (min max-rows) str count)
|
||||
;; ~ 9 is char width, 18 is width used in Figma.
|
||||
line-number-width (* font-scale (max 18 (* 9 max-line-digits)))
|
||||
max-text-height (some-> max-lines (* font-scale 18)) ;; 18 is font's line height.
|
||||
truncated? (and max-text-height (< max-text-height @text-height))
|
||||
maybe-mask-wrapper (if truncated?
|
||||
[masked-view/masked-view
|
||||
{:mask-element
|
||||
(reagent/as-element
|
||||
[linear-gradient/linear-gradient {:colors ["black" "transparent"]
|
||||
:locations [0.75 1]
|
||||
:style {:flex 1}}])}]
|
||||
[:<>])]
|
||||
|
||||
[rn/view {:style {:overflow :hidden
|
||||
:padding 8
|
||||
:background-color background-color
|
||||
:border-color border-color
|
||||
:border-width 1
|
||||
:border-radius 8
|
||||
;; Hide on intial render to avoid flicker when mask-wrapper is shown.
|
||||
:opacity (if @text-height 1 0)}}
|
||||
;; Line number container
|
||||
[rn/view {:style {:position :absolute
|
||||
:bottom 0
|
||||
:top 0
|
||||
:left 0
|
||||
:width (+ line-number-width 8 8)
|
||||
:background-color background-color-left
|
||||
:border-right-color border-color
|
||||
:border-right-width 1}}]
|
||||
(conj maybe-mask-wrapper [rn/view {:max-height max-text-height}
|
||||
[rn/view {:on-layout (fn [evt]
|
||||
(let [height (oops/oget evt "nativeEvent.layout.height")]
|
||||
(reset! text-height height)))}
|
||||
[code-block {:rows (take max-rows rows)
|
||||
:line-number-width line-number-width}]]])
|
||||
|
||||
;; Copy button
|
||||
[rn/view {:style {:position :absolute
|
||||
:bottom 8
|
||||
:right 8}}
|
||||
[button/button {:icon true
|
||||
:type :grey
|
||||
:size 24
|
||||
:on-press on-copy-press}
|
||||
:main-icons/copy]]]))))
|
||||
|
||||
(defn- wrap-renderer-fn [f {:keys [max-lines on-copy-press]}]
|
||||
(fn [^js props]
|
||||
(reagent/as-element [:f> f {:rows (.-rows props)
|
||||
:max-lines max-lines
|
||||
:on-copy-press on-copy-press}])))
|
||||
|
||||
(defn snippet [{:keys [language max-lines on-copy-press]} children]
|
||||
[highlighter/highlighter {:language language
|
||||
:renderer (wrap-renderer-fn
|
||||
native-renderer {:max-lines max-lines
|
||||
:on-copy-press #(when on-copy-press
|
||||
(on-copy-press children))})
|
||||
;; Default props to adapt Highlighter for react-native.
|
||||
:CodeTag react-native/View
|
||||
:PreTag react-native/View
|
||||
:show-line-numbers false
|
||||
:style #js {}
|
||||
:custom-style #js {:backgroundColor nil}}
|
||||
children])
|
||||
@@ -1,45 +1,44 @@
|
||||
(ns quo2.components.community.community-card-view
|
||||
(:require
|
||||
[quo2.components.community.community-view :as community-view]
|
||||
[status-im.utils.handlers :refer [<sub]]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.screens.communities.styles :as styles]
|
||||
[status-im.ui.screens.communities.icon :as communities.icon]))
|
||||
[quo2.components.community.style :as style]
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(defn community-card-view-item
|
||||
[{:keys [name description locked
|
||||
status tokens cover tags featured] :as community} on-press]
|
||||
(let [width (* (<sub [:dimensions/window-width]) 0.90)]
|
||||
[react/touchable-opacity {:on-press on-press}
|
||||
[react/view {:style (merge (styles/community-card 20)
|
||||
{:margin-bottom 16}
|
||||
(if featured
|
||||
{:margin-right 12
|
||||
:width width}
|
||||
{:flex 1
|
||||
:margin-horizontal 20}))}
|
||||
[react/view {:style {:height 230
|
||||
:border-radius 20}
|
||||
:on-press on-press}
|
||||
[react/view
|
||||
{:flex 1}
|
||||
[react/view (styles/community-cover-container 40)
|
||||
[react/image
|
||||
{:source cover
|
||||
:style
|
||||
{:flex 1
|
||||
:border-radius 20}}]]
|
||||
[react/view (styles/card-view-content-container 12)
|
||||
[react/view (styles/card-view-chat-icon 48)
|
||||
[communities.icon/community-icon-redesign community 48]]
|
||||
(when (= status :gated)
|
||||
[react/view (styles/permission-tag-styles)
|
||||
[community-view/permission-tag-container {:locked locked
|
||||
:status status
|
||||
:tokens tokens}]])
|
||||
[community-view/community-title
|
||||
{:title name
|
||||
:description description}]
|
||||
[community-view/community-stats-column :card-view]
|
||||
[community-view/community-tags tags]]]]]]))
|
||||
status tokens cover tags featured]} on-press]
|
||||
[rn/touchable-opacity {:on-press on-press}
|
||||
[rn/view {:style (merge (style/community-card 20)
|
||||
{:margin-bottom 16}
|
||||
(if featured
|
||||
{:margin-right 12}
|
||||
{:flex 1
|
||||
:margin-horizontal 20}))}
|
||||
[rn/view {:style {:height 230
|
||||
:border-radius 20}
|
||||
:on-press on-press}
|
||||
[rn/view
|
||||
{:flex 1}
|
||||
[rn/view (style/community-cover-container 40)
|
||||
[rn/image
|
||||
{:source cover
|
||||
:style
|
||||
{:flex 1
|
||||
:border-radius 20}}]]
|
||||
[rn/view (style/card-view-content-container 12)
|
||||
[rn/view (style/card-view-chat-icon 48)]
|
||||
;;TODO new pure component based on quo2 should be implemented without status-im usage
|
||||
;[communities.icon/community-icon-redesign community 48]]
|
||||
(when (= status :gated)
|
||||
[rn/view (style/permission-tag-styles)
|
||||
[community-view/permission-tag-container {:locked locked
|
||||
:status status
|
||||
:tokens tokens}]])
|
||||
[community-view/community-title
|
||||
{:title name
|
||||
:description description}]
|
||||
[rn/view {:style (style/card-stats-position)}
|
||||
[community-view/community-stats-column :card-view]]
|
||||
[rn/view {:style (style/community-tags-position)}
|
||||
[community-view/community-tags tags]]]]]]])
|
||||
|
||||
|
||||
@@ -3,74 +3,89 @@
|
||||
[quo2.components.community.community-view :as community-view]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[status-im.communities.core :as communities]
|
||||
[status-im.utils.handlers :refer [>evt]]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.screens.communities.styles :as styles]
|
||||
[status-im.ui.screens.communities.community :as community]
|
||||
[status-im.ui.screens.communities.icon :as communities.icon]))
|
||||
[quo2.components.counter.counter :as counter]
|
||||
[quo2.components.icon :as icons]
|
||||
[quo2.components.community.style :as style]
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(defn communities-list-view-item [{:keys [id name locked status tokens background-color] :as community}]
|
||||
[react/view {:style (merge (styles/community-card 16)
|
||||
{:margin-bottom 12
|
||||
:margin-horizontal 20})}
|
||||
[react/view {:style {:height 56
|
||||
:border-radius 16}
|
||||
:on-press (fn []
|
||||
(>evt [::communities/load-category-states id])
|
||||
(>evt [:dismiss-keyboard])
|
||||
(>evt [:navigate-to :community {:community-id id}]))
|
||||
:on-long-press #(>evt [:bottom-sheet/show-sheet
|
||||
{:content (fn []
|
||||
[community/community-actions community])}])}
|
||||
[react/view {:flex 1}
|
||||
[react/view {:flex-direction :row
|
||||
:border-radius 16
|
||||
:padding-horizontal 12
|
||||
:align-items :center
|
||||
:padding-vertical 8
|
||||
:background-color background-color}
|
||||
[react/view
|
||||
[communities.icon/community-icon-redesign community 32]]
|
||||
[react/view {:flex 1
|
||||
:margin-horizontal 12}
|
||||
(defn communities-list-view-item [props {:keys [name locked? status notifications
|
||||
tokens background-color]}]
|
||||
[rn/view {:style (merge (style/community-card 16)
|
||||
{:margin-bottom 12
|
||||
:margin-horizontal 20})}
|
||||
[rn/touchable-highlight (merge {:style {:height 56
|
||||
:border-radius 16}}
|
||||
props)
|
||||
[rn/view {:flex 1}
|
||||
[rn/view {:flex-direction :row
|
||||
:border-radius 16
|
||||
:padding-horizontal 12
|
||||
:align-items :center
|
||||
:padding-vertical 8
|
||||
:background-color background-color}
|
||||
[rn/view]
|
||||
;;TODO new pure component based on quo2 should be implemented without status-im usage
|
||||
;[communities.icon/community-icon-redesign community 32]]
|
||||
[rn/view {:flex 1
|
||||
:margin-horizontal 12}
|
||||
[text/text {:weight :semi-bold
|
||||
:size :paragraph-1
|
||||
:accessibility-label :community-name-text
|
||||
:number-of-lines 1
|
||||
:ellipsize-mode :tail}
|
||||
:ellipsize-mode :tail
|
||||
:style {:color (when (= notifications :muted)
|
||||
(colors/theme-colors
|
||||
colors/neutral-40
|
||||
colors/neutral-60))}}
|
||||
name]
|
||||
[community-view/community-stats-column :list-view]]
|
||||
(when (= status :gated)
|
||||
[community-view/permission-tag-container {:locked locked
|
||||
:status status
|
||||
:tokens tokens}])]]]])
|
||||
(if (= status :gated)
|
||||
[community-view/permission-tag-container {:locked? locked?
|
||||
:tokens tokens}]
|
||||
(cond
|
||||
(= notifications :unread-messages-count)
|
||||
[rn/view {:style {:width 8
|
||||
:height 8
|
||||
:border-radius 4
|
||||
:background-color (colors/theme-colors
|
||||
colors/neutral-40
|
||||
colors/neutral-60)}}]
|
||||
|
||||
(defn communities-membership-list-item [{:keys [id name status tokens locked] :as community}]
|
||||
[react/view {:margin-bottom 12
|
||||
:padding-horizontal 8}
|
||||
[react/touchable-highlight {:underlay-color colors/primary-50-opa-5
|
||||
:style {:border-radius 12}
|
||||
:on-press (fn []
|
||||
(>evt [::communities/load-category-states id])
|
||||
(>evt [:dismiss-keyboard])
|
||||
(>evt [:navigate-to :community {:community-id id}]))
|
||||
:on-long-press #(>evt [:bottom-sheet/show-sheet
|
||||
{:content (fn []
|
||||
[community/community-actions community])}])}
|
||||
[react/view {:flex 1
|
||||
:padding-vertical 8
|
||||
:padding-horizontal 12}
|
||||
[react/view {:flex-direction :row
|
||||
:border-radius 16
|
||||
:align-items :center}
|
||||
[communities.icon/community-icon-redesign community 48]
|
||||
[react/view {:margin-left 12
|
||||
:flex 1}
|
||||
[community-view/community-title {:title name}]]
|
||||
(= notifications :unread-mentions-count)
|
||||
[counter/counter {:type :default} 5]
|
||||
|
||||
(= notifications :muted)
|
||||
[icons/icon :main-icons2/muted {:container-style {:align-items :center
|
||||
:justify-content :center}
|
||||
:resize-mode :center
|
||||
:size 20
|
||||
:color (colors/theme-colors
|
||||
colors/neutral-40
|
||||
colors/neutral-50)}]))]]]])
|
||||
|
||||
(defn communities-membership-list-item [props {:keys [name status tokens locked?]}]
|
||||
[rn/view {:margin-bottom 20}
|
||||
[rn/touchable-highlight (merge {:underlay-color colors/primary-50-opa-5
|
||||
:style {:border-radius 12}}
|
||||
props)
|
||||
[rn/view {:flex 1}
|
||||
[rn/view {:flex-direction :row
|
||||
:border-radius 16
|
||||
:align-items :center}
|
||||
;;TODO new pure component based on quo2 should be implemented without status-im usage
|
||||
;[communities.icon/community-icon-redesign community 32]
|
||||
[rn/view {:flex 1
|
||||
:margin-left 12
|
||||
:justify-content :center}
|
||||
[text/text
|
||||
{:accessibility-label :chat-name-text
|
||||
:number-of-lines 1
|
||||
:ellipsize-mode :tail
|
||||
:weight :semi-bold
|
||||
:size :paragraph-1}
|
||||
name]]
|
||||
(when (= status :gated)
|
||||
[react/view {:justify-content :center
|
||||
:margin-right 12}
|
||||
[community-view/permission-tag-container {:locked locked
|
||||
:status status
|
||||
:tokens tokens}]])]]]])
|
||||
[rn/view {:justify-content :center
|
||||
:margin-right 12}
|
||||
[community-view/permission-tag-container {:locked? locked?
|
||||
:tokens tokens}]])]]]])
|
||||
@@ -4,83 +4,75 @@
|
||||
[quo2.components.icon :as icons]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.components.tags.permission-tag :as permission]
|
||||
[quo2.components.tags.tag :as tag]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.utils.money :as money]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.ui.screens.communities.styles :as styles]))
|
||||
[quo2.components.tags.tag :as tag]
|
||||
[quo2.components.community.style :as style]
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(defn format-members [count]
|
||||
(if (> count 1000000)
|
||||
(str (money/with-precision (/ count 1000000) 1) (i18n/label :t/M))
|
||||
(if (and (> count 999) (< count 1000000))
|
||||
(str (money/with-precision (/ count 1000) 1) (i18n/label :t/K))
|
||||
count)))
|
||||
|
||||
(defn community-stats [{:keys [icon count icon-color]}]
|
||||
[react/view (styles/stats-count-container)
|
||||
[react/view {:margin-right 4}
|
||||
(defn community-stats [{:keys [icon members-count icon-color]}]
|
||||
[rn/view (style/stats-count-container)
|
||||
[rn/view {:margin-right 4}
|
||||
[icons/icon icon {:container-style {:align-items :center
|
||||
:justify-content :center}
|
||||
:resize-mode :center
|
||||
:size 16
|
||||
:color icon-color}]]
|
||||
[text/text {:weight :regular
|
||||
:size :paragraph-1}
|
||||
(format-members count)]])
|
||||
:resize-mode :center
|
||||
:size 16
|
||||
:color icon-color}]]
|
||||
[text/text {:weight :regular
|
||||
:size :paragraph-1}
|
||||
members-count]])
|
||||
|
||||
(defn community-stats-column [type]
|
||||
(let [icon-color (colors/theme-colors colors/neutral-50 colors/neutral-40)]
|
||||
[react/view (if (= type :card-view)
|
||||
(styles/card-stats-container)
|
||||
(styles/list-stats-container))
|
||||
(let [icon-color (colors/theme-colors colors/neutral-50 colors/neutral-40)]
|
||||
[rn/view (if (= type :card-view)
|
||||
(style/card-stats-container)
|
||||
(style/list-stats-container))
|
||||
[community-stats {:icon :main-icons2/group
|
||||
:count "629.2K"
|
||||
:members-count "629.2K" ;;TODO here should be formatted value, use money/format-members from outside this component
|
||||
:icon-color icon-color}]
|
||||
[community-stats {:icon :main-icons2/lightning
|
||||
:count "112.1K"
|
||||
:members-count "112.1K"
|
||||
:icon-color icon-color}]
|
||||
[community-stats {:icon :main-icons2/placeholder
|
||||
:count 4
|
||||
:icon-color icon-color}]]))
|
||||
(when (= type :card-view)
|
||||
[community-stats {:icon :main-icons2/placeholder
|
||||
:count 4
|
||||
:icon-color icon-color}])]))
|
||||
|
||||
(defn community-tags [tags]
|
||||
[react/view (styles/community-tags-container)
|
||||
[rn/view (style/community-tags-container)
|
||||
(for [{:keys [id tag-label resource]} tags]
|
||||
^{:key id}
|
||||
[react/view {:margin-right 8}
|
||||
[rn/view {:margin-right 8}
|
||||
[tag/tag
|
||||
{:id id
|
||||
:size 24
|
||||
:label tag-label
|
||||
:type :emoji
|
||||
:labelled true
|
||||
:resource resource}]])])
|
||||
{:id id
|
||||
:size 24
|
||||
:label tag-label
|
||||
:type :emoji
|
||||
:labelled true
|
||||
:resource resource}]])])
|
||||
|
||||
(defn community-title [{:keys [title description size] :or {size :small}}]
|
||||
[react/view (styles/community-title-description-container (if (= size :large) 56 32))
|
||||
[rn/view (style/community-title-description-container (if (= size :large) 56 32))
|
||||
(when title
|
||||
[text/text
|
||||
{:accessibility-label :chat-name-text
|
||||
:number-of-lines 1
|
||||
:ellipsize-mode :tail
|
||||
:weight :semi-bold
|
||||
:size (if (= size :large) :heading-1 :heading-2)}
|
||||
:size (if (= size :large) :heading-1 :heading-2)}
|
||||
title])
|
||||
(when description
|
||||
[text/text
|
||||
{:accessibility-label :community-description-text
|
||||
:number-of-lines 2
|
||||
:ellipsize-mode :tail
|
||||
:weight :regular
|
||||
:size :paragraph-1
|
||||
:style {:margin-top (if (= size :large) 8 2)}}
|
||||
:weight :regular
|
||||
:size :paragraph-1
|
||||
:style {:margin-top (if (= size :large) 8 2)}}
|
||||
description])])
|
||||
|
||||
(defn permission-tag-container [{:keys [locked tokens]}]
|
||||
(defn permission-tag-container [{:keys [locked? tokens]}]
|
||||
[permission/tag {:background-color (colors/theme-colors
|
||||
colors/neutral-10
|
||||
colors/neutral-80)
|
||||
:locked locked
|
||||
:locked? locked?
|
||||
:tokens tokens
|
||||
:size 24}])
|
||||
@@ -0,0 +1,94 @@
|
||||
(ns quo2.components.community.discover-card
|
||||
(:require [quo2.components.markdown.text :as text]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.components.community.style :as style]
|
||||
[react-native.core :as rn]))
|
||||
|
||||
;; Discover card placeholders images.
|
||||
;; TODO replaced when real data is available
|
||||
(def images
|
||||
{:images [{:id 1 :column-images [{:id 1 :image ""}]}
|
||||
{:id 2 :column-images [{}]}]})
|
||||
|
||||
(defn card-title-and-description [title description]
|
||||
[rn/view
|
||||
{:flex 1
|
||||
:padding-top 8
|
||||
:padding-bottom 8
|
||||
:border-radius 12}
|
||||
[rn/view {:flex 1
|
||||
:padding-horizontal 12}
|
||||
[text/text {:accessibility-label :community-name-text
|
||||
:ellipsize-mode :tail
|
||||
:number-of-lines 1
|
||||
:weight :semi-bold
|
||||
:size :paragraph-1}
|
||||
title]
|
||||
[text/text {:accessibility-label :community-name-text
|
||||
:ellipsize-mode :tail
|
||||
:number-of-lines 1
|
||||
:color (colors/theme-colors
|
||||
colors/neutral-50
|
||||
colors/neutral-40)
|
||||
:weight :regular
|
||||
:size :paragraph-2}
|
||||
description]]])
|
||||
|
||||
(defn placeholder-list-images [{:keys [images width height border-radius]}]
|
||||
[rn/view
|
||||
[rn/view {:justify-content :center}
|
||||
(for [{:keys [id]} images]
|
||||
^{:key id}
|
||||
[rn/view {:border-radius border-radius
|
||||
:margin-top 4
|
||||
:margin-right 4
|
||||
:width width
|
||||
:height height
|
||||
:background-color colors/neutral-10}])]])
|
||||
|
||||
(defn placeholder-row-images [{:keys [first-image last-image images width height
|
||||
border-radius]}]
|
||||
[rn/view
|
||||
(when first-image
|
||||
[rn/view {:border-bottom-right-radius 6
|
||||
:border-bottom-left-radius 6
|
||||
:margin-right 4
|
||||
:width width
|
||||
:height height
|
||||
:background-color colors/neutral-10}])
|
||||
(when images
|
||||
[placeholder-list-images {:images images
|
||||
:width 32
|
||||
:height 32
|
||||
:border-radius 6}])
|
||||
(when last-image
|
||||
[rn/view {:border-top-left-radius border-radius
|
||||
:border-top-right-radius 6
|
||||
:margin-top 4
|
||||
:width width
|
||||
:height height
|
||||
:background-color colors/neutral-10}])])
|
||||
|
||||
(defn discover-card [{:keys [title description on-press accessibility-label]}]
|
||||
(let [on-joined-images (get images :images)]
|
||||
[rn/touchable-without-feedback
|
||||
{:on-press on-press
|
||||
:accessibility-label accessibility-label}
|
||||
[rn/view (merge (style/community-card 16)
|
||||
{:background-color (colors/theme-colors
|
||||
colors/white
|
||||
colors/neutral-90)}
|
||||
{:flex-direction :row
|
||||
:margin-horizontal 20
|
||||
:height 56
|
||||
:padding-right 12})
|
||||
[card-title-and-description title description]
|
||||
(for [{:keys [id column-images]} on-joined-images]
|
||||
^{:key id}
|
||||
[placeholder-row-images {:images (when (= id 1)
|
||||
column-images)
|
||||
:width 32
|
||||
:height (if (= id 1) 8 26)
|
||||
:border-radius 6
|
||||
:first-image "" ; TODO replace with real data
|
||||
:last-image ""}])]])) ; TODO replace with real data
|
||||
+16
-7
@@ -1,6 +1,5 @@
|
||||
(ns status-im.ui.screens.communities.styles
|
||||
(:require
|
||||
[quo2.foundations.colors :as colors]))
|
||||
(ns quo2.components.community.style
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
|
||||
(def category-item
|
||||
{:flex 1
|
||||
@@ -28,16 +27,26 @@
|
||||
:margin-right 16})
|
||||
|
||||
(defn card-stats-container []
|
||||
{:flex-direction :row
|
||||
:margin-top 12})
|
||||
{:flex-direction :row})
|
||||
|
||||
(defn list-stats-container []
|
||||
{:flex-direction :row
|
||||
:align-items :center})
|
||||
|
||||
(defn community-tags-container []
|
||||
{:flex-direction :row
|
||||
:margin-top 16})
|
||||
{:flex-direction :row})
|
||||
|
||||
(defn card-stats-position []
|
||||
{:position :absolute
|
||||
:top 116
|
||||
:right 12
|
||||
:left 12})
|
||||
|
||||
(defn community-tags-position []
|
||||
{:position :absolute
|
||||
:top 154
|
||||
:right 12
|
||||
:left 12})
|
||||
|
||||
(defn card-view-content-container [padding-horizontal]
|
||||
{:flex 1
|
||||
@@ -1,6 +1,6 @@
|
||||
(ns quo2.components.counter.counter
|
||||
(:require [quo.theme :as theme]
|
||||
[quo.react-native :as rn]
|
||||
(:require [quo2.theme :as theme]
|
||||
[react-native.core :as rn]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.foundations.colors :as colors]))
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
(ns quo2.components.dividers.divider-label
|
||||
(:require [quo.react-native :as rn]
|
||||
(:require [react-native.core :as rn]
|
||||
[quo2.components.markdown.text :as markdown.text]
|
||||
[status-im.ui.components.icons.icons :as icons]
|
||||
[quo2.foundations.colors :as colors]))
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.components.icon :as icons]))
|
||||
|
||||
(def chevron-icon-container-width 20)
|
||||
|
||||
@@ -42,8 +42,7 @@
|
||||
[icons/icon
|
||||
:main-icons/chevron-down
|
||||
{:color text-and-icon-color
|
||||
:width chevron-icon-container-width
|
||||
:height chevron-icon-container-height}])
|
||||
:size chevron-icon-container-width}])
|
||||
(when (pos? counter-value)
|
||||
[rn/view {:style {:border-radius 6
|
||||
:height 16
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
(ns quo2.components.dividers.new-messages
|
||||
(:require [quo.react-native :as rn]
|
||||
(:require [react-native.core :as rn]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.ui.components.react :as react]))
|
||||
[react-native.linear-gradient :as linear-gradient]))
|
||||
|
||||
(defn new-messages
|
||||
"new-messages params - label, color"
|
||||
[{:keys [label color] :or {label (i18n/label :new-messages-header)
|
||||
color :primary}}]
|
||||
[{:keys [label color] :or {color :primary}}]
|
||||
(let [bg-color (colors/custom-color-by-theme color 50 60 5 5)
|
||||
text-color (colors/custom-color-by-theme color 50 60)]
|
||||
[react/linear-gradient {:colors [bg-color "rgba(0,0,0,0)"]
|
||||
:start {:x 0 :y 0} :end {:x 0 :y 1}}
|
||||
[linear-gradient/linear-gradient {:colors [bg-color "rgba(0,0,0,0)"]
|
||||
:start {:x 0 :y 0} :end {:x 0 :y 1}}
|
||||
[rn/view {:style {:padding-left 60
|
||||
:padding-vertical 12
|
||||
:padding-right 24}}
|
||||
|
||||
@@ -1,68 +1,73 @@
|
||||
(ns quo2.components.drawers.action-drawers
|
||||
(:require [status-im.ui.components.react :as react]
|
||||
[quo.react-native :as rn]
|
||||
(:require [react-native.core :as rn]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.components.icon :as icon]
|
||||
[quo2.foundations.colors :as colors]))
|
||||
|
||||
(defn- get-icon-color [section]
|
||||
(if (= section :bottom)
|
||||
(defn- get-icon-color [danger?]
|
||||
(if danger?
|
||||
(colors/theme-colors colors/danger-50 colors/danger-60)
|
||||
(colors/theme-colors colors/neutral-50 colors/neutral-40)))
|
||||
|
||||
(defn action [section]
|
||||
(fn [{:keys [icon label sub-label right-icon on-press]}]
|
||||
[rn/touchable-opacity {:on-press on-press}
|
||||
[react/view {:style
|
||||
{:flex 1
|
||||
:height (if sub-label 56 47)
|
||||
:margin-horizontal 20
|
||||
:flex-direction :row}}
|
||||
[react/view {:style
|
||||
{:height 20
|
||||
:margin-top :auto
|
||||
:margin-bottom :auto
|
||||
:margin-right 12
|
||||
:width 20}}
|
||||
[icon/icon icon
|
||||
{:color (get-icon-color section)
|
||||
:size 20}]]
|
||||
[react/view
|
||||
{:style
|
||||
{:flex 1
|
||||
:justify-content :center}}
|
||||
(defn action [{:keys [icon
|
||||
label
|
||||
sub-label
|
||||
right-icon
|
||||
danger?
|
||||
on-press]}]
|
||||
[rn/touchable-opacity {:on-press on-press}
|
||||
[rn/view {:style
|
||||
{:height (if sub-label 56 47)
|
||||
:margin-horizontal 20
|
||||
:flex-direction :row}}
|
||||
[rn/view {:style
|
||||
{:height 20
|
||||
:margin-top :auto
|
||||
:margin-bottom :auto
|
||||
:margin-right 12
|
||||
:width 20}}
|
||||
[icon/icon icon
|
||||
{:color (get-icon-color danger?)
|
||||
:size 20}]]
|
||||
[rn/view
|
||||
{:style
|
||||
{:flex 1
|
||||
:justify-content :center}}
|
||||
[text/text
|
||||
{:size :paragraph-1
|
||||
:weight :medium
|
||||
:style {:color
|
||||
(when danger?
|
||||
(colors/theme-colors colors/danger-50 colors/danger-60))}}
|
||||
label]
|
||||
(when sub-label
|
||||
[text/text
|
||||
{:size :paragraph-1
|
||||
:weight :medium
|
||||
:style
|
||||
{:color (when (= section :bottom)
|
||||
(colors/theme-colors colors/danger-50 colors/danger-60))}}
|
||||
label]
|
||||
(when sub-label [text/text
|
||||
{:size :paragraph-2
|
||||
:style
|
||||
{:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}}
|
||||
sub-label])]
|
||||
(when right-icon
|
||||
[react/view {:style
|
||||
{:height 20
|
||||
:margin-top :auto
|
||||
:margin-bottom :auto
|
||||
:width 20}}
|
||||
[icon/icon right-icon
|
||||
{:color (get-icon-color section)
|
||||
:size 20}]])]]))
|
||||
{:size :paragraph-2
|
||||
:style {:color
|
||||
(colors/theme-colors colors/neutral-50 colors/neutral-40)}}
|
||||
sub-label])]
|
||||
(when right-icon
|
||||
[rn/view {:style
|
||||
{:height 20
|
||||
:margin-top :auto
|
||||
:margin-bottom :auto
|
||||
:width 20}}
|
||||
[icon/icon right-icon
|
||||
{:color (get-icon-color danger?)
|
||||
:size 20}]])]])
|
||||
|
||||
(defn action-drawer [{:keys [actions actions-with-consequence]}]
|
||||
(defn divider []
|
||||
[rn/view {:style {:border-top-width 1
|
||||
:border-top-color (colors/theme-colors colors/neutral-10 colors/neutral-80)
|
||||
:margin-top 8
|
||||
:margin-bottom 7
|
||||
:align-items :center
|
||||
:flex-direction :row}}])
|
||||
|
||||
(defn action-drawer [sections]
|
||||
[:<> {:style
|
||||
{:flex 1}}
|
||||
(map (action :top) actions)
|
||||
(when actions-with-consequence
|
||||
[:<>
|
||||
[rn/view {:style {:border-top-width 1
|
||||
:border-top-color (colors/theme-colors colors/neutral-10 colors/neutral-80)
|
||||
:margin-top 8
|
||||
:margin-bottom 7
|
||||
:align-items :center
|
||||
:flex-direction :row}}]
|
||||
(map (action :bottom) actions-with-consequence)])])
|
||||
(interpose
|
||||
[divider]
|
||||
(for [actions sections]
|
||||
(map action actions)))])
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
(ns quo2.components.dropdowns.dropdown
|
||||
(:require [quo.react-native :as rn]
|
||||
(:require [react-native.core :as rn]
|
||||
[quo2.components.icon :as icons]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.reanimated :as reanimated]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
(defn apply-anim [dd-height val]
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
(ns quo2.components.header
|
||||
(:require [oops.core :refer [oget]]
|
||||
[quo.animated :as animated]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[quo2.components.buttons.button :as button]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo.design-system.spacing :as spacing]
|
||||
[quo.react-native :as rn]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
(def header-height 56)
|
||||
@@ -61,15 +60,14 @@
|
||||
:right (max (:width left) (:width right))})))
|
||||
|
||||
(def header-actions-style
|
||||
(merge
|
||||
{:flex 1
|
||||
:flex-direction :row
|
||||
:align-items :center
|
||||
:justify-content :center}
|
||||
(:x-tiny spacing/padding-horizontal)))
|
||||
{:flex 1
|
||||
:flex-direction :row
|
||||
:align-items :center
|
||||
:justify-content :center
|
||||
:padding-horizontal 4})
|
||||
|
||||
(def header-action-placeholder
|
||||
{:width (:base spacing/spacing)})
|
||||
{:width 16})
|
||||
|
||||
(def element {:align-items :center
|
||||
:justify-content :center
|
||||
@@ -145,9 +143,9 @@
|
||||
border-bottom false}}]
|
||||
(let [status-bar-height (get insets :top 0)
|
||||
height (+ header-height status-bar-height)]
|
||||
[animated/view {:style (header-wrapper-style {:height height
|
||||
:background background
|
||||
:border-bottom border-bottom})}
|
||||
[reanimated/view {:style (header-wrapper-style {:height height
|
||||
:background background
|
||||
:border-bottom border-bottom})}
|
||||
[rn/view {:pointer-events :box-none
|
||||
:height status-bar-height}]
|
||||
[rn/view {:style (merge {:height header-height}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
(ns quo2.components.icon
|
||||
(:require
|
||||
[quo2.components.icons.icons :as icons]
|
||||
[status-im.ui.components.react :as react]
|
||||
[clojure.string :as string]
|
||||
[react-native.core :as rn]
|
||||
[quo2.components.icons.icons :as icons]
|
||||
[quo2.foundations.colors :as colors]))
|
||||
|
||||
(defn memo-icon-fn
|
||||
@@ -12,7 +12,7 @@
|
||||
:or {accessibility-label :icon}}]
|
||||
(let [size (or size 20)]
|
||||
^{:key icon-name}
|
||||
[react/image
|
||||
[rn/image
|
||||
{:style
|
||||
(merge {:width size
|
||||
:height size}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
(ns quo2.components.info.info-message
|
||||
(:require [quo.theme :as theme]
|
||||
[quo.react-native :as rn]
|
||||
(:require [quo2.theme :as theme]
|
||||
[react-native.core :as rn]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.components.icon :as quo2.icons]))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
(ns quo2.components.info.information-box
|
||||
(:require [quo.theme :as theme]
|
||||
[quo.react-native :as rn]
|
||||
(:require [quo2.theme :as theme]
|
||||
[react-native.core :as rn]
|
||||
[clojure.string :as string]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.components.icon :as quo2.icons]
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
(ns quo2.components.list-items.channel
|
||||
(:require [quo.react-native :as rn]
|
||||
(:require [react-native.core :as rn]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.components.counter.counter :as quo2.counter]
|
||||
[quo2.components.icon :as quo2.icons]
|
||||
[quo2.components.avatars.channel-avatar :as channel-avatar]
|
||||
[quo2.components.markdown.text :as quo2.text]
|
||||
[quo.theme :as theme]))
|
||||
[quo2.theme :as theme]))
|
||||
|
||||
(defn list-item [{:keys [name locked? mentions-count unread-messages?
|
||||
muted? is-active-channel? emoji channel-color]
|
||||
:or {channel-color colors/primary-50}}]
|
||||
:or {channel-color colors/primary-50}}]
|
||||
[rn/view {:style (merge {:height 48
|
||||
:display :flex
|
||||
:border-radius 12
|
||||
@@ -21,21 +21,19 @@
|
||||
:padding-right 12}
|
||||
(when is-active-channel?
|
||||
{:background-color (colors/theme-alpha channel-color 0.05 0.05)}))}
|
||||
[rn/view {:display :flex
|
||||
:flex-direction :row
|
||||
[rn/view {:display :flex
|
||||
:flex-direction :row
|
||||
:justify-content :flex-start
|
||||
:align-items :center}
|
||||
:align-items :center}
|
||||
[channel-avatar/channel-avatar
|
||||
{:big? true
|
||||
:locked? locked?
|
||||
:locked? locked?
|
||||
:emoji-background-color (colors/theme-alpha channel-color 0.1 0.1)
|
||||
:emoji emoji}]
|
||||
[quo2.text/text
|
||||
{:style (merge {:margin-left 12}
|
||||
(when
|
||||
(and (not locked?)
|
||||
muted?)
|
||||
{:color (if (theme/dark?) colors/neutral-60 colors/neutral-40)}))
|
||||
{:style (merge {:margin-left 12}
|
||||
(when (and (not locked?) muted?)
|
||||
{:color (if (theme/dark?) colors/neutral-60 colors/neutral-40)}))
|
||||
:weight :medium :size :paragraph-1} (str "# " name)]]
|
||||
[rn/view {:style {:height 20}}
|
||||
(when (and (not locked?)
|
||||
|
||||
@@ -1,43 +1,55 @@
|
||||
(ns quo2.components.list-items.menu-item
|
||||
(:require [quo.react-native :as rn]
|
||||
(:require [react-native.core :as rn]
|
||||
[quo2.foundations.colors :as colors :refer [theme-colors]]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.components.icon :as icons]))
|
||||
|
||||
(defn themes [type]
|
||||
(case type
|
||||
:main {:icon-color (theme-colors colors/neutral-50 colors/neutral-10)
|
||||
:background (theme-colors colors/white colors/neutral-90)
|
||||
:text-color (theme-colors colors/neutral-100 colors/white)}
|
||||
:danger {:icon-color (theme-colors colors/danger-50 colors/danger-60)
|
||||
:background (theme-colors colors/white colors/neutral-90)
|
||||
:text-color (theme-colors colors/danger-50 colors/danger-60)}))
|
||||
:main {:icon-color (theme-colors colors/neutral-50 colors/neutral-10)
|
||||
:background (theme-colors colors/white colors/neutral-90)
|
||||
:text-color (theme-colors colors/neutral-100 colors/white)}
|
||||
:danger {:icon-color (theme-colors colors/danger-50 colors/danger-60)
|
||||
:background (theme-colors colors/white colors/neutral-90)
|
||||
:text-color (theme-colors colors/danger-50 colors/danger-60)}))
|
||||
|
||||
(defn menu-item
|
||||
[{:keys [type title accessibility-label icon on-press]
|
||||
[{:keys [type title description accessibility-label icon on-press arrow?]
|
||||
:or {type :main}}]
|
||||
(let [{:keys [icon-color text-color background]} (themes type)]
|
||||
[rn/touchable-opacity
|
||||
(merge {:accessibility-label accessibility-label
|
||||
:style {:background-color background
|
||||
:height 48
|
||||
:flex-direction :row
|
||||
:align-items :center}}
|
||||
:style {:background-color background
|
||||
:height 48
|
||||
:flex-direction :row
|
||||
:align-items :center}}
|
||||
(when on-press
|
||||
{:on-press on-press}))
|
||||
[rn/view {:style {:flex-direction :row
|
||||
:flex-grow 0
|
||||
:flex-shrink 1
|
||||
:padding-horizontal 20}}
|
||||
[rn/view {:style {:width 20
|
||||
:height 20
|
||||
:align-items :center
|
||||
:justify-content :center
|
||||
:margin-right 12}}
|
||||
[rn/view {:style {:flex-direction :row
|
||||
:flex-grow 0
|
||||
:flex-shrink 1
|
||||
:padding-horizontal 20
|
||||
:align-items :center}}
|
||||
[rn/view {:style {:width 20
|
||||
:height 20
|
||||
:align-items :center
|
||||
:justify-content :center
|
||||
:margin-right 12}}
|
||||
[icons/icon icon {:color icon-color}]]
|
||||
[text/text {:weight :medium
|
||||
:style {:color text-color}
|
||||
:ellipsize-mode :tail
|
||||
:number-of-lines 1
|
||||
:size :paragraph-1}
|
||||
title]]]))
|
||||
[rn/view
|
||||
[text/text {:weight :medium
|
||||
:style {:color text-color}
|
||||
:ellipsize-mode :tail
|
||||
:number-of-lines 1
|
||||
:size :paragraph-1}
|
||||
title]
|
||||
(when description
|
||||
[text/text {:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}
|
||||
:ellipsize-mode :tail
|
||||
:number-of-lines 1
|
||||
:size :label}
|
||||
description])]]
|
||||
(when arrow?
|
||||
[rn/view {:style {:position :absolute
|
||||
:right 8}}
|
||||
[icons/icon :main-icons2/chevron-right {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}]])]))
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
(ns quo2.components.list-items.preview-list
|
||||
(:require [quo.react-native :as rn]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
(:require [react-native.core :as rn]
|
||||
[react-native.hole-view :as hole-view]
|
||||
[react-native.fast-image :as fast-image]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.components.icon :as quo2.icons]
|
||||
[status-im.ui.components.fast-image :as fast-image]
|
||||
[quo2.components.avatars.user-avatar :as user-avatar]
|
||||
[quo2.components.markdown.text :as quo2.text]))
|
||||
|
||||
@@ -42,13 +42,13 @@
|
||||
(defn list-item [index type size item list-size margin-left
|
||||
hole-size hole-radius hole-x hole-y border-radius]
|
||||
(let [last-item? (= index (- list-size 1))]
|
||||
[rn/hole-view {:style {:margin-left (if (= index 0) 0 margin-left)}
|
||||
:holes (if last-item? []
|
||||
[{:x hole-x
|
||||
:y hole-y
|
||||
:width hole-size
|
||||
:height hole-size
|
||||
:borderRadius hole-radius}])}
|
||||
[hole-view/hole-view {:style {:margin-left (if (= index 0) 0 margin-left)}
|
||||
:holes (if last-item? []
|
||||
[{:x hole-x
|
||||
:y hole-y
|
||||
:width hole-size
|
||||
:height hole-size
|
||||
:borderRadius hole-radius}])}
|
||||
[avatar item type size border-radius]]))
|
||||
|
||||
(defn get-overflow-color [transparent? transparent-color light-color dark-color override-theme]
|
||||
@@ -56,7 +56,7 @@
|
||||
transparent-color
|
||||
(colors/theme-colors light-color dark-color override-theme)))
|
||||
|
||||
(defn overflow-label [label size transparent? border-radius margin-left override-theme]
|
||||
(defn overflow-label [label size transparent? border-radius margin-left override-theme more-than-99-label]
|
||||
[rn/view {:style {:width size
|
||||
:height size
|
||||
:margin-left margin-left
|
||||
@@ -89,7 +89,7 @@
|
||||
;; If overflow label is below 100, show label as +label (ex. +30), else just show 99+
|
||||
(if (< label 100)
|
||||
(str "+" label)
|
||||
(i18n/label :counter-99-plus))])])
|
||||
more-than-99-label)])])
|
||||
|
||||
(defn border-type [type]
|
||||
(case type
|
||||
@@ -105,7 +105,7 @@
|
||||
:transparent? overflow-label transparent?}
|
||||
items preview list items (only 4 items is required for preview)
|
||||
"
|
||||
[{:keys [type size list-size transparent? override-theme]} items]
|
||||
[{:keys [type size list-size transparent? override-theme more-than-99-label]} items]
|
||||
(let [items-arr (into [] items)
|
||||
list-size (or list-size (count items))
|
||||
margin-left (get-in params [size :margin-left])
|
||||
@@ -120,4 +120,4 @@
|
||||
[list-item index type size (get items-arr index) list-size
|
||||
margin-left hole-size hole-radius hole-x hole-y border-radius])
|
||||
(when (> list-size 4)
|
||||
[overflow-label (- list-size 3) size transparent? border-radius margin-left override-theme])]))
|
||||
[overflow-label (- list-size 3) size transparent? border-radius margin-left override-theme more-than-99-label])]))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
(ns quo2.components.markdown.text
|
||||
(:require [quo.react-native :as rn]
|
||||
[quo.theme :as theme]
|
||||
(:require [react-native.core :as rn]
|
||||
[quo2.theme :as theme]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.foundations.typography :as typography]
|
||||
[reagent.core :as reagent]))
|
||||
@@ -12,6 +12,7 @@
|
||||
:semi-bold typography/font-semi-bold
|
||||
:bold typography/font-bold
|
||||
:monospace typography/monospace
|
||||
:code typography/code
|
||||
:inherit nil)
|
||||
(case (or size :paragraph-1)
|
||||
:label typography/label
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
(ns quo2.components.messages.gap
|
||||
(:require
|
||||
[oops.core :refer [oget]]
|
||||
[quo.react-native :as rn]
|
||||
[quo.theme :as theme]
|
||||
[react-native.core :as rn]
|
||||
[quo2.theme :as theme]
|
||||
[quo2.components.icon :as icon]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.ui.components.react :refer [pressable-class]]
|
||||
[status-im.utils.handlers :refer [>evt]]))
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
;;; helpers
|
||||
(def themes
|
||||
@@ -84,7 +81,7 @@
|
||||
:color (get-color :time)}} str])
|
||||
|
||||
(defn info-button [on-press]
|
||||
[pressable-class
|
||||
[rn/touchable-without-feedback
|
||||
{:on-press on-press}
|
||||
[icon/icon "message-gap-info" {:size 12 :no-color true :container-style {:padding 4}}]])
|
||||
|
||||
@@ -99,7 +96,7 @@
|
||||
[rn/image {:style {:flex 1} :source (get-image :circles) :resize-mode :repeat}]
|
||||
[circle]])
|
||||
|
||||
(defn body [timestamp-far timestamp-near chat-id gap-ids on-info-button-pressed]
|
||||
(defn body [timestamp-far timestamp-near on-info-button-pressed on-press warning-label]
|
||||
[rn/view {:flex 1}
|
||||
[rn/view
|
||||
{:flex-direction :row
|
||||
@@ -109,12 +106,10 @@
|
||||
[timestamp timestamp-far]
|
||||
(when on-info-button-pressed [info-button on-info-button-pressed])]
|
||||
|
||||
[pressable-class
|
||||
[rn/touchable-without-feedback
|
||||
{:style {:flex 1 :margin-top 16 :margin-bottom 20}
|
||||
:on-press #(when (and chat-id gap-ids)
|
||||
(>evt [:chat.ui/fill-gaps chat-id gap-ids]))}
|
||||
[text/text
|
||||
(i18n/label :messages-gap-warning)]]
|
||||
:on-press #(when on-press (on-press))}
|
||||
[text/text warning-label]]
|
||||
|
||||
[timestamp timestamp-near]])
|
||||
|
||||
@@ -124,10 +119,10 @@
|
||||
if `on-info-button-pressed` fn is provided, the info button will show up and is pressable"
|
||||
[{:keys [timestamp-far
|
||||
timestamp-near
|
||||
gap-ids
|
||||
chat-id
|
||||
on-info-button-pressed
|
||||
style]}]
|
||||
style
|
||||
on-press
|
||||
warning-label]}]
|
||||
(let [body-height (reagent/atom nil)]
|
||||
(fn []
|
||||
[rn/view
|
||||
@@ -145,6 +140,6 @@
|
||||
style)
|
||||
|
||||
[timeline]
|
||||
[body timestamp-far timestamp-near chat-id gap-ids on-info-button-pressed]]
|
||||
[body timestamp-far timestamp-near on-info-button-pressed on-press warning-label]]
|
||||
[vborder :left body-height]
|
||||
[vborder :right body-height]])))
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
(ns quo2.components.messages.system-message
|
||||
(:require [quo.react-native :as rn]
|
||||
[quo.theme :as theme]
|
||||
(:require [react-native.core :as rn]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[quo2.theme :as theme]
|
||||
[quo2.components.avatars.icon-avatar :as icon-avatar]
|
||||
[quo2.components.avatars.user-avatar :as user-avatar]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.reanimated :as ra]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.utils.core :as utils]))
|
||||
[utils :as utils]))
|
||||
|
||||
(def themes-landed {:pinned colors/primary-50-opa-5
|
||||
:added colors/primary-50-opa-5
|
||||
@@ -54,7 +53,7 @@
|
||||
|
||||
(defmulti sm-render :type)
|
||||
|
||||
(defmethod sm-render :deleted [{:keys [label timestamp-str]}]
|
||||
(defmethod sm-render :deleted [{:keys [label timestamp-str labels]}]
|
||||
[rn/view {:align-items :center
|
||||
:justify-content :space-between
|
||||
:flex 1
|
||||
@@ -67,10 +66,10 @@
|
||||
[text/text {:size :paragraph-2
|
||||
:style {:color (get-color :text)
|
||||
:margin-right 5}}
|
||||
(i18n/label (or label :message-deleted))]
|
||||
(or label (:message-deleted labels))]
|
||||
[sm-timestamp timestamp-str]]])
|
||||
|
||||
(defmethod sm-render :added [{:keys [state mentions timestamp-str]}]
|
||||
(defmethod sm-render :added [{:keys [state mentions timestamp-str labels]}]
|
||||
[rn/view {:align-items :center
|
||||
:flex-direction :row}
|
||||
[sm-icon {:icon :main-icons/add-user16
|
||||
@@ -84,14 +83,14 @@
|
||||
:style {:color (get-color :text)
|
||||
:margin-left 3
|
||||
:margin-right 3}}
|
||||
(i18n/label :added)]
|
||||
(:added labels)]
|
||||
[sm-user-avatar (:image (second mentions))]
|
||||
[text/text {:weight :semi-bold
|
||||
:size :paragraph-2}
|
||||
(:name (second mentions))]
|
||||
[sm-timestamp timestamp-str]])
|
||||
|
||||
(defmethod sm-render :pinned [{:keys [state pinned-by content timestamp-str]}]
|
||||
(defmethod sm-render :pinned [{:keys [state pinned-by content timestamp-str labels]}]
|
||||
[rn/view {:flex-direction :row
|
||||
:flex 1
|
||||
:align-items :center}
|
||||
@@ -110,7 +109,7 @@
|
||||
:margin-right 2}
|
||||
[text/text {:size :paragraph-2
|
||||
:style {:color (get-color :text)}}
|
||||
(i18n/label :pinned-a-message)]]
|
||||
(:pinned-a-message labels)]]
|
||||
[sm-timestamp timestamp-str]]
|
||||
[rn/view {:flex-direction :row}
|
||||
[rn/view {:flex-direction :row
|
||||
@@ -137,19 +136,19 @@
|
||||
(utils/truncate-str (:info content) 24)])]]]])
|
||||
|
||||
(defn system-message
|
||||
[{:keys [type style non-pressable? animate-landing?] :as message}]
|
||||
[{:keys [type style non-pressable? animate-landing? labels] :as message}]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [sv-color (ra/use-shared-value
|
||||
(let [sv-color (reanimated/use-shared-value
|
||||
(get-color :bg (if animate-landing? :landed :default) type))]
|
||||
(when animate-landing?
|
||||
(ra/animate-shared-value-with-delay
|
||||
(reanimated/animate-shared-value-with-delay
|
||||
sv-color (get-color :bg :default type) 0 :linear 1000))
|
||||
[ra/touchable-opacity
|
||||
[reanimated/touchable-opacity
|
||||
{:on-press #(when-not non-pressable?
|
||||
(ra/set-shared-value
|
||||
(reanimated/set-shared-value
|
||||
sv-color (get-color :bg :pressed type)))
|
||||
:style (ra/apply-animations-to-style
|
||||
:style (reanimated/apply-animations-to-style
|
||||
{:background-color sv-color}
|
||||
(merge
|
||||
{:flex-direction :row
|
||||
@@ -159,4 +158,4 @@
|
||||
:padding-horizontal 11
|
||||
:background-color sv-color}
|
||||
style))}
|
||||
[sm-render message]]))])
|
||||
[sm-render message labels]]))])
|
||||
|
||||
@@ -1,71 +1,78 @@
|
||||
(ns quo2.components.navigation.bottom-nav-tab
|
||||
(:require [quo.react-native :as rn]
|
||||
[reagent.core :as reagent]
|
||||
(:require [react-native.core :as rn]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[react-native.hole-view :as hole-view]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.components.icon :as icon]
|
||||
[quo2.components.icons.icons :as icons]
|
||||
[quo2.components.counter.counter :as counter]))
|
||||
|
||||
(defn toggle-background-color [background-color press-out? pass-through?]
|
||||
(let [color (cond
|
||||
press-out? nil
|
||||
pass-through? colors/white-opa-5
|
||||
:else colors/neutral-70)]
|
||||
(reset! background-color color)))
|
||||
(reanimated/set-shared-value
|
||||
background-color
|
||||
(cond
|
||||
press-out? "transparent"
|
||||
pass-through? colors/white-opa-5
|
||||
:else colors/neutral-70)))
|
||||
|
||||
(defn bottom-nav-tab
|
||||
"[bottom-nav-tab opts]
|
||||
opts
|
||||
{:icon :main-icons2/communities
|
||||
:selected? true/false
|
||||
:new-notifications? true/false
|
||||
:notification-indicator :unread-dot/:counter
|
||||
:counter-label number
|
||||
:on-press bottom-tab on-press function
|
||||
:pass-through? true/false
|
||||
:icon-color-anim reanimated shared value
|
||||
"
|
||||
[_]
|
||||
(let [background-color (reagent/atom nil)]
|
||||
(fn [{:keys [icon selected? new-notifications? notification-indicator counter-label on-press pass-through?]}]
|
||||
[rn/touchable-without-feedback
|
||||
{:on-press on-press
|
||||
:on-press-in #(toggle-background-color background-color false pass-through?)
|
||||
:on-press-out #(toggle-background-color background-color true pass-through?)}
|
||||
[rn/view {:style {:width 90
|
||||
:height 40
|
||||
:background-color @background-color
|
||||
:border-radius 10}}
|
||||
[rn/hole-view {:style {:padding-left 33
|
||||
:padding-top 8}
|
||||
:key new-notifications? ;; Key is required to force removal of holes
|
||||
:holes (cond
|
||||
(not new-notifications?) ;; No new notifications, remove holes
|
||||
[]
|
||||
[{:keys [icon new-notifications? notification-indicator counter-label
|
||||
on-press pass-through? icon-color-anim accessibility-label]}]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [icon-animated-style (reanimated/apply-animations-to-style
|
||||
{:tint-color icon-color-anim}
|
||||
{:width 24
|
||||
:height 24})
|
||||
background-color (reanimated/use-shared-value "transparent")
|
||||
background-animated-style (reanimated/apply-animations-to-style
|
||||
{:background-color background-color}
|
||||
{:width 90
|
||||
:height 40
|
||||
:border-radius 10})]
|
||||
[rn/touchable-without-feedback
|
||||
{:on-press on-press
|
||||
:on-press-in #(toggle-background-color background-color false pass-through?)
|
||||
:on-press-out #(toggle-background-color background-color true pass-through?)
|
||||
:accessibility-label accessibility-label}
|
||||
[reanimated/view {:style background-animated-style}
|
||||
[hole-view/hole-view {:style {:padding-left 33
|
||||
:padding-top 8}
|
||||
:key new-notifications? ;; Key is required to force removal of holes
|
||||
:holes (cond
|
||||
(not new-notifications?) ;; No new notifications, remove holes
|
||||
[]
|
||||
|
||||
(= notification-indicator :unread-dot)
|
||||
[{:x 50 :y 5 :width 10 :height 10 :borderRadius 5}]
|
||||
(= notification-indicator :unread-dot)
|
||||
[{:x 50 :y 5 :width 10 :height 10 :borderRadius 5}]
|
||||
|
||||
:else
|
||||
[{:x 47 :y 1 :width 18 :height 18 :borderRadius 7}])}
|
||||
[icon/icon
|
||||
icon
|
||||
{:size 24
|
||||
:color (cond
|
||||
selected? colors/white
|
||||
pass-through? colors/white-opa-40
|
||||
:else colors/neutral-50)}]]
|
||||
(when new-notifications?
|
||||
(if (= notification-indicator :counter)
|
||||
[counter/counter {:outline false
|
||||
:override-text-color colors/white
|
||||
:override-bg-color colors/primary-50
|
||||
:style {:position :absolute
|
||||
:left 48
|
||||
:top 2}}
|
||||
counter-label]
|
||||
[rn/view {:style {:width 8
|
||||
:height 8
|
||||
:border-radius 4
|
||||
:top 6
|
||||
:left 51
|
||||
:position :absolute
|
||||
:background-color colors/primary-50}}]))]])))
|
||||
:else
|
||||
[{:x 47 :y 1 :width 18 :height 18 :borderRadius 7}])}
|
||||
[reanimated/image
|
||||
{:style icon-animated-style
|
||||
:source (icons/icon-source (keyword (str icon 24)))}]]
|
||||
(when new-notifications?
|
||||
(if (= notification-indicator :counter)
|
||||
[counter/counter {:outline false
|
||||
:override-text-color colors/white
|
||||
:override-bg-color colors/primary-50
|
||||
:style {:position :absolute
|
||||
:left 48
|
||||
:top 2}}
|
||||
counter-label]
|
||||
[rn/view {:style {:width 8
|
||||
:height 8
|
||||
:border-radius 4
|
||||
:top 6
|
||||
:left 51
|
||||
:position :absolute
|
||||
:background-color colors/primary-50}}]))]]))])
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
(ns quo2.components.navigation.floating-shell-button
|
||||
(:require [quo.react-native :as rn]
|
||||
(:require [react-native.core :as rn]
|
||||
[quo2.components.buttons.dynamic-button :as dynamic-button]))
|
||||
|
||||
(defn dynamic-button-view [type dynamic-buttons style]
|
||||
(when-let [{:keys [count on-press customization-color]} (get dynamic-buttons type)]
|
||||
(when-let [{:keys [count on-press customization-color label]} (get dynamic-buttons type)]
|
||||
[dynamic-button/dynamic-button
|
||||
{:type type
|
||||
:label label
|
||||
:on-press on-press
|
||||
:count count
|
||||
:style style
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
(ns quo2.components.navigation.page-nav
|
||||
(:require [quo.react-native :as rn]
|
||||
(:require [react-native.core :as rn]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.components.icon :as icons]
|
||||
[status-im.utils.dimensions :as dimensions]
|
||||
[clojure.string :as string]
|
||||
[quo2.components.markdown.text :as text]))
|
||||
[quo2.components.markdown.text :as text]
|
||||
[clojure.string :as string]))
|
||||
|
||||
(def ^:private centrify-style
|
||||
{:display :flex
|
||||
@@ -185,8 +184,7 @@
|
||||
mid-section
|
||||
left-section
|
||||
right-section-buttons]}]
|
||||
(let [{:keys [height width]} (dimensions/window)
|
||||
put-middle-section-on-left? (or align-mid?
|
||||
(let [put-middle-section-on-left? (or align-mid?
|
||||
(> (count right-section-buttons) 1))
|
||||
mid-section-props
|
||||
{:type (:type mid-section)
|
||||
@@ -197,15 +195,12 @@
|
||||
:right-icon (:right-icon mid-section)
|
||||
:icon (:icon mid-section)
|
||||
:left-icon (:left-icon mid-section)}]
|
||||
[rn/view {:style (cond->
|
||||
{:display :flex
|
||||
:flex-direction :row
|
||||
:width width
|
||||
:height (* 0.0497 height)
|
||||
;; iPhone 11 Pro's height in Figma divided by Component height 56/1125
|
||||
:align-items :center
|
||||
:padding-horizontal 20
|
||||
:justify-content :space-between}
|
||||
[rn/view {:style (cond-> {:display :flex
|
||||
:flex-direction :row
|
||||
;; iPhone 11 Pro's height in Figma divided by Component height 56/1125
|
||||
:align-items :center
|
||||
:padding-horizontal 20
|
||||
:justify-content :space-between}
|
||||
page-nav-background-uri (assoc :background-color page-nav-color)
|
||||
page-nav-color (assoc :background page-nav-background-uri))}
|
||||
[rn/view {:style {:flex 1
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
(ns quo2.components.navigation.top-nav
|
||||
(:require [quo.react-native :as rn]
|
||||
(:require [react-native.core :as rn]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.components.counter.counter :as counter]
|
||||
[quo2.components.buttons.button :as quo2.button]
|
||||
[quo2.components.avatars.user-avatar :as user-avatar]))
|
||||
[quo2.components.avatars.user-avatar :as user-avatar]
|
||||
[react-native.hole-view :as hole-view]))
|
||||
|
||||
(defn- get-button-common-props [type]
|
||||
(let [default? (= type :default)
|
||||
@@ -44,8 +45,7 @@
|
||||
open-scanner show-qr open-activity-center style avatar counter-label]}]
|
||||
(let [button-common-props (get-button-common-props type)]
|
||||
[rn/view {:style (merge
|
||||
{:height 56
|
||||
:flex 1}
|
||||
{:height 56}
|
||||
style)}
|
||||
;; Left Section
|
||||
[rn/touchable-without-feedback {:on-press open-profile}
|
||||
@@ -68,16 +68,16 @@
|
||||
[base-button :main-icons2/qr-code show-qr :show-qr-button button-common-props]
|
||||
[rn/view ;; Keep view instead of "[:<>" to make sure relative
|
||||
;; position is calculated from this view instead of its parent
|
||||
[rn/hole-view {:key new-notifications? ;; Key is required to force removal of holes
|
||||
:holes (cond
|
||||
(not new-notifications?) ;; No new notifications, remove holes
|
||||
[]
|
||||
[hole-view/hole-view {:key new-notifications? ;; Key is required to force removal of holes
|
||||
:holes (cond
|
||||
(not new-notifications?) ;; No new notifications, remove holes
|
||||
[]
|
||||
|
||||
(= notification-indicator :unread-dot)
|
||||
[{:x 37 :y -3 :width 10 :height 10 :borderRadius 5}]
|
||||
(= notification-indicator :unread-dot)
|
||||
[{:x 37 :y -3 :width 10 :height 10 :borderRadius 5}]
|
||||
|
||||
:else
|
||||
[{:x 33 :y -7 :width 18 :height 18 :borderRadius 7}])}
|
||||
:else
|
||||
[{:x 33 :y -7 :width 18 :height 18 :borderRadius 7}])}
|
||||
[base-button :main-icons2/activity-center open-activity-center
|
||||
:open-activity-center-button button-common-props]]
|
||||
(when new-notifications?
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
(ns quo2.components.notifications.activity-logs
|
||||
(:require [quo.react-native :as rn]
|
||||
(:require [react-native.core :as rn]
|
||||
[quo2.components.buttons.button :as button]
|
||||
[quo2.components.icon :as icon]
|
||||
[quo2.components.markdown.text :as text]
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
(ns quo2.components.notifications.info-count
|
||||
(:require [react-native.core :as rn]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.foundations.typography :as typography]))
|
||||
|
||||
(defn info-count [count style]
|
||||
(when (> count 0)
|
||||
[rn/view {:style (merge {:width 16
|
||||
:height 16
|
||||
:position :absolute
|
||||
:right 22
|
||||
:border-radius 6
|
||||
:background-color (colors/theme-colors colors/primary-50 colors/primary-60)}
|
||||
style)}
|
||||
[rn/text {:style (merge typography/font-medium typography/label {:color colors/white :text-align :center})} count]]))
|
||||
@@ -0,0 +1,12 @@
|
||||
(ns quo2.components.notifications.notification-dot
|
||||
(:require [react-native.core :as rn]
|
||||
[quo2.foundations.colors :as colors]))
|
||||
|
||||
(defn notification-dot [style]
|
||||
[rn/view {:style (merge {:background-color (colors/theme-colors colors/primary-50 colors/primary-60)
|
||||
:width 8
|
||||
:height 8
|
||||
:border-radius 4
|
||||
:position :absolute
|
||||
:z-index 1}
|
||||
style)}])
|
||||
@@ -1,7 +1,7 @@
|
||||
(ns quo2.components.reactions.reaction
|
||||
(:require [quo2.components.markdown.text :as quo2.text]
|
||||
[quo.react-native :as rn]
|
||||
[quo.theme :as theme]
|
||||
[react-native.core :as rn]
|
||||
[quo2.theme :as theme]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.components.icon :as icons]))
|
||||
|
||||
@@ -16,14 +16,16 @@
|
||||
(defn open-reactions-menu
|
||||
[{:keys [on-press]}]
|
||||
(let [dark? (theme/dark?)]
|
||||
[rn/touchable-opacity {:on-press on-press
|
||||
:style (merge reaction-styling
|
||||
[rn/touchable-opacity
|
||||
{:on-press on-press
|
||||
:accessibility-label :emoji-reaction-add
|
||||
:style (merge reaction-styling
|
||||
{:padding-horizontal 9
|
||||
:border-width 1
|
||||
:margin-top 5
|
||||
:border-color (if dark?
|
||||
colors/neutral-70
|
||||
colors/neutral-30)})}
|
||||
:border-width 1
|
||||
:margin-top 5
|
||||
:border-color (if dark?
|
||||
colors/neutral-70
|
||||
colors/neutral-30)})}
|
||||
[icons/icon :main-icons2/add
|
||||
{:size 20
|
||||
:color (if dark?
|
||||
@@ -32,26 +34,30 @@
|
||||
|
||||
(defn reaction
|
||||
"Add your emoji as a param here"
|
||||
[{:keys [emoji clicks neutral? on-press]}]
|
||||
[{:keys [emoji clicks neutral? on-press accessibility-label]}]
|
||||
(let [dark? (theme/dark?)
|
||||
text-color (if dark? colors/white
|
||||
colors/neutral-100)
|
||||
numeric-value (int clicks)
|
||||
clicks-positive? (pos? numeric-value)]
|
||||
[rn/touchable-opacity {:on-press on-press
|
||||
:style (merge reaction-styling
|
||||
(cond-> {:background-color
|
||||
(if dark?
|
||||
(if neutral?
|
||||
colors/neutral-70
|
||||
:transparent)
|
||||
(if neutral?
|
||||
colors/neutral-30
|
||||
:transparent))}
|
||||
(and dark? (not neutral?)) (assoc :border-color colors/neutral-70
|
||||
:border-width 1)
|
||||
(and (not dark?) (not neutral?)) (assoc :border-color colors/neutral-30
|
||||
:border-width 1)))}
|
||||
[rn/touchable-opacity
|
||||
{:on-press on-press
|
||||
:accessibility-label accessibility-label
|
||||
:style (merge reaction-styling
|
||||
(cond-> {:background-color
|
||||
(if dark?
|
||||
(if neutral?
|
||||
colors/neutral-70
|
||||
:transparent)
|
||||
(if neutral?
|
||||
colors/neutral-30
|
||||
:transparent))}
|
||||
(and dark? (not neutral?))
|
||||
(assoc :border-color colors/neutral-70
|
||||
:border-width 1)
|
||||
(and (not dark?) (not neutral?))
|
||||
(assoc :border-color colors/neutral-30
|
||||
:border-width 1)))}
|
||||
[icons/icon emoji {:no-color true
|
||||
:size 16}]
|
||||
[quo2.text/text {:size :paragraph-2
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
(ns quo2.components.selectors.disclaimer
|
||||
(:require [quo.react-native :as rn]
|
||||
(:require [react-native.core :as rn]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.components.selectors.selectors :as selectors]))
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
(ns quo2.components.selectors.selectors
|
||||
(:require [quo.react-native :as rn]
|
||||
(:require [react-native.core :as rn]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo.theme :as theme]
|
||||
[quo2.theme :as theme]
|
||||
[reagent.core :as reagent]
|
||||
[quo2.components.icon :as icons]))
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
[rn/view {:style
|
||||
{:height 20
|
||||
:width 20}}
|
||||
[icons/icon :main-icons2/check
|
||||
[icons/icon :main-icons2/check-small
|
||||
{:size 20
|
||||
:color (colors/theme-colors
|
||||
(colors/alpha colors/neutral-100 (if disabled? 0.3 1))
|
||||
@@ -53,8 +53,8 @@
|
||||
[rn/view
|
||||
{:style (merge
|
||||
container-style
|
||||
{:height 21
|
||||
:width 21})}
|
||||
{:height 20
|
||||
:width 20})}
|
||||
[rn/view
|
||||
{:style {:flex 1
|
||||
:border-radius 6
|
||||
@@ -72,9 +72,9 @@
|
||||
[rn/view {:style
|
||||
{:height 20
|
||||
:width 20}}
|
||||
[icons/icon :main-icons2/check
|
||||
[icons/icon :main-icons2/check-small
|
||||
{:size 20
|
||||
:color (colors/alpha colors/white (if disabled? 0.3 1))}]])]]])))
|
||||
:color colors/white}]])]]])))
|
||||
|
||||
(defn radio [{:keys [default-checked?]}]
|
||||
(let [checked? (reagent/atom (or default-checked? false))]
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
(ns quo2.components.separator
|
||||
(:require [quo.react-native :as react]
|
||||
(:require [react-native.core :as rn]
|
||||
[quo2.foundations.colors :as quo2.colors]))
|
||||
|
||||
(defn separator [{:keys [style]}]
|
||||
[react/view
|
||||
[rn/view
|
||||
{:style
|
||||
(merge
|
||||
style
|
||||
{:background-color (quo2.colors/theme-colors
|
||||
quo2.colors/neutral-30
|
||||
quo2.colors/neutral-10
|
||||
quo2.colors/neutral-80)
|
||||
:align-self :stretch
|
||||
:height 1})}])
|
||||
@@ -1,7 +1,7 @@
|
||||
(ns quo2.components.tabs.account-selector
|
||||
(:require
|
||||
[quo.theme :as theme]
|
||||
[quo.react-native :as rn]
|
||||
[quo2.theme :as theme]
|
||||
[react-native.core :as rn]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.components.markdown.text :as quo2]))
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
(ns quo2.components.tabs.segmented-tab
|
||||
(:require [reagent.core :as reagent]
|
||||
[quo.react-native :as rn]
|
||||
[react-native.core :as rn]
|
||||
[quo2.components.tabs.tab :as tab]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo.theme :as theme]))
|
||||
[quo2.theme :as theme]))
|
||||
|
||||
(def themes {:light {:background-color colors/neutral-20}
|
||||
:dark {:background-color colors/neutral-80}})
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
(ns quo2.components.tabs.tab
|
||||
(:require [quo2.foundations.colors :as colors]
|
||||
[quo.react-native :as rn]
|
||||
[quo.theme :as theme]
|
||||
[status-im.ui.components.icons.icons :as icons]
|
||||
[quo2.components.markdown.text :as text]))
|
||||
[react-native.core :as rn]
|
||||
[quo2.theme :as theme]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.components.icon :as icons]))
|
||||
|
||||
(def themes {:light {:default {:background-color colors/neutral-20
|
||||
:icon-color colors/neutral-50
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
(ns quo2.components.tabs.tabs
|
||||
(:require [oops.core :refer [oget]]
|
||||
[quo.react-native :as rn]
|
||||
[react-native.core :as rn]
|
||||
[quo2.components.tabs.tab :as tab]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.utils.core :as utils]
|
||||
[status-im.utils.number :as number-utils]))
|
||||
[utils :as utils]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.components.notifications.notification-dot :refer [notification-dot]]
|
||||
[react-native.masked-view :as masked-view]
|
||||
[react-native.linear-gradient :as linear-gradient]))
|
||||
|
||||
(def default-tab-size 32)
|
||||
|
||||
@@ -14,17 +16,30 @@
|
||||
(fn [{:keys [data size] :or {size default-tab-size}}]
|
||||
[rn/view (merge {:flex-direction :row} style)
|
||||
(doall
|
||||
(for [{:keys [label id]} data]
|
||||
(for [{:keys [label id new-info accessibility-label]} data]
|
||||
^{:key id}
|
||||
[rn/view {:style {:margin-right (if (= size default-tab-size) 12 8)}}
|
||||
(when new-info
|
||||
[rn/view {:position :absolute
|
||||
:z-index 1
|
||||
:right -2
|
||||
:top -2
|
||||
:width 10
|
||||
:height 10
|
||||
:border-radius 5
|
||||
:justify-content :center
|
||||
:align-items :center
|
||||
:background-color (colors/theme-colors colors/neutral-5 colors/neutral-95)}
|
||||
[notification-dot]])
|
||||
[tab/tab
|
||||
{:id id
|
||||
:size size
|
||||
:active (= id @active-tab-id)
|
||||
:on-press (fn []
|
||||
(reset! active-tab-id id)
|
||||
(when on-change
|
||||
(on-change id)))}
|
||||
{:id id
|
||||
:size size
|
||||
:accessibility-label accessibility-label
|
||||
:active (= id @active-tab-id)
|
||||
:on-press (fn []
|
||||
(reset! active-tab-id id)
|
||||
(when on-change
|
||||
(on-change id)))}
|
||||
label]]))])))
|
||||
|
||||
(defn- calculate-fade-end-percentage
|
||||
@@ -35,7 +50,7 @@
|
||||
;; Truncate to avoid unnecessary rendering.
|
||||
(if (> fade-percentage 0.99)
|
||||
0.99
|
||||
(number-utils/naive-round fade-percentage 2))))
|
||||
(utils/naive-round fade-percentage 2))))
|
||||
|
||||
(defn scrollable-tabs
|
||||
"Just like the component `tabs`, displays horizontally scrollable tabs with
|
||||
@@ -83,15 +98,17 @@
|
||||
size default-tab-size}
|
||||
:as props}]
|
||||
(let [maybe-mask-wrapper (if fade-end?
|
||||
[react/masked-view
|
||||
{:mask-element (reagent/as-element
|
||||
[react/linear-gradient {:colors [:black :transparent]
|
||||
:locations [(get @fading :fade-end-percentage) 1]
|
||||
:start {:x 0 :y 0}
|
||||
:end {:x 1 :y 0}
|
||||
:pointer-events :none
|
||||
:style {:width "100%"
|
||||
:height "100%"}}])}]
|
||||
[masked-view/masked-view
|
||||
{:mask-element
|
||||
(reagent/as-element
|
||||
[linear-gradient/linear-gradient
|
||||
{:colors [:black :transparent]
|
||||
:locations [(get @fading :fade-end-percentage) 1]
|
||||
:start {:x 0 :y 0}
|
||||
:end {:x 1 :y 0}
|
||||
:pointer-events :none
|
||||
:style {:width "100%"
|
||||
:height "100%"}}])}]
|
||||
[:<>])]
|
||||
(conj maybe-mask-wrapper
|
||||
[rn/flat-list
|
||||
@@ -132,15 +149,15 @@
|
||||
[tab/tab {:id id
|
||||
:size size
|
||||
:override-theme override-theme
|
||||
:blur? blur?
|
||||
:active (= id @active-tab-id)
|
||||
:on-press (fn [id]
|
||||
(reset! active-tab-id id)
|
||||
(when scroll-on-press?
|
||||
(.scrollToIndex @flat-list-ref
|
||||
#js {:animated true
|
||||
:index index
|
||||
:viewPosition 0.5}))
|
||||
(when on-change
|
||||
(on-change id)))}
|
||||
:blur? blur?
|
||||
:active (= id @active-tab-id)
|
||||
:on-press (fn [id]
|
||||
(reset! active-tab-id id)
|
||||
(when scroll-on-press?
|
||||
(.scrollToIndex @flat-list-ref
|
||||
#js {:animated true
|
||||
:index index
|
||||
:viewPosition 0.5}))
|
||||
(when on-change
|
||||
(on-change id)))}
|
||||
label]])})])))))
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
(ns quo2.components.tags.base-tag
|
||||
(:require
|
||||
[quo.react-native :as rn]))
|
||||
(:require [react-native.core :as rn]))
|
||||
|
||||
(defn style-container [size disabled border-color border-width background-color label type]
|
||||
(merge {:height size
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
(ns quo2.components.tags.context-tags
|
||||
(:require [quo2.foundations.colors :as colors]
|
||||
[quo.theme :as quo.theme]
|
||||
[quo2.theme :as quo2.theme]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.components.avatars.group-avatar :as group-avatar]
|
||||
[quo.react-native :as rn]))
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(defn padding-left-for-type [type]
|
||||
(case type
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
(defn base-tag [_ _]
|
||||
(fn [{:keys [override-theme style]} & children]
|
||||
(let [theme (or override-theme (quo.theme/get-theme))]
|
||||
(let [theme (or override-theme (quo2.theme/get-theme))]
|
||||
(into
|
||||
[rn/view
|
||||
(merge
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
(ns quo2.components.tags.permission-tag
|
||||
(:require [status-im.ui.components.react :as react]
|
||||
(:require [react-native.core :as rn]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.components.icon :as icons]
|
||||
[quo2.components.tags.base-tag :as base-tag]
|
||||
[status-im.i18n.i18n :as i18n]))
|
||||
[quo2.components.tags.base-tag :as base-tag]))
|
||||
|
||||
(defn outer-resource-container [size background-color]
|
||||
{:background-color background-color
|
||||
@@ -28,8 +27,8 @@
|
||||
(defn extra-count [total-group-count selected-count size background-color]
|
||||
(let [extra-group-count (- total-group-count selected-count)]
|
||||
(when (> extra-group-count 0)
|
||||
[react/view (outer-resource-container size background-color)
|
||||
[react/view (extra-count-styles size)
|
||||
[rn/view (outer-resource-container size background-color)
|
||||
[rn/view (extra-count-styles size)
|
||||
(if (< extra-group-count 4)
|
||||
[text/text {:size :label
|
||||
:style {:align-items :center
|
||||
@@ -58,20 +57,20 @@
|
||||
:or {size 24}}]
|
||||
(let [tokens-count (count group)
|
||||
selected-tokens (take (selected-token-count group) group)]
|
||||
[react/view {:flex-direction :row
|
||||
:align-items :center}
|
||||
[rn/view {:flex-direction :row
|
||||
:align-items :center}
|
||||
(for [{:keys [token-icon]} selected-tokens]
|
||||
^{:key token-icon}
|
||||
[react/view {:flex-direction :row}
|
||||
[react/view (outer-resource-container size background-color)
|
||||
[react/image {:source token-icon
|
||||
:style {:height (case size 32 28 24 20)
|
||||
:width (case size 32 28 24 20)
|
||||
:border-radius size}}]]])
|
||||
[rn/view {:flex-direction :row}
|
||||
[rn/view (outer-resource-container size background-color)
|
||||
[rn/image {:source token-icon
|
||||
:style {:height (case size 32 28 24 20)
|
||||
:width (case size 32 28 24 20)
|
||||
:border-radius size}}]]])
|
||||
|
||||
[extra-count tokens-count (count selected-tokens) size]
|
||||
(when-not last-group
|
||||
[react/view {:align-items :center}
|
||||
[rn/view {:align-items :center}
|
||||
[text/text {:weight :medium
|
||||
:size (case size 32 :paragraph-2 24 :label)
|
||||
:style {:color (colors/theme-colors
|
||||
@@ -80,7 +79,7 @@
|
||||
:padding-left 4
|
||||
:text-transform :lowercase
|
||||
:padding-right (case size 32 16 24 12)}}
|
||||
(i18n/label :t/or)]])])))
|
||||
"or"]])])))
|
||||
|
||||
(defn selected-group-count [tokens]
|
||||
(cond
|
||||
@@ -94,8 +93,8 @@
|
||||
:or {size 24}}]
|
||||
(let [selected-groups (take (selected-group-count tokens) tokens)
|
||||
last-group-id ((last selected-groups) :id)]
|
||||
[react/view {:flex-direction :row
|
||||
:align-items :center}
|
||||
[rn/view {:flex-direction :row
|
||||
:align-items :center}
|
||||
(for [{:keys [id group]} selected-groups]
|
||||
^{:key id}
|
||||
[token-group {:group group
|
||||
@@ -110,11 +109,11 @@
|
||||
[base-tag/base-tag {:background-color background-color
|
||||
:size size
|
||||
:type :permission}
|
||||
[react/view {:flex-direction :row
|
||||
:align-items :center
|
||||
:justify-content :flex-end}
|
||||
[react/view {:padding-left (case 32 8 24 6)
|
||||
:padding-right (case size 32 16 24 12)}
|
||||
[rn/view {:flex-direction :row
|
||||
:align-items :center
|
||||
:justify-content :flex-end}
|
||||
[rn/view {:padding-left (case 32 8 24 6)
|
||||
:padding-right (case size 32 16 24 12)}
|
||||
[icons/icon (if locked :main-icons2/locked
|
||||
:main-icons2/unlocked)
|
||||
{:resize-mode :center
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
(ns quo2.components.tags.status-tags
|
||||
(:require [quo.react-native :as rn]
|
||||
[quo.theme :as quo.theme]
|
||||
(:require [react-native.core :as rn]
|
||||
[quo2.theme :as quo2.theme]
|
||||
[quo2.components.icon :as icon]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[status-im.i18n.i18n :as i18n]))
|
||||
[quo2.foundations.colors :as colors]))
|
||||
|
||||
(def default-container-style
|
||||
{:border-radius 20
|
||||
@@ -54,7 +53,7 @@
|
||||
:background-color colors/success-50-opa-10
|
||||
:icon :verified
|
||||
:border-color colors/success-50-opa-20
|
||||
:label (or label (i18n/label :positive))
|
||||
:label label
|
||||
:text-color (if (= theme :light) colors/success-50
|
||||
colors/success-60)}])
|
||||
|
||||
@@ -64,7 +63,7 @@
|
||||
:icon :untrustworthy
|
||||
:background-color colors/danger-50-opa-10
|
||||
:border-color colors/danger-50-opa-20
|
||||
:label (or label (i18n/label :negative))
|
||||
:label label
|
||||
:text-color (if (= theme :light)
|
||||
colors/danger-50
|
||||
colors/danger-60)}])
|
||||
@@ -73,7 +72,7 @@
|
||||
[size theme label]
|
||||
[base-tag {:size size
|
||||
:icon :pending
|
||||
:label (or label (i18n/label :pending))
|
||||
:label label
|
||||
:background-color (if (= theme :light)
|
||||
colors/neutral-10
|
||||
colors/neutral-80)
|
||||
@@ -91,5 +90,5 @@
|
||||
nil)]
|
||||
[status-component
|
||||
size
|
||||
(or override-theme (quo.theme/get-theme))
|
||||
(or override-theme (quo2.theme/get-theme))
|
||||
label])))
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
(ns quo2.components.tags.tag
|
||||
(:require [quo2.foundations.colors :as colors]
|
||||
[quo.theme :as theme]
|
||||
[quo.react-native :as rn]
|
||||
[quo2.theme :as theme]
|
||||
[react-native.core :as rn]
|
||||
[quo2.components.icon :as icons]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.components.tags.base-tag :as base-tag]))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
(ns quo2.components.tags.tags
|
||||
(:require [reagent.core :as reagent]
|
||||
[quo.react-native :as rn]
|
||||
[react-native.core :as rn]
|
||||
[quo2.components.tags.tag :as tag]))
|
||||
|
||||
(defn tags [{:keys [default-active on-change]}]
|
||||
@@ -25,5 +25,4 @@
|
||||
:main-icons2/placeholder
|
||||
resource)
|
||||
:on-press #(do (reset! active-tab-id %)
|
||||
(when on-change (on-change %)))})
|
||||
tag-label]])]))))
|
||||
(when on-change (on-change %)))})]])]))))
|
||||
@@ -1,9 +1,9 @@
|
||||
(ns quo2.components.tags.token-tag
|
||||
(:require [quo2.foundations.colors :as colors]
|
||||
[quo.react-native :as rn]
|
||||
[quo.theme :as theme]
|
||||
[react-native.core :as rn]
|
||||
[quo2.theme :as theme]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[status-im.ui.components.icons.icons :as icons]))
|
||||
[quo2.components.icon :as icons]))
|
||||
|
||||
(def themes {:light {:background-color colors/neutral-20}
|
||||
:dark {:background-color colors/neutral-80}})
|
||||
@@ -76,23 +76,19 @@
|
||||
:or
|
||||
{size :small border-color (colors/custom-color-by-theme :purple 50 60)}}]
|
||||
|
||||
[tag {:size size
|
||||
[tag {:size size
|
||||
:token-img-src token-img-src
|
||||
:border-color (when is-required border-color)
|
||||
:border-color (when is-required border-color)
|
||||
:overlay
|
||||
(when (or is-required is-purchasable)
|
||||
[rn/view
|
||||
{:style (merge icon-container-styles
|
||||
{:width 15.5
|
||||
:height 15.5
|
||||
:background-color border-color
|
||||
:border-color (if (= (theme/get-theme) :dark) colors/neutral-100 colors/white)
|
||||
:border-width 1
|
||||
:right (get-value-from-size size -3.75 -5.75)
|
||||
:bottom (get-value-from-size size (- 32 7.75 4) (- 24 7.75 2)) ; (- height (icon-height/2) spacing)
|
||||
})}
|
||||
[icons/icon (if is-required :main-icons2/required-checkmark12 :main-icons2/purchasable12)
|
||||
{:background-color border-color
|
||||
:border-color (if (= (theme/get-theme) :dark) colors/neutral-100 colors/white)
|
||||
:border-width 1
|
||||
:right (get-value-from-size size -3.75 -5.75)
|
||||
:bottom (get-value-from-size size (- 32 7.75 4) (- 24 7.75 2))})}
|
||||
[icons/icon (if is-required :main-icons2/hold :main-icons2/add)
|
||||
{:no-color true
|
||||
:width 13.5
|
||||
:height 13.5}]])}
|
||||
:size 12}]])}
|
||||
(str value " " token)]))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
(ns quo2.components.wallet.lowest-price
|
||||
(:require [quo.react-native :as rn]
|
||||
(:require [react-native.core :as rn]
|
||||
[clojure.string :as string]))
|
||||
|
||||
(def centrify {:style {:flex-direction :row
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
(ns quo2.components.wallet.network-amount
|
||||
(:require [clojure.string :as string]
|
||||
[quo.react-native :as rn]
|
||||
(:require [react-native.core :as rn]
|
||||
[quo2.components.icon :as icon]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[status-im.i18n.i18n :as i18n]))
|
||||
[quo2.foundations.colors :as colors]))
|
||||
|
||||
(defn network-amount
|
||||
"[network-amount opts]
|
||||
@@ -13,7 +11,7 @@
|
||||
:network-name \"Mainnet\" ;; shown network name
|
||||
:icon :main-icons2/ethereum ;; key of icon belonging to the network
|
||||
:eth-value 1.2345678 ;; shown ETH value}"
|
||||
[{:keys [show-right-border? style network-name icon eth-value] :as _opts}]
|
||||
[{:keys [show-right-border? style network-name icon eth-value labels] :as _opts}]
|
||||
[rn/view (merge {:accessibility-label :network-amount
|
||||
:background-color (colors/theme-colors colors/white colors/neutral-95)
|
||||
:border-radius 16
|
||||
@@ -35,7 +33,7 @@
|
||||
[text/text {:weight :medium
|
||||
:size :paragraph-2
|
||||
:style {:color (colors/theme-colors colors/neutral-100 colors/white)}}
|
||||
eth-value \space (i18n/label :t/eth)]
|
||||
eth-value \space (:eth labels)]
|
||||
[rn/view {:style {:border-right-width (when show-right-border? 1)
|
||||
:border-right-color (colors/theme-colors colors/neutral-40 colors/neutral-50)
|
||||
:padding-left 8
|
||||
@@ -44,4 +42,4 @@
|
||||
[text/text {:weight :medium
|
||||
:size :label
|
||||
:style {:color colors/neutral-50}}
|
||||
(string/lower-case (i18n/label :t/on)) \space network-name]]]])
|
||||
(:on labels) \space network-name]]]])
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
(ns quo2.components.wallet.network-breakdown
|
||||
(:require [quo.react-native :as rn]
|
||||
(:require [react-native.core :as rn]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.components.wallet.network-amount :refer [network-amount]]))
|
||||
|
||||
(defn network-breakdown
|
||||
[{:keys [top-value network-conversions]}]
|
||||
[{:keys [top-value network-conversions labels]}]
|
||||
[rn/view {:style {:background-color (colors/theme-colors
|
||||
colors/white
|
||||
colors/neutral-95)
|
||||
@@ -41,5 +41,6 @@
|
||||
[network-amount {:show-right-border? (not= idx last-item-idx)
|
||||
:icon icon
|
||||
:network-name network
|
||||
:eth-value conversion}]])
|
||||
:eth-value conversion
|
||||
:labels labels}]])
|
||||
network-conversions))]])
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
(ns quo2.components.wallet.token-overview
|
||||
(:require
|
||||
[quo2.foundations.colors :as colors]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[quo.react-native :as rn]
|
||||
[react-native.core :as rn]
|
||||
[clojure.string :as string]
|
||||
[status-im.utils.currency :as currencies]
|
||||
[status-im.ui.components.icons.icons :as icons]
|
||||
[quo2.components.markdown.text :as text]))
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.components.icon :as icons]))
|
||||
|
||||
(def container-style {:display :flex :width "100%" :padding-left 20 :padding-right 20 :padding-top 12 :padding-bottom 12})
|
||||
|
||||
@@ -42,15 +40,15 @@
|
||||
}"
|
||||
[]
|
||||
(fn
|
||||
[{:keys [currency price percentage-change] :or {currency :usd price "0.00" percentage-change "0.0"}}]
|
||||
[{:keys [currency price percentage-change label] :or {price "0.00" percentage-change "0.0"}}]
|
||||
(let [direction (get-direction percentage-change)]
|
||||
[rn/view {:style container-style}
|
||||
[text/text {:number-of-lines 1
|
||||
:size :paragraph-2} (i18n/label :token-price)]
|
||||
:size :paragraph-2} label]
|
||||
[text/text {:style {:margin-top 4}
|
||||
:weight :semi-bold
|
||||
:number-of-lines 1
|
||||
:size :heading-2} (str (get-in currencies/currencies [currency :symbol]) price)]
|
||||
:size :heading-2} (str currency price)]
|
||||
|
||||
[rn/view {:style {:display :flex :flex-direction :row :margin-top 6 :align-items :center}}
|
||||
(when (not (zero? direction)) [icons/icon (if (>= direction 0) :main-icons2/price-increase12 :main-icons2/price-decrease12)
|
||||
@@ -76,7 +74,7 @@
|
||||
:percentage-change :string
|
||||
}"
|
||||
[]
|
||||
(fn [{:keys [token token-img-src currency account-balance price percentage-change] :or {currency :usd account-balance "0.00" price "0.00" percentage-change "0.0"}}]
|
||||
(fn [{:keys [token token-img-src currency account-balance price percentage-change] :or {account-balance "0.00" price "0.00" percentage-change "0.0"}}]
|
||||
(let [direction (get-direction percentage-change)]
|
||||
[rn/view {:style container-style}
|
||||
[text/text {:weight :regular
|
||||
@@ -85,20 +83,19 @@
|
||||
[rn/view {:style {:display :flex :flex-direction :row :flex 1 :justify-content :space-between}}
|
||||
[text/text {:number-of-lines 1
|
||||
:weight :semi-bold
|
||||
:size :heading-1} (str (get-in currencies/currencies [currency :symbol]) account-balance)]
|
||||
:size :heading-1} (str currency account-balance)]
|
||||
[rn/image {:source token-img-src
|
||||
:style {:height 32
|
||||
:width 32}}]]
|
||||
[rn/view {:style {:display :flex :flex-direction :row :margin-top 6 :align-items :center}}
|
||||
(when (not (zero? direction)) [icons/icon (if (pos? direction) :main-icons2/price-increase12 :main-icons2/price-decrease12)
|
||||
(when (not (zero? direction)) [icons/icon (if (pos? direction) :main-icons2/price-increase :main-icons2/price-decrease)
|
||||
{:no-color true
|
||||
:width 12
|
||||
:height 12
|
||||
:size 12
|
||||
:container-style {:margin-right 4}}])
|
||||
[text/text {:number-of-lines 1
|
||||
:weight :medium
|
||||
:size :paragraph-2
|
||||
:style {:color (price-color direction)}} (str (get-in currencies/currencies [currency :symbol]) price)]
|
||||
:style {:color (price-color direction)}} (str currency price)]
|
||||
[divider direction]
|
||||
[text/text {:number-of-lines 1
|
||||
:weight :medium
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
(ns quo2.core
|
||||
(:require quo2.components.buttons.button
|
||||
quo2.components.buttons.dynamic-button
|
||||
quo2.components.markdown.text
|
||||
quo2.components.icon
|
||||
quo2.components.separator
|
||||
quo2.components.header
|
||||
quo2.components.avatars.account-avatar
|
||||
quo2.components.avatars.channel-avatar
|
||||
quo2.components.avatars.group-avatar
|
||||
quo2.components.avatars.icon-avatar
|
||||
quo2.components.avatars.user-avatar
|
||||
quo2.components.avatars.wallet-user-avatar
|
||||
quo2.components.community.community-card-view
|
||||
quo2.components.community.community-list-view
|
||||
quo2.components.community.community-view
|
||||
quo2.components.community.discover-card
|
||||
quo2.components.counter.counter
|
||||
quo2.components.dividers.divider-label
|
||||
quo2.components.dividers.new-messages
|
||||
quo2.components.drawers.action-drawers
|
||||
quo2.components.dropdowns.dropdown
|
||||
quo2.components.info.info-message
|
||||
quo2.components.info.information-box
|
||||
quo2.components.list-items.channel
|
||||
quo2.components.list-items.menu-item
|
||||
quo2.components.list-items.preview-list
|
||||
quo2.components.messages.gap
|
||||
quo2.components.messages.system-message
|
||||
quo2.components.reactions.reaction
|
||||
quo2.components.notifications.activity-logs
|
||||
quo2.components.notifications.info-count
|
||||
quo2.components.notifications.notification-dot
|
||||
quo2.components.tags.tags
|
||||
quo2.components.tabs.tabs
|
||||
quo2.components.tabs.account-selector))
|
||||
|
||||
(def button quo2.components.buttons.button/button)
|
||||
(def dynamic-button quo2.components.buttons.dynamic-button/dynamic-button)
|
||||
(def text quo2.components.markdown.text/text)
|
||||
(def icon quo2.components.icon/icon)
|
||||
(def separator quo2.components.separator/separator)
|
||||
(def counter quo2.components.counter.counter/counter)
|
||||
(def header quo2.components.header/header)
|
||||
(def action-drawer quo2.components.drawers.action-drawers/action-drawer)
|
||||
(def dropdown quo2.components.dropdowns.dropdown/dropdown)
|
||||
(def info-message quo2.components.info.info-message/info-message)
|
||||
(def information-box quo2.components.info.information-box/information-box)
|
||||
(def gap quo2.components.messages.gap/gap)
|
||||
(def system-message quo2.components.messages.system-message/system-message)
|
||||
(def reaction quo2.components.reactions.reaction/reaction)
|
||||
(def tags quo2.components.tags.tags/tags)
|
||||
(def tabs quo2.components.tabs.tabs/tabs)
|
||||
(def scrollable-tabs quo2.components.tabs.tabs/scrollable-tabs)
|
||||
(def account-selector quo2.components.tabs.account-selector/account-selector)
|
||||
|
||||
;;;; AVATAR
|
||||
(def account-avatar quo2.components.avatars.account-avatar/account-avatar)
|
||||
(def channel-avatar quo2.components.avatars.channel-avatar/channel-avatar)
|
||||
(def group-avatar quo2.components.avatars.group-avatar/group-avatar)
|
||||
(def icon-avatar quo2.components.avatars.icon-avatar/icon-avatar)
|
||||
(def user-avatar quo2.components.avatars.user-avatar/user-avatar)
|
||||
(def wallet-user-avatar quo2.components.avatars.wallet-user-avatar/wallet-user-avatar)
|
||||
|
||||
;;;; COMMUNITY
|
||||
(def community-card-view-item quo2.components.community.community-card-view/community-card-view-item)
|
||||
(def communities-list-view-item quo2.components.community.community-list-view/communities-list-view-item)
|
||||
(def communities-membership-list-item quo2.components.community.community-list-view/communities-membership-list-item)
|
||||
(def community-stats-column quo2.components.community.community-view/community-stats-column)
|
||||
(def community-stats quo2.components.community.community-view/community-stats)
|
||||
(def community-tags quo2.components.community.community-view/community-tags)
|
||||
(def community-title quo2.components.community.community-view/community-title)
|
||||
(def discover-card quo2.components.community.discover-card/discover-card)
|
||||
|
||||
;;;; DIVIDERS
|
||||
(def divider-label quo2.components.dividers.divider-label/divider-label)
|
||||
(def new-messages quo2.components.dividers.new-messages/new-messages)
|
||||
|
||||
;;;; LIST ITEMS
|
||||
(def channel-list-item quo2.components.list-items.channel/list-item)
|
||||
(def menu-item quo2.components.list-items.menu-item/menu-item)
|
||||
(def preview-list quo2.components.list-items.preview-list/preview-list)
|
||||
|
||||
;;;; NOTIFICATIONS
|
||||
(def activity-log quo2.components.notifications.activity-logs/activity-log)
|
||||
(def info-count quo2.components.notifications.info-count/info-count)
|
||||
(def notification-dot quo2.components.notifications.notification-dot/notification-dot)
|
||||
@@ -1,6 +1,6 @@
|
||||
(ns quo2.foundations.colors
|
||||
(:require [clojure.string :as string]
|
||||
[quo.theme :as theme]))
|
||||
[quo2.theme :as theme]))
|
||||
|
||||
(defn alpha [value opacity]
|
||||
(when value
|
||||
@@ -45,7 +45,6 @@
|
||||
;;Blur
|
||||
(def neutral-5-opa-70 (alpha neutral-5 0.7))
|
||||
(def neutral-90-opa-70 (alpha neutral-90 0.7))
|
||||
(def neutral-95-opa-70 (alpha neutral-95 0.7))
|
||||
|
||||
;;80 with transparency
|
||||
(def neutral-80-opa-5 (alpha neutral-80 0.05))
|
||||
@@ -60,6 +59,20 @@
|
||||
(def neutral-80-opa-90 (alpha neutral-80 0.9))
|
||||
(def neutral-80-opa-95 (alpha neutral-80 0.95))
|
||||
|
||||
;;95 with transparency
|
||||
(def neutral-95-opa-60 (alpha neutral-95 0.6))
|
||||
(def neutral-95-opa-70 (alpha neutral-95 0.7))
|
||||
(def neutral-95-opa-80 (alpha neutral-95 0.8))
|
||||
(def neutral-95-opa-90 (alpha neutral-95 0.9))
|
||||
(def neutral-95-opa-95 (alpha neutral-95 0.95))
|
||||
|
||||
;;100 with transparency
|
||||
(def neutral-100-opa-60 (alpha neutral-100 0.6))
|
||||
(def neutral-100-opa-70 (alpha neutral-100 0.7))
|
||||
(def neutral-100-opa-80 (alpha neutral-100 0.8))
|
||||
(def neutral-100-opa-90 (alpha neutral-100 0.9))
|
||||
(def neutral-100-opa-95 (alpha neutral-100 0.95))
|
||||
|
||||
;;;;White
|
||||
|
||||
;;Solid
|
||||
@@ -104,6 +117,12 @@
|
||||
(def success-50-opa-30 (alpha success-50 0.3))
|
||||
(def success-50-opa-40 (alpha success-50 0.4))
|
||||
|
||||
(def success-60-opa-5 (alpha success-60 0.05))
|
||||
(def success-60-opa-10 (alpha success-60 0.1))
|
||||
(def success-60-opa-20 (alpha success-60 0.2))
|
||||
(def success-60-opa-30 (alpha success-60 0.3))
|
||||
(def success-60-opa-40 (alpha success-60 0.4))
|
||||
|
||||
;;;;Danger
|
||||
|
||||
;;Solid
|
||||
|
||||
@@ -45,3 +45,5 @@
|
||||
(def font-bold {:font-family "Inter-Bold"}) ; 700
|
||||
|
||||
(def monospace {:font-family "InterStatus-Regular"})
|
||||
|
||||
(def code {:font-family "UbuntuMono-Regular"})
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
(ns quo2.theme
|
||||
(:require [reagent.core :as reagent]))
|
||||
|
||||
(def theme (reagent/atom :light))
|
||||
|
||||
(defn dark? []
|
||||
(= :dark @theme))
|
||||
|
||||
(defn get-theme []
|
||||
@theme)
|
||||
|
||||
(defn set-theme [value]
|
||||
(reset! theme value))
|
||||
@@ -0,0 +1,31 @@
|
||||
(ns react-native.core
|
||||
(:require [reagent.core :as reagent]
|
||||
["react-native" :as react-native]
|
||||
[react-native.flat-list :as flat-list]))
|
||||
|
||||
(def view (reagent/adapt-react-class (.-View ^js react-native)))
|
||||
(def scroll-view (reagent/adapt-react-class (.-ScrollView ^js react-native)))
|
||||
(def image (reagent/adapt-react-class (.-Image ^js react-native)))
|
||||
(def text (reagent/adapt-react-class (.-Text ^js react-native)))
|
||||
(def text-input (reagent/adapt-react-class (.-TextInput ^js react-native)))
|
||||
|
||||
(def touchable-opacity (reagent/adapt-react-class (.-TouchableOpacity ^js react-native)))
|
||||
(def touchable-highlight (reagent/adapt-react-class (.-TouchableHighlight ^js react-native)))
|
||||
(def touchable-without-feedback (reagent/adapt-react-class (.-TouchableWithoutFeedback ^js react-native)))
|
||||
|
||||
(def flat-list flat-list/flat-list)
|
||||
|
||||
(def activity-indicator (reagent/adapt-react-class (.-ActivityIndicator ^js react-native)))
|
||||
|
||||
(def modal (reagent/adapt-react-class (.-Modal ^js react-native)))
|
||||
|
||||
(def keyboard ^js (.-Keyboard ^js react-native))
|
||||
|
||||
(def dismiss-keyboard! #(.dismiss keyboard))
|
||||
|
||||
(defn use-window-dimensions []
|
||||
(let [window ^js (react-native/useWindowDimensions)]
|
||||
{:font-scale (.-fontScale window)
|
||||
:height (.-height window)
|
||||
:scale (.-scale window)
|
||||
:width (.-width window)}))
|
||||
@@ -0,0 +1,32 @@
|
||||
(ns react-native.fast-image
|
||||
(:require ["react-native-fast-image" :as FastImage]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
(def fast-image-class (reagent/adapt-react-class ^js FastImage))
|
||||
|
||||
(defn placeholder [style child]
|
||||
[rn/view {:style (merge style {:flex 1 :justify-content :center :align-items :center})}
|
||||
child])
|
||||
|
||||
(defn fast-image [_]
|
||||
(let [loaded? (reagent/atom false)
|
||||
error? (reagent/atom false)]
|
||||
(fn [props]
|
||||
[fast-image-class (merge
|
||||
props
|
||||
{:on-error (fn [e]
|
||||
(when-let [on-error (:on-error props)]
|
||||
(on-error e))
|
||||
(reset! error? true))
|
||||
:on-load (fn [e]
|
||||
(when-let [on-load (:on-load props)]
|
||||
(on-load e))
|
||||
(reset! loaded? true)
|
||||
(reset! error? false))})
|
||||
(when (or @error? (not @loaded?))
|
||||
[placeholder (:style props)
|
||||
(if @error?
|
||||
[rn/text "X"]
|
||||
(when-not @loaded?
|
||||
[rn/activity-indicator {:animating true}]))])])))
|
||||
@@ -0,0 +1,35 @@
|
||||
(ns react-native.flat-list
|
||||
(:require [reagent.core :as reagent]
|
||||
["react-native" :as react-native]))
|
||||
|
||||
(def react-native-flat-list (reagent/adapt-react-class (.-FlatList ^js react-native)))
|
||||
|
||||
(defn- wrap-render-fn [f render-data]
|
||||
(fn [data]
|
||||
(reagent/as-element [f (.-item ^js data) (.-index ^js data)
|
||||
(.-separators ^js data) render-data
|
||||
(.-isActive ^js data) (.-drag ^js data)])))
|
||||
|
||||
(defn- wrap-on-drag-end-fn [f]
|
||||
(fn [data]
|
||||
(f (.-from ^js data) (.-to ^js data) (.-data ^js data))))
|
||||
|
||||
(defn- wrap-key-fn [f]
|
||||
(fn [data index]
|
||||
{:post [(some? %)]}
|
||||
(f data index)))
|
||||
|
||||
(defn base-list-props
|
||||
[{:keys [key-fn render-fn empty-component header footer separator data render-data on-drag-end-fn] :as props}]
|
||||
(merge {:data (to-array data)}
|
||||
(when key-fn {:keyExtractor (wrap-key-fn key-fn)})
|
||||
(when render-fn {:renderItem (wrap-render-fn render-fn render-data)})
|
||||
(when separator {:ItemSeparatorComponent (fn [] (reagent/as-element separator))})
|
||||
(when empty-component {:ListEmptyComponent (fn [] (reagent/as-element empty-component))})
|
||||
(when header {:ListHeaderComponent (reagent/as-element header)})
|
||||
(when footer {:ListFooterComponent (reagent/as-element footer)})
|
||||
(when on-drag-end-fn {:onDragEnd (wrap-on-drag-end-fn on-drag-end-fn)})
|
||||
(dissoc props :data :header :footer :empty-component :separator :render-fn :key-fn :on-drag-end-fn)))
|
||||
|
||||
(defn flat-list [props]
|
||||
[react-native-flat-list (base-list-props props)])
|
||||
@@ -1,4 +1,4 @@
|
||||
(ns quo2.gesture
|
||||
(ns react-native.gesture
|
||||
(:require ["react-native-gesture-handler" :refer (GestureDetector Gesture)]
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
(ns react-native.hole-view
|
||||
(:require [reagent.core :as reagent]
|
||||
["react-native-hole-view" :refer (RNHoleView)]))
|
||||
|
||||
(def hole-view (reagent/adapt-react-class RNHoleView))
|
||||
@@ -0,0 +1,5 @@
|
||||
(ns react-native.linear-gradient
|
||||
(:require ["react-native-linear-gradient" :default LinearGradient]
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
(def linear-gradient (reagent/adapt-react-class LinearGradient))
|
||||
@@ -0,0 +1,5 @@
|
||||
(ns react-native.masked-view
|
||||
(:require ["@react-native-community/masked-view" :default MaskedView]
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
(def masked-view (reagent/adapt-react-class MaskedView))
|
||||
@@ -0,0 +1,9 @@
|
||||
(ns react-native.platform
|
||||
(:require ["react-native" :as react-native]))
|
||||
|
||||
(def platform (.-Platform ^js react-native))
|
||||
|
||||
(def os (when platform (.-OS ^js platform)))
|
||||
|
||||
(def android? (= os "android"))
|
||||
(def ios? (= os "ios"))
|
||||
@@ -1,4 +1,4 @@
|
||||
(ns quo2.reanimated
|
||||
(ns react-native.reanimated
|
||||
(:require ["react-native" :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[clojure.string :as string]
|
||||
@@ -0,0 +1,12 @@
|
||||
(ns react-native.safe-area
|
||||
(:require ["react-native-safe-area-context" :as safe-area-context
|
||||
:refer (SafeAreaInsetsContext)]
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
(def ^:private consumer-raw (reagent/adapt-react-class (.-Consumer ^js SafeAreaInsetsContext)))
|
||||
|
||||
(defn consumer [component]
|
||||
[consumer-raw
|
||||
(fn [insets]
|
||||
(reagent/as-element
|
||||
[component (js->clj insets :keywordize-keys true)]))])
|
||||
@@ -0,0 +1,5 @@
|
||||
(ns react-native.syntax-highlighter
|
||||
(:require ["react-syntax-highlighter" :default Highlighter]
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
(def highlighter (reagent/adapt-react-class Highlighter))
|
||||
@@ -1,6 +1,6 @@
|
||||
(ns status-im.activity-center.core
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.constants :as constants]
|
||||
(:require [re-frame.core :as rf]
|
||||
[status-im.activity-center.notification-types :as types]
|
||||
[status-im.data-store.activities :as data-store.activities]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[status-im.utils.fx :as fx]
|
||||
@@ -18,21 +18,23 @@
|
||||
~excessively~ big, this implementation will probably need to be revisited."
|
||||
[db-notifications new-notifications]
|
||||
(reduce (fn [acc {:keys [id type read] :as notification}]
|
||||
(let [filter-status (if read :read :unread)]
|
||||
(cond-> (-> acc
|
||||
(update-in [type :read :data]
|
||||
(fn [data]
|
||||
(remove #(= id (:id %)) data)))
|
||||
(update-in [type :unread :data]
|
||||
(fn [data]
|
||||
(remove #(= id (:id %)) data))))
|
||||
(not (or (:dismissed notification) (:accepted notification)))
|
||||
(update-in [type filter-status :data]
|
||||
(fn [data]
|
||||
(->> notification
|
||||
(conj data)
|
||||
(sort-by (juxt :timestamp :id))
|
||||
reverse))))))
|
||||
(let [filter-status (if read :read :unread)
|
||||
remove-notification (fn [data]
|
||||
(remove #(= id (:id %)) data))
|
||||
insert-and-sort (fn [data]
|
||||
(->> notification
|
||||
(conj data)
|
||||
(sort-by (juxt :timestamp :id))
|
||||
reverse))]
|
||||
(as-> acc $
|
||||
(update-in $ [type :read :data] remove-notification)
|
||||
(update-in $ [type :unread :data] remove-notification)
|
||||
(update-in $ [types/no-type :read :data] remove-notification)
|
||||
(update-in $ [types/no-type :unread :data] remove-notification)
|
||||
(if (or (:dismissed notification) (:accepted notification))
|
||||
$
|
||||
(-> $ (update-in [type filter-status :data] insert-and-sort)
|
||||
(update-in [types/no-type filter-status :data] insert-and-sort))))))
|
||||
db-notifications
|
||||
new-notifications))
|
||||
|
||||
@@ -43,11 +45,37 @@
|
||||
{:db (update-in db [:activity-center :notifications]
|
||||
update-notifications new-notifications)}))
|
||||
|
||||
;;;; Contact verification
|
||||
|
||||
(fx/defn contact-verification-decline
|
||||
{:events [:activity-center.contact-verification/decline]}
|
||||
[_ contact-verification-id]
|
||||
{::json-rpc/call [{:method "wakuext_declineContactVerificationRequest"
|
||||
:params [contact-verification-id]
|
||||
:on-success #(rf/dispatch [:activity-center.contact-verification/decline-success %])
|
||||
:on-error #(rf/dispatch [:activity-center.contact-verification/decline-error contact-verification-id %])}]})
|
||||
|
||||
(fx/defn contact-verification-decline-success
|
||||
{:events [:activity-center.contact-verification/decline-success]}
|
||||
[cofx response]
|
||||
(->> response
|
||||
:activityCenterNotifications
|
||||
(map data-store.activities/<-rpc)
|
||||
(notifications-reconcile cofx)))
|
||||
|
||||
(fx/defn contact-verification-decline-error
|
||||
{:events [:activity-center.contact-verification/decline-error]}
|
||||
[_ contact-verification-id error]
|
||||
(log/warn "Failed to decline contact verification"
|
||||
{:contact-verification-id contact-verification-id
|
||||
:error error})
|
||||
nil)
|
||||
|
||||
;;;; Notifications fetching and pagination
|
||||
|
||||
(def defaults
|
||||
{:filter-status :unread
|
||||
:filter-type constants/activity-center-notification-type-no-type
|
||||
:filter-type types/no-type
|
||||
:notifications-per-page 10})
|
||||
|
||||
(def start-or-end-cursor
|
||||
@@ -70,8 +98,8 @@
|
||||
{:db (assoc-in db [:activity-center :notifications filter-type filter-status :loading?] true)
|
||||
::json-rpc/call [{:method (filter-status->rpc-method filter-status)
|
||||
:params [cursor (defaults :notifications-per-page) filter-type]
|
||||
:on-success #(re-frame/dispatch [:activity-center.notifications/fetch-success filter-type filter-status reset-data? %])
|
||||
:on-error #(re-frame/dispatch [:activity-center.notifications/fetch-error filter-type filter-status %])}]}))
|
||||
:on-success #(rf/dispatch [:activity-center.notifications/fetch-success filter-type filter-status reset-data? %])
|
||||
:on-error #(rf/dispatch [:activity-center.notifications/fetch-error filter-type filter-status %])}]}))
|
||||
|
||||
(fx/defn notifications-fetch-first-page
|
||||
{:events [:activity-center.notifications/fetch-first-page]}
|
||||
|
||||
@@ -2,366 +2,491 @@
|
||||
(:require [cljs.test :refer [deftest is testing]]
|
||||
[day8.re-frame.test :as rf-test]
|
||||
[re-frame.core :as rf]
|
||||
[status-im.constants :as c]
|
||||
[status-im.activity-center.notification-types :as types]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
status-im.events
|
||||
[status-im.test-helpers :as h]))
|
||||
[status-im.test-helpers :as h]
|
||||
[status-im.utils.config :as config]))
|
||||
|
||||
(defn setup []
|
||||
(h/register-helper-events)
|
||||
(rf/dispatch [:init/app-started]))
|
||||
|
||||
(defn remove-color-key
|
||||
"Remove `:color` key from notifications because they have random values that we
|
||||
can't assert against."
|
||||
[grouped-notifications {:keys [type status]}]
|
||||
(update-in grouped-notifications
|
||||
[type status :data]
|
||||
(fn [old _]
|
||||
(map #(dissoc % :color) old))
|
||||
nil))
|
||||
;;;; Contact verification
|
||||
|
||||
(deftest contact-verification-decline-test
|
||||
(with-redefs [config/new-activity-center-enabled? true]
|
||||
(testing "successfully declines and reconciles returned notification"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [spy-queue (atom [])
|
||||
contact-verification-id 24
|
||||
expected-notification {:accepted false
|
||||
:author "0x04d03f"
|
||||
:chat-id "0x04d03f"
|
||||
:contact-verification-status 3
|
||||
:dismissed false
|
||||
:id 24
|
||||
:last-message nil
|
||||
:message {:command-parameters nil
|
||||
:content {:chat-id nil
|
||||
:ens-name nil
|
||||
:image nil
|
||||
:line-count nil
|
||||
:links nil
|
||||
:parsed-text nil
|
||||
:response-to nil
|
||||
:rtl? nil
|
||||
:sticker nil
|
||||
:text nil}
|
||||
:outgoing false
|
||||
:outgoing-status nil
|
||||
:quoted-message nil}
|
||||
:name "0x04d03f"
|
||||
:read true
|
||||
:reply-message nil
|
||||
:timestamp 1666647286000
|
||||
:type types/contact-verification}]
|
||||
(h/stub-fx-with-callbacks
|
||||
::json-rpc/call
|
||||
:on-success (constantly {:activityCenterNotifications
|
||||
[{:accepted false
|
||||
:author "0x04d03f"
|
||||
:chatId "0x04d03f"
|
||||
:contactVerificationStatus 3
|
||||
:dismissed false
|
||||
:id contact-verification-id
|
||||
:message {}
|
||||
:name "0x04d03f"
|
||||
:read true
|
||||
:timestamp 1666647286000
|
||||
:type types/contact-verification}]}))
|
||||
|
||||
(h/spy-fx spy-queue ::json-rpc/call)
|
||||
|
||||
(rf/dispatch [:activity-center.contact-verification/decline contact-verification-id])
|
||||
|
||||
(is (= {:method "wakuext_declineContactVerificationRequest"
|
||||
:params [contact-verification-id]}
|
||||
(-> @spy-queue
|
||||
(get-in [0 :args 0])
|
||||
(select-keys [:method :params]))))
|
||||
|
||||
(is (= {types/no-type
|
||||
{:read {:data [expected-notification]}
|
||||
:unread {:data []}}
|
||||
types/contact-verification
|
||||
{:read {:data [expected-notification]}
|
||||
:unread {:data []}}}
|
||||
(get-in (h/db) [:activity-center :notifications]))))))
|
||||
|
||||
(testing "logs failure"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [contact-verification-id 666]
|
||||
(h/using-log-test-appender
|
||||
(fn [logs]
|
||||
(h/stub-fx-with-callbacks ::json-rpc/call :on-error (constantly :fake-error))
|
||||
|
||||
(rf/dispatch [:activity-center.contact-verification/decline contact-verification-id])
|
||||
|
||||
(is (= {:args ["Failed to decline contact verification"
|
||||
{:contact-verification-id contact-verification-id
|
||||
:error :fake-error}]
|
||||
:level :warn}
|
||||
(last @logs))))))))))
|
||||
|
||||
;;;; Notification reconciliation
|
||||
|
||||
(deftest notifications-reconcile-test
|
||||
(testing "does nothing when there are no new notifications"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [notifications {c/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x1"
|
||||
:read true
|
||||
:type c/activity-center-notification-type-one-to-one-chat}
|
||||
{:id "0x2"
|
||||
:read true
|
||||
:type c/activity-center-notification-type-one-to-one-chat}]}
|
||||
:unread {:cursor ""
|
||||
:data [{:id "0x3"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-one-to-one-chat}]}}
|
||||
c/activity-center-notification-type-private-group-chat
|
||||
{:unread {:cursor ""
|
||||
:data [{:id "0x4"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-private-group-chat}]}}}]
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications] notifications])
|
||||
(with-redefs [config/new-activity-center-enabled? true]
|
||||
(testing "does nothing when there are no new notifications"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [notifications {types/one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x1"
|
||||
:read true
|
||||
:type types/one-to-one-chat}
|
||||
{:id "0x2"
|
||||
:read true
|
||||
:type types/one-to-one-chat}]}
|
||||
:unread {:cursor ""
|
||||
:data [{:id "0x3"
|
||||
:read false
|
||||
:type types/one-to-one-chat}]}}
|
||||
types/private-group-chat
|
||||
{:unread {:cursor ""
|
||||
:data [{:id "0x4"
|
||||
:read false
|
||||
:type types/private-group-chat}]}}}]
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications] notifications])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/reconcile nil])
|
||||
(rf/dispatch [:activity-center.notifications/reconcile nil])
|
||||
|
||||
(is (= notifications (get-in (h/db) [:activity-center :notifications]))))))
|
||||
(is (= notifications (get-in (h/db) [:activity-center :notifications]))))))
|
||||
|
||||
(testing "removes dismissed or accepted notifications"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications]
|
||||
{c/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x1" :read true :type c/activity-center-notification-type-one-to-one-chat}
|
||||
{:id "0x2" :read true :type c/activity-center-notification-type-one-to-one-chat}]}
|
||||
:unread {:cursor ""
|
||||
:data [{:id "0x3" :read false :type c/activity-center-notification-type-one-to-one-chat}]}}
|
||||
2 {:unread {:cursor ""
|
||||
:data [{:id "0x4" :read false :type 2}
|
||||
{:id "0x6" :read false :type 2}]}}}])
|
||||
(testing "removes dismissed or accepted notifications"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications]
|
||||
{types/one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x1" :read true :type types/one-to-one-chat}
|
||||
{:id "0x2" :read true :type types/one-to-one-chat}]}
|
||||
:unread {:cursor ""
|
||||
:data [{:id "0x3" :read false :type types/one-to-one-chat}]}}
|
||||
types/private-group-chat
|
||||
{:unread {:cursor ""
|
||||
:data [{:id "0x4" :read false :type types/private-group-chat}
|
||||
{:id "0x6" :read false :type types/private-group-chat}]}}}])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/reconcile
|
||||
[{:id "0x1"
|
||||
:read true
|
||||
:type c/activity-center-notification-type-one-to-one-chat
|
||||
:dismissed true}
|
||||
{:id "0x3"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-one-to-one-chat
|
||||
:accepted true}
|
||||
{:id "0x4"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-private-group-chat
|
||||
:dismissed true}
|
||||
{:id "0x5"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-private-group-chat
|
||||
:accepted true}]])
|
||||
(rf/dispatch [:activity-center.notifications/reconcile
|
||||
[{:id "0x1"
|
||||
:read true
|
||||
:type types/one-to-one-chat
|
||||
:dismissed true}
|
||||
{:id "0x3"
|
||||
:read false
|
||||
:type types/one-to-one-chat
|
||||
:accepted true}
|
||||
{:id "0x4"
|
||||
:read false
|
||||
:type types/private-group-chat
|
||||
:dismissed true}
|
||||
{:id "0x5"
|
||||
:read false
|
||||
:type types/private-group-chat
|
||||
:accepted true}]])
|
||||
|
||||
(is (= {c/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x2"
|
||||
:read true
|
||||
:type c/activity-center-notification-type-one-to-one-chat}]}
|
||||
:unread {:cursor ""
|
||||
:data []}}
|
||||
c/activity-center-notification-type-private-group-chat
|
||||
{:read {:data []}
|
||||
:unread {:cursor ""
|
||||
:data [{:id "0x6"
|
||||
(is (= {types/no-type
|
||||
{:read {:data []}
|
||||
:unread {:data []}}
|
||||
|
||||
types/one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x2"
|
||||
:read true
|
||||
:type types/one-to-one-chat}]}
|
||||
:unread {:cursor ""
|
||||
:data []}}
|
||||
types/private-group-chat
|
||||
{:read {:data []}
|
||||
:unread {:cursor ""
|
||||
:data [{:id "0x6"
|
||||
:read false
|
||||
:type types/private-group-chat}]}}}
|
||||
(get-in (h/db) [:activity-center :notifications])))))
|
||||
|
||||
(testing "replaces old notifications with newly arrived ones"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications]
|
||||
{types/no-type
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x1"
|
||||
:read true
|
||||
:type types/one-to-one-chat}]}
|
||||
:unread {:cursor ""
|
||||
:data [{:id "0x4"
|
||||
:read false
|
||||
:type types/private-group-chat}
|
||||
{:id "0x6"
|
||||
:read false
|
||||
:type types/private-group-chat}]}}
|
||||
types/one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x1"
|
||||
:read true
|
||||
:type types/one-to-one-chat}]}}
|
||||
types/private-group-chat
|
||||
{:unread {:cursor ""
|
||||
:data [{:id "0x4"
|
||||
:read false
|
||||
:type types/private-group-chat}
|
||||
{:id "0x6"
|
||||
:read false
|
||||
:type types/private-group-chat}]}}}])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/reconcile
|
||||
[{:id "0x1"
|
||||
:read true
|
||||
:type types/one-to-one-chat
|
||||
:last-message {}}
|
||||
{:id "0x4"
|
||||
:read false
|
||||
:type types/private-group-chat
|
||||
:author "0xabc"}
|
||||
{:id "0x6"
|
||||
:read false
|
||||
:type types/private-group-chat}]])
|
||||
|
||||
(is (= {types/no-type
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x1"
|
||||
:read true
|
||||
:type types/one-to-one-chat
|
||||
:last-message {}}]}
|
||||
:unread {:cursor ""
|
||||
:data [{:id "0x6"
|
||||
:read false
|
||||
:type types/private-group-chat}
|
||||
{:id "0x4"
|
||||
:read false
|
||||
:type types/private-group-chat
|
||||
:author "0xabc"}]}}
|
||||
types/one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x1"
|
||||
:read true
|
||||
:type types/one-to-one-chat
|
||||
:last-message {}}]}
|
||||
:unread {:data []}}
|
||||
types/private-group-chat
|
||||
{:read {:data []}
|
||||
:unread {:cursor ""
|
||||
:data [{:id "0x6"
|
||||
:read false
|
||||
:type types/private-group-chat}
|
||||
{:id "0x4"
|
||||
:read false
|
||||
:type types/private-group-chat
|
||||
:author "0xabc"}]}}}
|
||||
(get-in (h/db) [:activity-center :notifications])))))
|
||||
|
||||
(testing "reconciles notifications that switched their read/unread status"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications]
|
||||
{types/one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x1"
|
||||
:read true
|
||||
:type types/one-to-one-chat}]}}}])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/reconcile
|
||||
[{:id "0x1"
|
||||
:read false
|
||||
:type types/one-to-one-chat}]])
|
||||
|
||||
(is (= {types/no-type
|
||||
{:read {:data []}
|
||||
:unread {:data [{:id "0x1"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-private-group-chat}]}}}
|
||||
(get-in (h/db) [:activity-center :notifications])))))
|
||||
:type types/one-to-one-chat}]}}
|
||||
|
||||
(testing "replaces old notifications with newly arrived ones"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications]
|
||||
{c/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x1"
|
||||
:read true
|
||||
:type c/activity-center-notification-type-one-to-one-chat}]}}
|
||||
c/activity-center-notification-type-private-group-chat
|
||||
{:unread {:cursor ""
|
||||
:data [{:id "0x4"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-private-group-chat}
|
||||
{:id "0x6"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-private-group-chat}]}}}])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/reconcile
|
||||
[{:id "0x1"
|
||||
:read true
|
||||
:type c/activity-center-notification-type-one-to-one-chat
|
||||
:last-message {}}
|
||||
{:id "0x4"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-private-group-chat
|
||||
:author "0xabc"}
|
||||
{:id "0x6"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-private-group-chat}]])
|
||||
|
||||
(is (= {c/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x1"
|
||||
:read true
|
||||
:type c/activity-center-notification-type-one-to-one-chat
|
||||
:last-message {}}]}
|
||||
:unread {:data []}}
|
||||
c/activity-center-notification-type-private-group-chat
|
||||
{:read {:data []}
|
||||
:unread {:cursor ""
|
||||
:data [{:id "0x6"
|
||||
types/one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data []}
|
||||
:unread {:data [{:id "0x1"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-private-group-chat}
|
||||
{:id "0x4"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-private-group-chat
|
||||
:author "0xabc"}]}}}
|
||||
(get-in (h/db) [:activity-center :notifications])))))
|
||||
:type types/one-to-one-chat}]}}}
|
||||
(get-in (h/db) [:activity-center :notifications])))))
|
||||
|
||||
(testing "reconciles notifications that switched their read/unread status"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications]
|
||||
{c/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x1"
|
||||
:read true
|
||||
:type c/activity-center-notification-type-one-to-one-chat}]}}}])
|
||||
;; Sorting by timestamp and ID is compatible with what the backend does when
|
||||
;; returning paginated results.
|
||||
(testing "sorts notifications by timestamp and id in descending order"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications]
|
||||
{types/one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x1" :read true :type types/one-to-one-chat :timestamp 1}
|
||||
{:id "0x2" :read true :type types/one-to-one-chat :timestamp 1}]}
|
||||
:unread {:cursor ""
|
||||
:data [{:id "0x3" :read false :type types/one-to-one-chat :timestamp 50}
|
||||
{:id "0x4" :read false :type types/one-to-one-chat :timestamp 100}
|
||||
{:id "0x5" :read false :type types/one-to-one-chat :timestamp 100}]}}}])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/reconcile
|
||||
[{:id "0x1"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-one-to-one-chat}]])
|
||||
(rf/dispatch [:activity-center.notifications/reconcile
|
||||
[{:id "0x1" :read true :type types/one-to-one-chat :timestamp 1 :last-message {}}
|
||||
{:id "0x4" :read false :type types/one-to-one-chat :timestamp 100 :last-message {}}]])
|
||||
|
||||
(is (= {c/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data []}
|
||||
:unread {:data [{:id "0x1"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-one-to-one-chat}]}}}
|
||||
(get-in (h/db) [:activity-center :notifications])))))
|
||||
|
||||
;; Sorting by timestamp and ID is compatible with what the backend does when
|
||||
;; returning paginated results.
|
||||
(testing "sorts notifications by timestamp and id in descending order"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications]
|
||||
{c/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x1" :read true :type c/activity-center-notification-type-one-to-one-chat :timestamp 1}
|
||||
{:id "0x2" :read true :type c/activity-center-notification-type-one-to-one-chat :timestamp 1}]}
|
||||
:unread {:cursor ""
|
||||
:data [{:id "0x3" :read false :type c/activity-center-notification-type-one-to-one-chat :timestamp 50}
|
||||
{:id "0x4" :read false :type c/activity-center-notification-type-one-to-one-chat :timestamp 100}
|
||||
{:id "0x5" :read false :type c/activity-center-notification-type-one-to-one-chat :timestamp 100}]}}}])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/reconcile
|
||||
[{:id "0x1" :read true :type c/activity-center-notification-type-one-to-one-chat :timestamp 1 :last-message {}}
|
||||
{:id "0x4" :read false :type c/activity-center-notification-type-one-to-one-chat :timestamp 100 :last-message {}}]])
|
||||
|
||||
(is (= {c/activity-center-notification-type-one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x2"
|
||||
:read true
|
||||
:type c/activity-center-notification-type-one-to-one-chat
|
||||
:timestamp 1}
|
||||
{:id "0x1"
|
||||
(is (= {types/no-type
|
||||
{:read {:data [{:id "0x1"
|
||||
:read true
|
||||
:type c/activity-center-notification-type-one-to-one-chat
|
||||
:type types/one-to-one-chat
|
||||
:timestamp 1
|
||||
:last-message {}}]}
|
||||
:unread {:cursor ""
|
||||
:data [{:id "0x5"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-one-to-one-chat
|
||||
:timestamp 100}
|
||||
{:id "0x4"
|
||||
:unread {:data [{:id "0x4"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-one-to-one-chat
|
||||
:type types/one-to-one-chat
|
||||
:timestamp 100
|
||||
:last-message {}}
|
||||
{:id "0x3"
|
||||
:read false
|
||||
:type c/activity-center-notification-type-one-to-one-chat
|
||||
:timestamp 50}]}}}
|
||||
(get-in (h/db) [:activity-center :notifications]))))))
|
||||
:last-message {}}]}}
|
||||
types/one-to-one-chat
|
||||
{:read {:cursor ""
|
||||
:data [{:id "0x2"
|
||||
:read true
|
||||
:type types/one-to-one-chat
|
||||
:timestamp 1}
|
||||
{:id "0x1"
|
||||
:read true
|
||||
:type types/one-to-one-chat
|
||||
:timestamp 1
|
||||
:last-message {}}]}
|
||||
:unread {:cursor ""
|
||||
:data [{:id "0x5"
|
||||
:read false
|
||||
:type types/one-to-one-chat
|
||||
:timestamp 100}
|
||||
{:id "0x4"
|
||||
:read false
|
||||
:type types/one-to-one-chat
|
||||
:timestamp 100
|
||||
:last-message {}}
|
||||
{:id "0x3"
|
||||
:read false
|
||||
:type types/one-to-one-chat
|
||||
:timestamp 50}]}}}
|
||||
(get-in (h/db) [:activity-center :notifications])))))))
|
||||
|
||||
;;;; Notifications fetching and pagination
|
||||
|
||||
(deftest notifications-fetch-test
|
||||
(testing "fetches first page"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [spy-queue (atom [])]
|
||||
(h/stub-fx-with-callbacks
|
||||
::json-rpc/call
|
||||
:on-success (constantly {:cursor "10"
|
||||
:notifications [{:id "0x1"
|
||||
:type c/activity-center-notification-type-one-to-one-chat
|
||||
:read false
|
||||
:chatId "0x9"}]}))
|
||||
(h/spy-fx spy-queue ::json-rpc/call)
|
||||
(with-redefs [config/new-activity-center-enabled? true]
|
||||
(testing "fetches first page"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [spy-queue (atom [])]
|
||||
(h/stub-fx-with-callbacks
|
||||
::json-rpc/call
|
||||
:on-success (constantly {:cursor "10"
|
||||
:notifications [{:id "0x1"
|
||||
:type types/one-to-one-chat
|
||||
:read false
|
||||
:chatId "0x9"}]}))
|
||||
(h/spy-fx spy-queue ::json-rpc/call)
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/fetch-first-page
|
||||
{:filter-type c/activity-center-notification-type-one-to-one-chat}])
|
||||
(rf/dispatch [:activity-center.notifications/fetch-first-page
|
||||
{:filter-type types/one-to-one-chat}])
|
||||
|
||||
(is (= :unread (get-in (h/db) [:activity-center :filter :status])))
|
||||
(is (= "" (get-in @spy-queue [0 :args 0 :params 0]))
|
||||
"Should be called with empty cursor when fetching first page")
|
||||
(is (= {c/activity-center-notification-type-one-to-one-chat
|
||||
{:unread {:cursor "10"
|
||||
:data [{:chat-id "0x9"
|
||||
:chat-name nil
|
||||
:chat-type c/activity-center-notification-type-one-to-one-chat
|
||||
:group-chat false
|
||||
:id "0x1"
|
||||
:public? false
|
||||
:last-message nil
|
||||
:message nil
|
||||
:read false
|
||||
:reply-message nil
|
||||
:type c/activity-center-notification-type-one-to-one-chat}]}}}
|
||||
(remove-color-key (get-in (h/db) [:activity-center :notifications])
|
||||
{:status :unread
|
||||
:type c/activity-center-notification-type-one-to-one-chat}))))))
|
||||
(is (= :unread (get-in (h/db) [:activity-center :filter :status])))
|
||||
(is (= "" (get-in @spy-queue [0 :args 0 :params 0]))
|
||||
"Should be called with empty cursor when fetching first page")
|
||||
(is (= {types/one-to-one-chat
|
||||
{:unread {:cursor "10"
|
||||
:data [{:chat-id "0x9"
|
||||
:chat-name nil
|
||||
:chat-type types/one-to-one-chat
|
||||
:group-chat false
|
||||
:id "0x1"
|
||||
:public? false
|
||||
:last-message nil
|
||||
:message nil
|
||||
:read false
|
||||
:reply-message nil
|
||||
:type types/one-to-one-chat}]}}}
|
||||
(get-in (h/db) [:activity-center :notifications]))))))
|
||||
|
||||
(testing "does not fetch next page when pagination cursor reached the end"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [spy-queue (atom [])]
|
||||
(h/spy-fx spy-queue ::json-rpc/call)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
|
||||
:unread])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
|
||||
c/activity-center-notification-type-one-to-one-chat])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications c/activity-center-notification-type-one-to-one-chat :unread :cursor]
|
||||
""])
|
||||
(testing "does not fetch next page when pagination cursor reached the end"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [spy-queue (atom [])]
|
||||
(h/spy-fx spy-queue ::json-rpc/call)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
|
||||
:unread])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
|
||||
types/one-to-one-chat])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications types/one-to-one-chat :unread :cursor]
|
||||
""])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/fetch-next-page])
|
||||
(rf/dispatch [:activity-center.notifications/fetch-next-page])
|
||||
|
||||
(is (= [] @spy-queue)))))
|
||||
(is (= [] @spy-queue)))))
|
||||
|
||||
;; The cursor can be nil sometimes because the reconciliation doesn't care
|
||||
;; about updating the cursor value, but we have to make sure the next page is
|
||||
;; only fetched if the current cursor is valid.
|
||||
(testing "does not fetch next page when cursor is nil"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [spy-queue (atom [])]
|
||||
(h/spy-fx spy-queue ::json-rpc/call)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
|
||||
:unread])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
|
||||
c/activity-center-notification-type-one-to-one-chat])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications c/activity-center-notification-type-one-to-one-chat :unread :cursor]
|
||||
nil])
|
||||
;; The cursor can be nil sometimes because the reconciliation doesn't care
|
||||
;; about updating the cursor value, but we have to make sure the next page is
|
||||
;; only fetched if the current cursor is valid.
|
||||
(testing "does not fetch next page when cursor is nil"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [spy-queue (atom [])]
|
||||
(h/spy-fx spy-queue ::json-rpc/call)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
|
||||
:unread])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
|
||||
types/one-to-one-chat])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications types/one-to-one-chat :unread :cursor]
|
||||
nil])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/fetch-next-page])
|
||||
(rf/dispatch [:activity-center.notifications/fetch-next-page])
|
||||
|
||||
(is (= [] @spy-queue)))))
|
||||
(is (= [] @spy-queue)))))
|
||||
|
||||
(testing "fetches next page when pagination cursor is not empty"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [spy-queue (atom [])]
|
||||
(h/stub-fx-with-callbacks
|
||||
::json-rpc/call
|
||||
:on-success (constantly {:cursor ""
|
||||
:notifications [{:id "0x1"
|
||||
:type c/activity-center-notification-type-mention
|
||||
:read false
|
||||
:chatId "0x9"}]}))
|
||||
(h/spy-fx spy-queue ::json-rpc/call)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
|
||||
:unread])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
|
||||
c/activity-center-notification-type-mention])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications c/activity-center-notification-type-mention :unread :cursor]
|
||||
"10"])
|
||||
(testing "fetches next page when pagination cursor is not empty"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [spy-queue (atom [])]
|
||||
(h/stub-fx-with-callbacks
|
||||
::json-rpc/call
|
||||
:on-success (constantly {:cursor ""
|
||||
:notifications [{:id "0x1"
|
||||
:type types/mention
|
||||
:read false
|
||||
:chatId "0x9"}]}))
|
||||
(h/spy-fx spy-queue ::json-rpc/call)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
|
||||
:unread])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
|
||||
types/mention])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications types/mention :unread :cursor]
|
||||
"10"])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/fetch-next-page])
|
||||
(rf/dispatch [:activity-center.notifications/fetch-next-page])
|
||||
|
||||
(is (= "wakuext_unreadActivityCenterNotifications" (get-in @spy-queue [0 :args 0 :method])))
|
||||
(is (= "10" (get-in @spy-queue [0 :args 0 :params 0]))
|
||||
"Should be called with current cursor")
|
||||
(is (= {c/activity-center-notification-type-mention
|
||||
{:unread {:cursor ""
|
||||
:data [{:chat-id "0x9"
|
||||
:chat-name nil
|
||||
:chat-type 3
|
||||
:id "0x1"
|
||||
:last-message nil
|
||||
:message nil
|
||||
:read false
|
||||
:reply-message nil
|
||||
:type c/activity-center-notification-type-mention}]}}}
|
||||
(remove-color-key (get-in (h/db) [:activity-center :notifications])
|
||||
{:status :unread
|
||||
:type c/activity-center-notification-type-mention}))))))
|
||||
(is (= "wakuext_unreadActivityCenterNotifications" (get-in @spy-queue [0 :args 0 :method])))
|
||||
(is (= "10" (get-in @spy-queue [0 :args 0 :params 0]))
|
||||
"Should be called with current cursor")
|
||||
(is (= {types/mention
|
||||
{:unread {:cursor ""
|
||||
:data [{:chat-id "0x9"
|
||||
:chat-name nil
|
||||
:chat-type 3
|
||||
:id "0x1"
|
||||
:last-message nil
|
||||
:message nil
|
||||
:read false
|
||||
:reply-message nil
|
||||
:type types/mention}]}}}
|
||||
(get-in (h/db) [:activity-center :notifications]))))))
|
||||
|
||||
(testing "does not fetch next page while it is still loading"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [spy-queue (atom [])]
|
||||
(h/spy-fx spy-queue ::json-rpc/call)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
|
||||
:read])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
|
||||
c/activity-center-notification-type-one-to-one-chat])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications c/activity-center-notification-type-one-to-one-chat :read :cursor]
|
||||
"10"])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications c/activity-center-notification-type-one-to-one-chat :read :loading?]
|
||||
true])
|
||||
(testing "does not fetch next page while it is still loading"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [spy-queue (atom [])]
|
||||
(h/spy-fx spy-queue ::json-rpc/call)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
|
||||
:read])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
|
||||
types/one-to-one-chat])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications types/one-to-one-chat :read :cursor]
|
||||
"10"])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications types/one-to-one-chat :read :loading?]
|
||||
true])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/fetch-next-page])
|
||||
(rf/dispatch [:activity-center.notifications/fetch-next-page])
|
||||
|
||||
(is (= [] @spy-queue)))))
|
||||
(is (= [] @spy-queue)))))
|
||||
|
||||
(testing "resets loading flag after an error"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [spy-queue (atom [])]
|
||||
(h/stub-fx-with-callbacks ::json-rpc/call :on-error (constantly :fake-error))
|
||||
(h/spy-event-fx spy-queue :activity-center.notifications/fetch-error)
|
||||
(h/spy-fx spy-queue ::json-rpc/call)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
|
||||
:unread])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
|
||||
c/activity-center-notification-type-one-to-one-chat])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications c/activity-center-notification-type-one-to-one-chat :unread :cursor]
|
||||
""])
|
||||
(testing "resets loading flag after an error"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [spy-queue (atom [])]
|
||||
(h/stub-fx-with-callbacks ::json-rpc/call :on-error (constantly :fake-error))
|
||||
(h/spy-event-fx spy-queue :activity-center.notifications/fetch-error)
|
||||
(h/spy-fx spy-queue ::json-rpc/call)
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :status]
|
||||
:unread])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :filter :type]
|
||||
types/one-to-one-chat])
|
||||
(rf/dispatch [:test/assoc-in [:activity-center :notifications types/one-to-one-chat :unread :cursor]
|
||||
""])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/fetch-first-page])
|
||||
(rf/dispatch [:activity-center.notifications/fetch-first-page])
|
||||
|
||||
(is (nil? (get-in (h/db) [:activity-center :notifications c/activity-center-notification-type-one-to-one-chat :unread :loading?])))
|
||||
(is (= [:activity-center.notifications/fetch-error
|
||||
c/activity-center-notification-type-one-to-one-chat
|
||||
:unread
|
||||
:fake-error]
|
||||
(:args (last @spy-queue))))))))
|
||||
(is (nil? (get-in (h/db) [:activity-center :notifications types/one-to-one-chat :unread :loading?])))
|
||||
(is (= [:activity-center.notifications/fetch-error
|
||||
types/one-to-one-chat
|
||||
:unread
|
||||
:fake-error]
|
||||
(:args (last @spy-queue)))))))))
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
(ns status-im.activity-center.notification-types)
|
||||
|
||||
(def ^:const no-type 0)
|
||||
(def ^:const one-to-one-chat 1)
|
||||
(def ^:const private-group-chat 2)
|
||||
(def ^:const mention 3)
|
||||
(def ^:const reply 4)
|
||||
(def ^:const contact-request 5)
|
||||
(def ^:const contact-verification 6)
|
||||
|
||||
;; TODO: Remove this constant once the old Notification Center code is removed.
|
||||
;; Its value clashes with the new constant `-contact-verification`
|
||||
;; used in status-go.
|
||||
(def ^:const contact-request-retracted 6)
|
||||
|
||||
;; TODO: Replace with correct enum values once status-go implements them.
|
||||
(def ^:const admin 66610)
|
||||
(def ^:const tx 66612)
|
||||
(def ^:const membership 66613)
|
||||
(def ^:const system 66614)
|
||||
@@ -176,6 +176,11 @@
|
||||
:on-error #(log/error "failed to clear history " chat-id %)}]}
|
||||
(clear-history chat-id remove-chat?)))
|
||||
|
||||
(fx/defn chat-deactivated
|
||||
{:events [::chat-deactivated]}
|
||||
[_ chat-id]
|
||||
(log/debug "chat deactivated" chat-id))
|
||||
|
||||
(fx/defn deactivate-chat
|
||||
"Deactivate chat in db, no side effects"
|
||||
[{:keys [db now] :as cofx} chat-id]
|
||||
@@ -185,10 +190,10 @@
|
||||
(assoc-in db [:chats chat-id :active] false)
|
||||
(update db :chats dissoc chat-id))
|
||||
(update :chats-home-list disj chat-id)
|
||||
(assoc-in [:current-chat-id] nil))
|
||||
(assoc :current-chat-id nil))
|
||||
::json-rpc/call [{:method "wakuext_deactivateChat"
|
||||
:params [{:id chat-id}]
|
||||
:on-success #(log/debug "chat deactivated" chat-id)
|
||||
:on-success #(re-frame/dispatch [::chat-deactivated chat-id])
|
||||
:on-error #(log/error "failed to create public chat" chat-id %)}]}
|
||||
(clear-history chat-id true)))
|
||||
|
||||
@@ -227,9 +232,7 @@
|
||||
{:clear-message-notifications
|
||||
[[chat-id] (get-in db [:multiaccount :remote-push-notifications-enabled?])]}
|
||||
(deactivate-chat chat-id)
|
||||
(offload-messages chat-id)
|
||||
(when (not (= (:view-id db) :home))
|
||||
(navigation/pop-to-root-tab :chat-stack))))
|
||||
(offload-messages chat-id)))
|
||||
|
||||
(fx/defn show-more-chats
|
||||
{:events [:chat.ui/show-more-chats]}
|
||||
@@ -265,6 +268,7 @@
|
||||
[{db :db :as cofx} chat-id from-switcher?]
|
||||
(fx/merge cofx
|
||||
{:db (assoc db :current-chat-id chat-id)}
|
||||
(offload-messages chat-id)
|
||||
(preload-chat-data chat-id)
|
||||
(navigation2/navigate-to-nav2 :chat chat-id nil from-switcher?)))
|
||||
|
||||
@@ -384,6 +388,11 @@
|
||||
(log/error "mute chat failed" chat-id error)
|
||||
{:db (assoc-in db [:chats chat-id :muted] (not muted?))})
|
||||
|
||||
(fx/defn mute-chat-toggled-successfully
|
||||
{:events [::mute-chat-toggled-successfully]}
|
||||
[_ chat-id]
|
||||
(log/debug "muted chat successfully" chat-id))
|
||||
|
||||
(fx/defn mute-chat
|
||||
{:events [::mute-chat-toggled]}
|
||||
[{:keys [db] :as cofx} chat-id muted?]
|
||||
@@ -392,7 +401,7 @@
|
||||
::json-rpc/call [{:method method
|
||||
:params [chat-id]
|
||||
:on-error #(re-frame/dispatch [::mute-chat-failed chat-id muted? %])
|
||||
:on-success #(log/debug "muted chat successfully")}]}))
|
||||
:on-success #(re-frame/dispatch [::mute-chat-toggled-successfully chat-id])}]}))
|
||||
|
||||
(fx/defn show-profile
|
||||
{:events [:chat.ui/show-profile]}
|
||||
@@ -496,4 +505,4 @@
|
||||
(update-in [:chats chat-id :unviewed-messages-count]
|
||||
#(max (- % count) 0))
|
||||
(update-in [:chats chat-id :unviewed-mentions-count]
|
||||
#(max (- % countWithMentions) 0)))})
|
||||
#(max (- % countWithMentions) 0)))})
|
||||
|
||||
@@ -91,7 +91,8 @@
|
||||
:pinned (pin-message :pinned)})
|
||||
(when pinned
|
||||
(protocol/send-chat-messages [{:chat-id (pin-message :chat-id)
|
||||
:content-type constants/content-type-pin
|
||||
:content-type constants/content-type-system-text
|
||||
:text "pinned a message"
|
||||
:response-to (pin-message :message-id)
|
||||
:ens-name preferred-name}])))))
|
||||
|
||||
@@ -99,3 +100,16 @@
|
||||
{:events [::load-pin-messages]}
|
||||
[{:keys [db] :as cofx} chat-id]
|
||||
(load-more-pin-messages cofx chat-id true))
|
||||
|
||||
(fx/defn show-pin-limit-modal
|
||||
{:events [::show-pin-limit-modal]}
|
||||
[{:keys [db] :as cofx} chat-id]
|
||||
(fx/merge
|
||||
{:db (assoc-in db [:pin-modal chat-id] true)}))
|
||||
|
||||
(fx/defn hide-pin-limit-modal
|
||||
{:events [::hide-pin-limit-modal]}
|
||||
[{:keys [db] :as cofx} chat-id]
|
||||
(fx/merge
|
||||
{:db (assoc-in db [:pin-modal chat-id] false)}))
|
||||
|
||||
|
||||
@@ -84,3 +84,10 @@
|
||||
:group-chat true}
|
||||
"opened" {}
|
||||
"1-1" {}}})
|
||||
|
||||
(deftest navigate-to-chat-nav2
|
||||
(let [chat-id "test_chat"
|
||||
db {:pagination-info {chat-id {:all-loaded? true}}}]
|
||||
(testing "Pagination info should be reset on navigation"
|
||||
(let [res (chat/navigate-to-chat-nav2 {:db db} chat-id false)]
|
||||
(is (nil? (get-in res [:db :pagination-info chat-id :all-loaded?])))))))
|
||||
|
||||
@@ -655,7 +655,7 @@
|
||||
{:db (update-in db [:communities community-id :categories] merge categories)}))
|
||||
|
||||
(fx/defn load-category-states
|
||||
{:events [::load-category-states]}
|
||||
{:events [:communities/load-category-states]}
|
||||
[{:keys [db]} community-id]
|
||||
(let [public-key (get-in db [:multiaccount :public-key])
|
||||
categories (get-in db [:communities community-id :categories])
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
(def ^:const content-type-community 9)
|
||||
(def ^:const content-type-gap 10)
|
||||
(def ^:const content-type-contact-request 11) ;; TODO: temp, will be removed
|
||||
(def ^:const content-type-pin 13)
|
||||
|
||||
(def ^:const contact-request-state-none 0)
|
||||
(def ^:const contact-request-state-mutual 1)
|
||||
@@ -22,6 +21,13 @@
|
||||
(def ^:const contact-request-state-received 3)
|
||||
(def ^:const contact-request-state-dismissed 4)
|
||||
|
||||
(def ^:const contact-verification-state-unknown 0)
|
||||
(def ^:const contact-verification-state-pending 1)
|
||||
(def ^:const contact-verification-state-accepted 2)
|
||||
(def ^:const contact-verification-state-declined 3)
|
||||
(def ^:const contact-verification-state-cancelled 4)
|
||||
(def ^:const contact-verification-state-trusted 5)
|
||||
|
||||
(def ^:const emoji-reaction-love 1)
|
||||
(def ^:const emoji-reaction-thumbs-up 2)
|
||||
(def ^:const emoji-reaction-thumbs-down 3)
|
||||
@@ -177,21 +183,6 @@
|
||||
(def ^:const docs-link "https://status.im/docs/")
|
||||
(def ^:const principles-link "https://our.status.im/our-principles/")
|
||||
|
||||
(def ^:const activity-center-notification-type-no-type 0)
|
||||
(def ^:const activity-center-notification-type-one-to-one-chat 1)
|
||||
(def ^:const activity-center-notification-type-private-group-chat 2)
|
||||
(def ^:const activity-center-notification-type-mention 3)
|
||||
(def ^:const activity-center-notification-type-reply 4)
|
||||
(def ^:const activity-center-notification-type-contact-request 5)
|
||||
(def ^:const activity-center-notification-type-contact-request-retracted 6)
|
||||
|
||||
;; TODO: Replace with correct enum values once status-go implements them.
|
||||
(def ^:const activity-center-notification-type-admin 66610)
|
||||
(def ^:const activity-center-notification-type-identity-verification 66611)
|
||||
(def ^:const activity-center-notification-type-tx 66612)
|
||||
(def ^:const activity-center-notification-type-membership 66613)
|
||||
(def ^:const activity-center-notification-type-system 66614)
|
||||
|
||||
(def ^:const visibility-status-unknown 0)
|
||||
(def ^:const visibility-status-automatic 1)
|
||||
(def ^:const visibility-status-dnd 2)
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
[status-im.utils.logging.core :as utils.logs]
|
||||
[status-im.utils.platform :as platform]
|
||||
[status-im.utils.snoopy :as snoopy]
|
||||
[status-im.switcher.animation :as animation]
|
||||
[status-im.async-storage.core :as async-storage]
|
||||
[status-im.utils.universal-links.core :as utils.universal-links]))
|
||||
|
||||
(set! interop/next-tick js/setTimeout)
|
||||
@@ -39,6 +41,9 @@
|
||||
|
||||
(utils.universal-links/initialize)
|
||||
|
||||
;; TODO(parvesh) - Remove while moving functionality to status-go
|
||||
(async-storage/get-item :selected-stack-id #(animation/selected-stack-id-loaded %))
|
||||
|
||||
;;DEV
|
||||
(snoopy/subscribe!)
|
||||
(when (and js/goog.DEBUG platform/ios? DevSettings)
|
||||
|
||||
@@ -1,46 +1,49 @@
|
||||
(ns status-im.data-store.activities
|
||||
(:require [status-im.data-store.messages :as messages]
|
||||
[status-im.constants :as constants]
|
||||
(:require [clojure.set :as set]
|
||||
[quo.design-system.colors :as colors]
|
||||
clojure.set))
|
||||
[status-im.constants :as constants]
|
||||
[status-im.activity-center.notification-types :as notification-types]
|
||||
[status-im.data-store.messages :as messages]
|
||||
[status-im.utils.config :as config]))
|
||||
|
||||
(defn rpc->type [{:keys [type name] :as chat}]
|
||||
(cond
|
||||
(= constants/activity-center-notification-type-reply type)
|
||||
(defn- rpc->type [{:keys [type name] :as chat}]
|
||||
(case type
|
||||
notification-types/reply
|
||||
(assoc chat
|
||||
:chat-name name
|
||||
:chat-type constants/private-group-chat-type)
|
||||
|
||||
(= constants/activity-center-notification-type-mention type)
|
||||
notification-types/mention
|
||||
(assoc chat
|
||||
:chat-type constants/private-group-chat-type
|
||||
:chat-name name)
|
||||
|
||||
(= constants/activity-center-notification-type-private-group-chat type)
|
||||
notification-types/private-group-chat
|
||||
(assoc chat
|
||||
:chat-type constants/private-group-chat-type
|
||||
:chat-name name
|
||||
:public? false
|
||||
:group-chat true)
|
||||
|
||||
(= constants/activity-center-notification-type-one-to-one-chat type)
|
||||
notification-types/one-to-one-chat
|
||||
(assoc chat
|
||||
:chat-type constants/one-to-one-chat-type
|
||||
:chat-name name
|
||||
:public? false
|
||||
:group-chat false)
|
||||
|
||||
:else
|
||||
chat))
|
||||
|
||||
(defn <-rpc [item]
|
||||
(-> item
|
||||
rpc->type
|
||||
(clojure.set/rename-keys {:lastMessage :last-message
|
||||
:replyMessage :reply-message
|
||||
:chatId :chat-id})
|
||||
(assoc :color (rand-nth colors/chat-colors))
|
||||
(update :last-message #(when % (messages/<-rpc %)))
|
||||
(update :message #(when % (messages/<-rpc %)))
|
||||
(update :reply-message #(when % (messages/<-rpc %)))
|
||||
(dissoc :chatId)))
|
||||
(cond-> (-> item
|
||||
rpc->type
|
||||
(set/rename-keys {:lastMessage :last-message
|
||||
:replyMessage :reply-message
|
||||
:chatId :chat-id
|
||||
:contactVerificationStatus :contact-verification-status})
|
||||
(update :last-message #(when % (messages/<-rpc %)))
|
||||
(update :message #(when % (messages/<-rpc %)))
|
||||
(update :reply-message #(when % (messages/<-rpc %)))
|
||||
(dissoc :chatId))
|
||||
(not config/new-activity-center-enabled?)
|
||||
(assoc :color (rand-nth colors/chat-colors))))
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user