Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e08529da0 | ||
|
|
95d927c941 | ||
|
|
9dfb855090 | ||
|
|
775d9e5457 | ||
|
|
294eb34418 | ||
|
|
da0f0d3a81 | ||
|
|
f7af7ca25d | ||
|
|
f3ad8beb6d | ||
|
|
2e68fb22f5 | ||
|
|
f18044c9dc | ||
|
|
5947769881 | ||
|
|
d4897de205 | ||
|
|
d948939ce3 | ||
|
|
1000cb79a2 | ||
|
|
f55b646c20 | ||
|
|
bc7578ae85 | ||
|
|
49e9738ff8 | ||
|
|
9e1412007f | ||
|
|
c198133769 | ||
|
|
6d5ff4aa92 | ||
|
|
0e614d51ef | ||
|
|
42de2a6384 | ||
|
|
3d71fdba98 | ||
|
|
9de4417f03 | ||
|
|
9e7db45d08 | ||
|
|
14c243803f | ||
|
|
e61889f16c | ||
|
|
1230594046 | ||
|
|
3fc1ba357e | ||
|
|
3152cf0aa3 | ||
|
|
c6e8aad745 | ||
|
|
bab0fc7ac0 | ||
|
|
32d85d5059 | ||
|
|
a9295ac17e |
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"presets": [
|
||||
"module:metro-react-native-babel-preset"
|
||||
],
|
||||
"plugins": [
|
||||
"react-native-reanimated/plugin"
|
||||
]
|
||||
}
|
||||
@@ -200,7 +200,7 @@ status-im.utils.platform/isMacOs?
|
||||
status-im.utils.platform/isNix?
|
||||
status-im.utils.platform/isWin?
|
||||
status-im.utils.platform/android-version>=
|
||||
status-im.utils.debounce/clear-all
|
||||
utils.debounce/clear-all
|
||||
status-im.transport.db/create-chat
|
||||
status-im.utils.priority-map/priority-map
|
||||
status-im.utils.priority-map/priority-map-by
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
# Xcode
|
||||
#
|
||||
/component-spec
|
||||
result/
|
||||
build/
|
||||
*.pbxuser
|
||||
@@ -177,4 +178,6 @@ test/appium/tests/users.py
|
||||
/lib/
|
||||
|
||||
## visual tests
|
||||
/artifacts
|
||||
/artifacts
|
||||
|
||||
/.calva/
|
||||
|
||||
@@ -307,7 +307,7 @@ test-watch: ##@ Watch tests and re-run no changes to cljs files
|
||||
|
||||
test: export TARGET := clojure
|
||||
test: ##@test Run tests once in NodeJS
|
||||
# Here we creates the gyp bindings for nodejs
|
||||
# Here we create the gyp bindings for nodejs
|
||||
yarn install
|
||||
yarn shadow-cljs compile mocks && \
|
||||
yarn shadow-cljs compile test && \
|
||||
@@ -316,14 +316,27 @@ test: ##@test Run tests once in NodeJS
|
||||
|
||||
run-visual-test-ios: export TARGET := clojure
|
||||
run-visual-test-ios: XCODE_DERIVED_DATA := $(HOME)/Library/Developer/Xcode/DerivedData
|
||||
run-visual-test-ios: APPLICATION_NAME := $(shell ls $(XCODE_DERIVED_DATA) | grep -E '\bStatusIm-')
|
||||
run-visual-test-ios: APPLICATION_NAME := StatusIm-brfnruzfrkkycpbndmdoeyrigthc
|
||||
run-visual-test-ios: export TEST_BINARY_PATH := $(XCODE_DERIVED_DATA)/$(APPLICATION_NAME)/Build/Products/Debug-iphonesimulator/StatusIm.app
|
||||
run-visual-test-ios: ##@test Run tests once in NodeJS
|
||||
detox test --configuration ios.sim.debug
|
||||
|
||||
component-test-watch: export TARGET := clojure
|
||||
component-test-watch: export COMPONENT_TEST := true
|
||||
component-test-watch: export BABEL_ENV := test
|
||||
component-test-watch: ##@ Watch tests and re-run no changes to cljs files
|
||||
yarn install
|
||||
detox build --configuration ios.sim.debug && \
|
||||
detox test --configuration ios.sim.debug
|
||||
nodemon --exec 'yarn shadow-cljs compile component-test && jest --config=test/jest/jest.config.js' -e cljs
|
||||
|
||||
component-test: export TARGET := clojure
|
||||
component-test: export COMPONENT_TEST := true
|
||||
component-test: export BABEL_ENV := test
|
||||
component-test: ##@test Run tests once in NodeJS
|
||||
# Here we create the gyp bindings for nodejs
|
||||
yarn install
|
||||
yarn shadow-cljs compile component-test && \
|
||||
jest --config=test/jest/jest.config.js
|
||||
|
||||
#--------------
|
||||
# Other
|
||||
#--------------
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
module.exports = {
|
||||
|
||||
"presets": [
|
||||
"module:metro-react-native-babel-preset"
|
||||
],
|
||||
"plugins": [
|
||||
"react-native-reanimated/plugin"
|
||||
],
|
||||
"env": {
|
||||
"test": {
|
||||
"presets": [
|
||||
'@babel/preset-react',
|
||||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
targets: {
|
||||
node: '14',
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
library 'status-jenkins-lib@v1.6.2'
|
||||
library 'status-jenkins-lib@v1.6.3'
|
||||
|
||||
/* Options section can't access functions in objects. */
|
||||
def isPRBuild = utils.isPRBuild()
|
||||
@@ -36,7 +36,7 @@ pipeline {
|
||||
LANG = "en_US.UTF-8"
|
||||
LC_ALL = "en_US.UTF-8"
|
||||
LANGUAGE = "en_US.UTF-8"
|
||||
TARGET = 'android'
|
||||
TARGET = "android${utils.isE2EBuild() ? "-e2e" : ""}"
|
||||
BUILD_ENV = 'prod'
|
||||
NIX_CONF_DIR = "${env.WORKSPACE}/nix"
|
||||
FASTLANE_DISABLE_COLORS = 1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
library 'status-jenkins-lib@v1.6.2'
|
||||
library 'status-jenkins-lib@v1.6.3'
|
||||
|
||||
pipeline {
|
||||
agent { label 'linux' }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
library 'status-jenkins-lib@v1.6.2'
|
||||
library 'status-jenkins-lib@v1.6.3'
|
||||
|
||||
/* Options section can't access functions in objects. */
|
||||
def isPRBuild = utils.isPRBuild()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
library 'status-jenkins-lib@v1.6.2'
|
||||
library 'status-jenkins-lib@v1.6.3'
|
||||
|
||||
pipeline {
|
||||
agent { label params.AGENT_LABEL }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
library 'status-jenkins-lib@v1.6.2'
|
||||
library 'status-jenkins-lib@v1.6.3'
|
||||
|
||||
/* Options section can't access functions in objects. */
|
||||
def isPRBuild = utils.isPRBuild()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
library 'status-jenkins-lib@v1.6.2'
|
||||
library 'status-jenkins-lib@v1.6.3'
|
||||
|
||||
pipeline {
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
library 'status-jenkins-lib@v1.6.2'
|
||||
library 'status-jenkins-lib@v1.6.3'
|
||||
|
||||
pipeline {
|
||||
agent { label 'macos' }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
library 'status-jenkins-lib@v1.6.2'
|
||||
library 'status-jenkins-lib@v1.6.3'
|
||||
|
||||
pipeline {
|
||||
agent { label 'linux' }
|
||||
|
||||
@@ -62,6 +62,31 @@ with the source file using it. For a real example, see
|
||||
(do-something)]])
|
||||
```
|
||||
|
||||
#### Styles def vs defn
|
||||
|
||||
Always use `def` over `defn`, unless the style relies on dynamic values, such as
|
||||
deref'ed atoms.
|
||||
|
||||
```clojure
|
||||
;; bad
|
||||
(defn title-column []
|
||||
{:height 56})
|
||||
|
||||
;; good
|
||||
(def title-column
|
||||
{:height 56})
|
||||
```
|
||||
|
||||
```clojure
|
||||
;; bad
|
||||
(def community-card
|
||||
{:background-color (colors/theme-colors colors/white colors/neutral-90)})
|
||||
|
||||
;; good
|
||||
(defn community-card []
|
||||
{:background-color (colors/theme-colors colors/white colors/neutral-90)})
|
||||
```
|
||||
|
||||
### Using TODOs comments
|
||||
|
||||
_TODO_ comments are used extensively in the codebase, but prefer to use them
|
||||
@@ -249,6 +274,31 @@ test macros `deftest` and `is`, which are ubiquitous in the Clojure community.
|
||||
(:require [cljs-time.coerce :as time.coerce]))
|
||||
```
|
||||
|
||||
### Namespace aliases
|
||||
|
||||
Prefer the following namespace aliases:
|
||||
|
||||
```clojure
|
||||
[clojure.string :as string]
|
||||
[taoensso.timbre :as log]
|
||||
```
|
||||
|
||||
### Javascript interop
|
||||
|
||||
Use [binaryage/oops](https://github.com/binaryage/cljs-oops) macros instead of
|
||||
core interop macros.
|
||||
|
||||
```clojure
|
||||
;; bad
|
||||
(fn [^js event]
|
||||
(.-width (.-nativeEvent event)))
|
||||
|
||||
;; good
|
||||
(require '[oops.core :as oops])
|
||||
(fn [event]
|
||||
(oops/oget event "nativeEvent.width"))
|
||||
```
|
||||
|
||||
### Accessibility labels
|
||||
|
||||
Use keywords instead of strings. As a bonus, remember keywords are cached in
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
package im.status.ethereum.module;
|
||||
|
||||
import android.view.ActionMode;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.uimanager.NativeViewHierarchyManager;
|
||||
import com.facebook.react.uimanager.UIBlock;
|
||||
import com.facebook.react.uimanager.UIManagerModule;
|
||||
import com.facebook.react.views.textinput.ReactEditText;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
class RNSelectableTextInputModule extends ReactContextBaseJavaModule {
|
||||
|
||||
private ActionMode lastActionMode;
|
||||
|
||||
public RNSelectableTextInputModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getName() {
|
||||
return "RNSelectableTextInputManager";
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void setupMenuItems(final Integer selectableTextViewReactTag, final Integer textInputReactTag) {
|
||||
ReactApplicationContext reactContext = this.getReactApplicationContext();
|
||||
UIManagerModule uiManager = reactContext.getNativeModule(UIManagerModule.class);
|
||||
uiManager.addUIBlock(new UIBlock() {
|
||||
public void execute (NativeViewHierarchyManager nvhm) {
|
||||
RNSelectableTextInputViewManager rnSelectableTextManager = (RNSelectableTextInputViewManager) nvhm.resolveViewManager(selectableTextViewReactTag);
|
||||
ReactEditText reactTextView = (ReactEditText) nvhm.resolveView(textInputReactTag);
|
||||
rnSelectableTextManager.registerSelectionListener(reactTextView);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void startActionMode(final Integer textInputReactTag) {
|
||||
ReactApplicationContext reactContext = this.getReactApplicationContext();
|
||||
UIManagerModule uiManager = reactContext.getNativeModule(UIManagerModule.class);
|
||||
uiManager.addUIBlock(new UIBlock() {
|
||||
public void execute (NativeViewHierarchyManager nvhm) {
|
||||
ReactEditText reactTextView = (ReactEditText) nvhm.resolveView(textInputReactTag);
|
||||
lastActionMode = reactTextView.startActionMode(reactTextView.getCustomSelectionActionModeCallback(), ActionMode.TYPE_FLOATING);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void hideLastActionMode(){
|
||||
ReactApplicationContext reactContext = this.getReactApplicationContext();
|
||||
UIManagerModule uiManager = reactContext.getNativeModule(UIManagerModule.class);
|
||||
uiManager.addUIBlock(new UIBlock() {
|
||||
public void execute (NativeViewHierarchyManager nvhm) {
|
||||
if(lastActionMode!=null){
|
||||
lastActionMode.finish();
|
||||
lastActionMode = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void setSelection(final Integer textInputReactTag, final Integer start, final Integer end){
|
||||
ReactApplicationContext reactContext = this.getReactApplicationContext();
|
||||
UIManagerModule uiManager = reactContext.getNativeModule(UIManagerModule.class);
|
||||
uiManager.addUIBlock(new UIBlock() {
|
||||
public void execute (NativeViewHierarchyManager nvhm) {
|
||||
ReactEditText reactTextView = (ReactEditText) nvhm.resolveView(textInputReactTag);
|
||||
reactTextView.setSelection(start, end);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package im.status.ethereum.module;
|
||||
|
||||
import android.view.ActionMode;
|
||||
import android.view.ActionMode.Callback;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.common.MapBuilder;
|
||||
import com.facebook.react.uimanager.ThemedReactContext;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
import com.facebook.react.uimanager.events.RCTEventEmitter;
|
||||
import com.facebook.react.views.textinput.ReactEditText;
|
||||
import com.facebook.react.views.view.ReactViewGroup;
|
||||
import com.facebook.react.views.view.ReactViewManager;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class RNSelectableTextInputViewManager extends ReactViewManager {
|
||||
public static final String REACT_CLASS = "RNSelectableTextInput";
|
||||
private String[] _menuItems = new String[0];
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return REACT_CLASS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactViewGroup createViewInstance(ThemedReactContext context) {
|
||||
return new ReactViewGroup(context);
|
||||
}
|
||||
|
||||
@ReactProp(name = "menuItems")
|
||||
public void setMenuItems(ReactViewGroup reactViewGroup, ReadableArray items) {
|
||||
if(items != null) {
|
||||
List<String> result = new ArrayList<String>(items.size());
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
result.add(items.getString(i));
|
||||
}
|
||||
this._menuItems = result.toArray(new String[items.size()]);
|
||||
}
|
||||
}
|
||||
|
||||
public void registerSelectionListener(final ReactEditText view) {
|
||||
view.setCustomSelectionActionModeCallback(new Callback() {
|
||||
@Override
|
||||
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
|
||||
menu.clear();
|
||||
for (int i = 0; i < _menuItems.length; i++) {
|
||||
menu.add(0, i, 0, _menuItems[i]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyActionMode(ActionMode mode) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
||||
int selectionStart = view.getSelectionStart();
|
||||
int selectionEnd = view.getSelectionEnd();
|
||||
String selectedText = view.getText().toString().substring(selectionStart, selectionEnd);
|
||||
|
||||
// Dispatch event
|
||||
onSelectNativeEvent(view, item.getItemId(), selectedText, selectionStart, selectionEnd);
|
||||
|
||||
mode.finish();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public void onSelectNativeEvent(ReactEditText view, int eventType, String content, int selectionStart, int selectionEnd) {
|
||||
WritableMap event = Arguments.createMap();
|
||||
event.putInt("eventType", eventType);
|
||||
event.putString("content", content);
|
||||
event.putInt("selectionStart", selectionStart);
|
||||
event.putInt("selectionEnd", selectionEnd);
|
||||
|
||||
// Dispatch
|
||||
ReactContext reactContext = (ReactContext) view.getContext();
|
||||
reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(view.getId(), "topSelection", event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map getExportedCustomDirectEventTypeConstants() {
|
||||
return MapBuilder.builder()
|
||||
.put("topSelection", MapBuilder.of("registrationName","onSelection"))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.uimanager.ViewManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@@ -29,12 +30,15 @@ public class StatusPackage implements ReactPackage {
|
||||
List<NativeModule> modules = new ArrayList<>();
|
||||
|
||||
modules.add(new StatusModule(reactContext, this.rootedDevice));
|
||||
modules.add(new RNSelectableTextInputModule(reactContext));
|
||||
|
||||
return modules;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
||||
return Collections.emptyList();
|
||||
return Arrays.<ViewManager>asList(
|
||||
new RNSelectableTextInputViewManager()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,8 +45,8 @@ in stdenv.mkDerivation rec {
|
||||
filter = lib.mkFilter {
|
||||
root = path;
|
||||
include = [
|
||||
"package.json" "yarn.lock" "metro.config.js" ".babelrc"
|
||||
"resources/.*" "translations/.*" "src/js/worklet_factory.js"
|
||||
"package.json" "yarn.lock" "metro.config.js" "babel.config.js"
|
||||
"resources/.*" "translations/.*" "src/js/.*"
|
||||
"modules/react-native-status/android.*" "android/.*"
|
||||
envFileName "VERSION" "status-go-version.json" "react-native.config.js"
|
||||
];
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
"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",
|
||||
"rn-emoji-keyboard": "0.7.0",
|
||||
"tdigest": "^0.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -82,15 +82,21 @@
|
||||
"@babel/helper-builder-react-jsx": "7.0.0",
|
||||
"@babel/plugin-transform-block-scoping": "7.0.0",
|
||||
"@babel/preset-env": "7.1.0",
|
||||
"@babel/preset-react": "^7.18.6",
|
||||
"@babel/register": "7.0.0",
|
||||
"@jest/globals": "^25.1.0",
|
||||
"@mapbox/node-pre-gyp": "^1.0.9",
|
||||
"@testing-library/jest-native": "^5.0.0",
|
||||
"@testing-library/react-native": "^11.2.0",
|
||||
"@types/jest": "^28.1.6",
|
||||
"detox": "^19.9.1",
|
||||
"jest": "^25.1.0",
|
||||
"jest-circus": "^26.0.0",
|
||||
"jest-image-snapshot": "^5.1.0",
|
||||
"jest": "^28.1.3",
|
||||
"nodemon": "^2.0.16",
|
||||
"nyc": "^14.1.1",
|
||||
"process": "0.11.10",
|
||||
"react-test-renderer": "16.13.1",
|
||||
"rn-snoopy": "git+https://github.com/status-im/rn-snoopy.git#refs/tags/v2.0.2-status",
|
||||
"shadow-cljs": "2.11.16"
|
||||
},
|
||||
|
||||
|
After Width: | Height: | Size: 469 B |
|
After Width: | Height: | Size: 758 B |
|
After Width: | Height: | Size: 525 B |
|
After Width: | Height: | Size: 759 B |
|
After Width: | Height: | Size: 924 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 12 KiB |
@@ -64,15 +64,15 @@
|
||||
{:closure-defines
|
||||
{status-im.utils.config/POKT_TOKEN #shadow/env "POKT_TOKEN"
|
||||
status-im.utils.config/OPENSEA_API_KEY #shadow/env "OPENSEA_API_KEY"}
|
||||
:compiler-options {:output-feature-set :es6
|
||||
:compiler-options {:output-feature-set :es6
|
||||
;;disable for android build as there
|
||||
;;is an intermittent warning with deftype
|
||||
:warnings-as-errors false
|
||||
:infer-externs :auto
|
||||
:static-fns true
|
||||
:fn-invoke-direct true
|
||||
:optimizations :advanced
|
||||
:js-options {:js-provider :closure}}}}
|
||||
:warnings-as-errors false
|
||||
:infer-externs :auto
|
||||
:static-fns true
|
||||
:fn-invoke-direct true
|
||||
:optimizations :advanced
|
||||
:js-options {:js-provider :closure}}}}
|
||||
;; the tests are ran with node, react-native dependencies are mocked
|
||||
;; by using node --require override.js, which uses the node-library
|
||||
;; produced by the target :mocks below and redefines node require
|
||||
@@ -82,6 +82,7 @@
|
||||
:output-dir "target/test"
|
||||
:optimizations :simple
|
||||
:target :node-test
|
||||
:ns-regexp "status-im.chat.models-test$"
|
||||
:main status-im.test-runner/main
|
||||
;; set :ui-driven to true to let shadow-cljs inject node-repl
|
||||
:ui-driven true
|
||||
@@ -106,4 +107,11 @@
|
||||
:output-to "target/mocks/mocks.js"
|
||||
:output-dir "target/mocks"
|
||||
:compiler-options {:optimizations :simple
|
||||
:source-map false}}}}
|
||||
:source-map false}}
|
||||
:component-test {:target :npm-module
|
||||
:entries [quo2.core-spec]
|
||||
:ns-regexp "-component-spec$"
|
||||
:output-dir "component-spec"
|
||||
:compiler-options {:warnings-as-errors false
|
||||
:static-fns false
|
||||
:infer-externs true}}}}
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
import { useDerivedValue, withTiming, withSequence, withDelay, Easing } from 'react-native-reanimated';
|
||||
|
||||
// Shell Worklets
|
||||
|
||||
// Home Stack States
|
||||
const CLOSE_WITH_ANIMATION = 0;
|
||||
const OPEN_WITH_ANIMATION = 1;
|
||||
const CLOSE_WITHOUT_ANIMATION = 3;
|
||||
const OPEN_WITHOUT_ANIMATION = 4;
|
||||
|
||||
// Derived Values
|
||||
export function stackOpacity (stackId, selectedStackId) {
|
||||
return useDerivedValue(
|
||||
function () {
|
||||
'worklet'
|
||||
return selectedStackId.value == stackId ? 1 : 0;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function stackPointer (stackId, selectedStackId) {
|
||||
return useDerivedValue(
|
||||
function () {
|
||||
'worklet'
|
||||
return selectedStackId.value == stackId ? "auto" : "none";
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function bottomTabIconColor (stackId, selectedStackId, homeStackState,
|
||||
passThrough, selectedTabColor, defaultColor,
|
||||
passThroughColor) {
|
||||
return useDerivedValue(
|
||||
function () {
|
||||
'worklet'
|
||||
var homeStackStateValue = homeStackState.value;
|
||||
if (selectedStackId.value == stackId &&
|
||||
(homeStackStateValue == OPEN_WITH_ANIMATION ||
|
||||
homeStackStateValue == OPEN_WITHOUT_ANIMATION)){
|
||||
return selectedTabColor;
|
||||
}
|
||||
else if (passThrough.value){
|
||||
return passThroughColor;
|
||||
}
|
||||
else {
|
||||
return defaultColor;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function bottomTabsHeight(homeStackState, height, extendedHeight) {
|
||||
return useDerivedValue(
|
||||
function () {
|
||||
'worklet'
|
||||
switch (homeStackState.value) {
|
||||
case OPEN_WITH_ANIMATION:
|
||||
return withTiming(extendedHeight, defaultDurationAndEasing);
|
||||
break;
|
||||
case CLOSE_WITH_ANIMATION:
|
||||
return withTiming(height, defaultDurationAndEasing);
|
||||
break;
|
||||
case OPEN_WITHOUT_ANIMATION:
|
||||
return extendedHeight;
|
||||
break;
|
||||
case CLOSE_WITHOUT_ANIMATION:
|
||||
return height;
|
||||
break;
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// Home Stack
|
||||
|
||||
const shellAnimationTime = 200;
|
||||
|
||||
const defaultDurationAndEasing = {
|
||||
duration: shellAnimationTime,
|
||||
easing: Easing.bezier(0, 0, 1, 1),
|
||||
}
|
||||
|
||||
export function homeStackOpacity (homeStackState) {
|
||||
return useDerivedValue(
|
||||
function () {
|
||||
'worklet'
|
||||
switch (homeStackState.value) {
|
||||
case OPEN_WITH_ANIMATION:
|
||||
return withTiming(1, defaultDurationAndEasing);
|
||||
break;
|
||||
case CLOSE_WITH_ANIMATION:
|
||||
return withTiming(0, defaultDurationAndEasing);
|
||||
break;
|
||||
case OPEN_WITHOUT_ANIMATION:
|
||||
return 1;
|
||||
break;
|
||||
case CLOSE_WITHOUT_ANIMATION:
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function homeStackTop (homeStackState, top) {
|
||||
return useDerivedValue(
|
||||
function () {
|
||||
'worklet'
|
||||
switch (homeStackState.value) {
|
||||
case OPEN_WITH_ANIMATION:
|
||||
return withTiming(0, defaultDurationAndEasing);
|
||||
break;
|
||||
case CLOSE_WITH_ANIMATION:
|
||||
return withTiming(top, defaultDurationAndEasing);
|
||||
break;
|
||||
case OPEN_WITHOUT_ANIMATION:
|
||||
return 0;
|
||||
break;
|
||||
case CLOSE_WITHOUT_ANIMATION:
|
||||
return top;
|
||||
break;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function homeStackLeft (selectedStackId, animateHomeStackLeft, homeStackState, left) {
|
||||
return useDerivedValue(
|
||||
function () {
|
||||
'worklet'
|
||||
if (animateHomeStackLeft.value) {
|
||||
var leftValue = left[selectedStackId.value];
|
||||
switch (homeStackState.value) {
|
||||
case OPEN_WITH_ANIMATION:
|
||||
return withSequence(withTiming(leftValue, {duration: 0}), withTiming(0, defaultDurationAndEasing))
|
||||
break;
|
||||
case CLOSE_WITH_ANIMATION:
|
||||
return withTiming(leftValue, defaultDurationAndEasing);
|
||||
break;
|
||||
case OPEN_WITHOUT_ANIMATION:
|
||||
return 0;
|
||||
break;
|
||||
case CLOSE_WITHOUT_ANIMATION:
|
||||
return leftValue;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function homeStackPointer (homeStackState) {
|
||||
return useDerivedValue(
|
||||
function () {
|
||||
'worklet'
|
||||
var homeStackStateValue = homeStackState.value;
|
||||
return (homeStackStateValue == OPEN_WITH_ANIMATION ||
|
||||
homeStackStateValue == OPEN_WITHOUT_ANIMATION) ? "auto" : "none";
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function homeStackScale (homeStackState, minimizeScale) {
|
||||
return useDerivedValue(
|
||||
function () {
|
||||
'worklet'
|
||||
switch (homeStackState.value) {
|
||||
case OPEN_WITH_ANIMATION:
|
||||
return withTiming(1, defaultDurationAndEasing);
|
||||
break;
|
||||
case CLOSE_WITH_ANIMATION:
|
||||
return withTiming(minimizeScale, defaultDurationAndEasing);
|
||||
break;
|
||||
case OPEN_WITHOUT_ANIMATION:
|
||||
return 1;
|
||||
break;
|
||||
case CLOSE_WITHOUT_ANIMATION:
|
||||
return minimizeScale;
|
||||
break;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
import { useDerivedValue, withTiming, withSequence, withDelay, Easing } from 'react-native-reanimated';
|
||||
|
||||
// Generic Worklets
|
||||
|
||||
export function applyAnimationsToStyle(animations, style) {
|
||||
@@ -29,106 +27,3 @@ export function applyAnimationsToStyle(animations, style) {
|
||||
return Object.assign(animatedStyle, style);
|
||||
};
|
||||
};
|
||||
|
||||
// Switcher Worklets
|
||||
|
||||
export function stackOpacity (stackId, selectedStackId) {
|
||||
return useDerivedValue(
|
||||
function () {
|
||||
'worklet'
|
||||
return selectedStackId.value == stackId ? 1 : 0;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function stackPointer (stackId, selectedStackId) {
|
||||
return useDerivedValue(
|
||||
function () {
|
||||
'worklet'
|
||||
return selectedStackId.value == stackId ? "auto" : "none";
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function bottomTabIconColor (stackId, selectedStackId, homeStackOpen,
|
||||
passThrough, selectedTabColor, defaultColor,
|
||||
passThroughColor) {
|
||||
return useDerivedValue(
|
||||
function () {
|
||||
'worklet'
|
||||
if (selectedStackId.value == stackId && homeStackOpen.value){
|
||||
return selectedTabColor;
|
||||
}
|
||||
else if (passThrough.value){
|
||||
return passThroughColor;
|
||||
}
|
||||
else {
|
||||
return defaultColor;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Home Stack
|
||||
|
||||
const shellAnimationTime = 200;
|
||||
|
||||
const defaultDurationAndEasing = {
|
||||
duration: shellAnimationTime,
|
||||
easing: Easing.bezier(0, 0, 1, 1),
|
||||
}
|
||||
|
||||
export function homeStackOpacity (homeStackOpen) {
|
||||
return useDerivedValue(
|
||||
function () {
|
||||
'worklet'
|
||||
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);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -282,6 +282,8 @@
|
||||
(def worklet-factory
|
||||
#js {:applyAnimationsToStyle (fn [])})
|
||||
|
||||
(def shell-worklets #js {})
|
||||
|
||||
;; Update i18n_resources.cljs
|
||||
(defn mock [module]
|
||||
(case module
|
||||
@@ -328,6 +330,7 @@
|
||||
"@react-native-async-storage/async-storage" async-storage
|
||||
"react-native-svg" react-native-svg
|
||||
"../src/js/worklet_factory.js" worklet-factory
|
||||
"../src/js/shell_worklets.js" shell-worklets
|
||||
"./fleets.js" default-fleets
|
||||
"./chats.js" default-chats
|
||||
"@walletconnect/client" wallet-connect-client
|
||||
|
||||
@@ -21,6 +21,6 @@
|
||||
:align-items :center
|
||||
:justify-content :center
|
||||
:border-radius (/ container-size 2)
|
||||
:background-color (colors/theme-alpha color 0.5 0.6)}
|
||||
[icon/icon :i/group {:size icon-size ; TODO: group icon sizes 12 and 20 (small and large) are missing
|
||||
:background-color (colors/custom-color-by-theme color 50 60)}
|
||||
[icon/icon :i/group {:size icon-size
|
||||
:color colors/white-opa-70}]])))
|
||||
|
||||
@@ -7,147 +7,149 @@
|
||||
[quo2.components.icon :as quo2.icons]))
|
||||
|
||||
(def themes
|
||||
{:light {:primary {:icon-color colors/white
|
||||
:label {:style {:color colors/white}}
|
||||
:background-color {:default colors/primary-50
|
||||
:pressed colors/primary-60
|
||||
:disabled colors/primary-50}}
|
||||
:secondary {:icon-color colors/primary-50
|
||||
:label {:style {:color colors/primary-50}}
|
||||
:background-color {:default colors/primary-50-opa-20
|
||||
:pressed colors/primary-50-opa-40
|
||||
:disabled colors/primary-50-opa-20}}
|
||||
:grey {:icon-color colors/neutral-100
|
||||
:icon-secondary-color colors/neutral-50
|
||||
:label {:style {:color colors/neutral-100}}
|
||||
:background-color {:default colors/neutral-10
|
||||
:pressed colors/neutral-20
|
||||
:disabled colors/neutral-10}}
|
||||
:dark-grey {:icon-color colors/neutral-100
|
||||
:icon-secondary-color colors/neutral-50
|
||||
:label {:style {:color colors/neutral-100}}
|
||||
:background-color {:default colors/neutral-20
|
||||
:pressed colors/neutral-30
|
||||
:disabled colors/neutral-20}}
|
||||
:outline {:icon-color colors/neutral-50
|
||||
:icon-secondary-color colors/neutral-50
|
||||
:label {:style {:color colors/neutral-100}}
|
||||
:border-color {:default colors/neutral-20
|
||||
:pressed colors/neutral-40
|
||||
:disabled colors/neutral-20}}
|
||||
:ghost {:icon-color colors/neutral-50
|
||||
:icon-secondary-color colors/neutral-50
|
||||
:label {:style {:color colors/neutral-100}}
|
||||
:background-color {:pressed colors/neutral-10}}
|
||||
:danger {:icon-color colors/white
|
||||
:label {:style {:color colors/white}}
|
||||
: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}}
|
||||
:background-color {:default colors/white-opa-40
|
||||
:pressed colors/white-opa-50
|
||||
:disabled colors/white-opa-40}}
|
||||
:blur-bg {:icon-color colors/neutral-100
|
||||
:icon-secondary-color colors/neutral-80-opa-40
|
||||
:label {:style {:color colors/neutral-100}}
|
||||
:background-color {:default colors/neutral-80-opa-5
|
||||
:pressed colors/neutral-80-opa-10
|
||||
:disabled colors/neutral-80-opa-5}}
|
||||
:blur-bg-outline {:icon-color colors/neutral-100
|
||||
:icon-secondary-color colors/neutral-80-opa-40
|
||||
:label {:style {:color colors/neutral-100}}
|
||||
:border-color {:default colors/neutral-80-opa-10
|
||||
:pressed colors/neutral-80-opa-20
|
||||
:disabled colors/neutral-80-opa-10}}
|
||||
{:light {:primary {:icon-color colors/white
|
||||
:label-color colors/white
|
||||
:background-color {:default colors/primary-50
|
||||
:pressed colors/primary-60
|
||||
:disabled colors/primary-50}}
|
||||
:secondary {:icon-color colors/primary-50
|
||||
:label-color colors/primary-50
|
||||
:background-color {:default colors/primary-50-opa-20
|
||||
:pressed colors/primary-50-opa-40
|
||||
:disabled colors/primary-50-opa-20}}
|
||||
:grey {:icon-color colors/neutral-100
|
||||
:icon-secondary-color colors/neutral-50
|
||||
:label-color colors/neutral-100
|
||||
:background-color {:default colors/neutral-10
|
||||
:pressed colors/neutral-20
|
||||
:disabled colors/neutral-10}}
|
||||
:dark-grey {:icon-color colors/neutral-100
|
||||
:icon-secondary-color colors/neutral-50
|
||||
:label-color colors/neutral-100
|
||||
:background-color {:default colors/neutral-20
|
||||
:pressed colors/neutral-30
|
||||
:disabled colors/neutral-20}}
|
||||
:outline {:icon-color colors/neutral-50
|
||||
:icon-secondary-color colors/neutral-50
|
||||
:label-color colors/neutral-100
|
||||
:border-color {:default colors/neutral-20
|
||||
:pressed colors/neutral-40
|
||||
:disabled colors/neutral-20}}
|
||||
:ghost {:icon-color colors/neutral-50
|
||||
:icon-secondary-color colors/neutral-50
|
||||
:label-color colors/neutral-100
|
||||
:background-color {:pressed colors/neutral-10}}
|
||||
:danger {:icon-color colors/white
|
||||
:label-color colors/white
|
||||
:background-color {:default colors/danger-50
|
||||
:pressed colors/danger-60
|
||||
:disabled colors/danger-50}}
|
||||
:positive {:icon-color colors/white
|
||||
:label-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-color colors/neutral-100
|
||||
:background-color {:default colors/white-opa-40
|
||||
:pressed colors/white-opa-50
|
||||
:disabled colors/white-opa-40}}
|
||||
:blur-bg {:icon-color colors/neutral-100
|
||||
:icon-secondary-color colors/neutral-80-opa-40
|
||||
:label-color colors/neutral-100
|
||||
:background-color {:default colors/neutral-80-opa-5
|
||||
:pressed colors/neutral-80-opa-10
|
||||
:disabled colors/neutral-80-opa-5}}
|
||||
:blur-bg-outline {:icon-color colors/neutral-100
|
||||
:icon-secondary-color colors/neutral-80-opa-40
|
||||
:label-color colors/neutral-100
|
||||
:border-color {:default colors/neutral-80-opa-10
|
||||
:pressed colors/neutral-80-opa-20
|
||||
:disabled colors/neutral-80-opa-10}}
|
||||
:shell {:icon-color colors/white
|
||||
:label {:style {:color colors/white}}
|
||||
:label-color colors/white
|
||||
:background-color {:default colors/neutral-95
|
||||
:pressed colors/neutral-95
|
||||
:disabled colors/neutral-95}}}
|
||||
:dark {:primary {:icon-color colors/white
|
||||
:label {:style {:color colors/white}}
|
||||
:background-color {:default colors/primary-60
|
||||
:pressed colors/primary-50
|
||||
:disabled colors/primary-60}}
|
||||
:secondary {:icon-color colors/primary-50
|
||||
:label {:style {:color colors/primary-50}}
|
||||
:background-color {:default colors/primary-50-opa-20
|
||||
:pressed colors/primary-50-opa-30
|
||||
:disabled colors/primary-50-opa-20}}
|
||||
:grey {:icon-color colors/white
|
||||
:icon-secondary-color colors/neutral-40
|
||||
:label {:style {:color colors/white}}
|
||||
:background-color {:default colors/neutral-80
|
||||
:pressed colors/neutral-60
|
||||
:disabled colors/neutral-80}}
|
||||
:dark-grey {:icon-color colors/white
|
||||
:icon-secondary-color colors/neutral-40
|
||||
:label {:style {:color colors/white}}
|
||||
:background-color {:default colors/neutral-70
|
||||
:pressed colors/neutral-60
|
||||
:disabled colors/neutral-70}}
|
||||
:outline {:icon-color colors/neutral-40
|
||||
:icon-secondary-color colors/neutral-40
|
||||
:label {:style {:color colors/white}}
|
||||
:border-color {:default colors/neutral-70
|
||||
:pressed colors/neutral-60
|
||||
:disabled colors/neutral-70}}
|
||||
:ghost {:icon-color colors/neutral-40
|
||||
:icon-secondary-color colors/neutral-40
|
||||
:label {:style {:color colors/white}}
|
||||
:background-color {:pressed colors/neutral-80}}
|
||||
:danger {:icon-color colors/white
|
||||
:label {:style {:color colors/white}}
|
||||
: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}}
|
||||
:background-color {:default colors/neutral-80-opa-40
|
||||
:pressed colors/neutral-80-opa-50
|
||||
:disabled colors/neutral-80-opa-40}}
|
||||
:blur-bg {:icon-color colors/white
|
||||
:icon-secondary-color colors/white-opa-40
|
||||
:label {:style {:color colors/white}}
|
||||
:background-color {:default colors/white-opa-5
|
||||
:pressed colors/white-opa-10
|
||||
:disabled colors/white-opa-5}}
|
||||
:blur-bg-outline {:icon-color colors/white
|
||||
:icon-secondary-color colors/white-opa-40
|
||||
:label {:style {:color colors/white}}
|
||||
:border-color {:default colors/white-opa-10
|
||||
:pressed colors/white-opa-20
|
||||
:disabled colors/white-opa-5}}
|
||||
:shell {:icon-color colors/white
|
||||
:label {:style {:color colors/white}}
|
||||
:background-color {:default colors/neutral-95}}}})
|
||||
:dark {:primary {:icon-color colors/white
|
||||
:label-color colors/white
|
||||
:background-color {:default colors/primary-60
|
||||
:pressed colors/primary-50
|
||||
:disabled colors/primary-60}}
|
||||
:secondary {:icon-color colors/primary-50
|
||||
:label-color colors/primary-50
|
||||
:background-color {:default colors/primary-50-opa-20
|
||||
:pressed colors/primary-50-opa-30
|
||||
:disabled colors/primary-50-opa-20}}
|
||||
:grey {:icon-color colors/white
|
||||
:icon-secondary-color colors/neutral-40
|
||||
:label-color colors/white
|
||||
:background-color {:default colors/neutral-80
|
||||
:pressed colors/neutral-60
|
||||
:disabled colors/neutral-80}}
|
||||
:dark-grey {:icon-color colors/white
|
||||
:icon-secondary-color colors/neutral-40
|
||||
:label-color colors/white
|
||||
:background-color {:default colors/neutral-70
|
||||
:pressed colors/neutral-60
|
||||
:disabled colors/neutral-70}}
|
||||
:outline {:icon-color colors/neutral-40
|
||||
:icon-secondary-color colors/neutral-40
|
||||
:label-color colors/white
|
||||
:border-color {:default colors/neutral-70
|
||||
:pressed colors/neutral-60
|
||||
:disabled colors/neutral-70}}
|
||||
:ghost {:icon-color colors/neutral-40
|
||||
:icon-secondary-color colors/neutral-40
|
||||
:label-color colors/white
|
||||
:background-color {:pressed colors/neutral-80}}
|
||||
:danger {:icon-color colors/white
|
||||
:label-color colors/white
|
||||
:background-color {:default colors/danger-60
|
||||
:pressed colors/danger-50
|
||||
:disabled colors/danger-60}}
|
||||
:positive {:icon-color colors/white
|
||||
:label-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-color colors/white
|
||||
:background-color {:default colors/neutral-80-opa-40
|
||||
:pressed colors/neutral-80-opa-50
|
||||
:disabled colors/neutral-80-opa-40}}
|
||||
:blur-bg {:icon-color colors/white
|
||||
:icon-secondary-color colors/white-opa-40
|
||||
:label-color colors/white
|
||||
:background-color {:default colors/white-opa-5
|
||||
:pressed colors/white-opa-10
|
||||
:disabled colors/white-opa-5}}
|
||||
:blur-bg-outline {:icon-color colors/white
|
||||
:icon-secondary-color colors/white-opa-40
|
||||
:label-color colors/white
|
||||
:border-color {:default colors/white-opa-10
|
||||
:pressed colors/white-opa-20
|
||||
:disabled colors/white-opa-5}}
|
||||
:shell {:icon-color colors/white
|
||||
:label-color colors/white
|
||||
:background-color {:default colors/neutral-95}}}})
|
||||
|
||||
(defn shape-style-container [type icon size]
|
||||
{:border-radius (if (and icon (#{:primary :secondary :danger} type))
|
||||
24
|
||||
(case size
|
||||
56 12
|
||||
40 12
|
||||
32 10
|
||||
24 8))})
|
||||
|
||||
(defn style-container [type size disabled background-color border-color icon above width before after]
|
||||
(merge {:height size
|
||||
:align-items :center
|
||||
:justify-content :center
|
||||
:flex-direction (if above :column :row)
|
||||
:border-radius (if (and icon (#{:primary :secondary :danger} type))
|
||||
24
|
||||
(case size
|
||||
56 12
|
||||
40 12
|
||||
32 10
|
||||
24 8))
|
||||
:background-color background-color
|
||||
:padding-horizontal (when-not (or icon before after)
|
||||
(case size 56 16 40 16 32 12 24 8))
|
||||
@@ -159,6 +161,7 @@
|
||||
(case size 56 0 40 9 32 5 24 3))
|
||||
:padding-bottom (when-not (or icon before after)
|
||||
(case size 56 0 40 9 32 5 24 4))}
|
||||
(shape-style-container type icon size)
|
||||
(when width
|
||||
{:width width})
|
||||
(when icon
|
||||
@@ -169,14 +172,19 @@
|
||||
(when disabled
|
||||
{:opacity 0.3})))
|
||||
|
||||
(defn community-themed? [type community-color]
|
||||
(and (= type :community) (string? community-color)))
|
||||
|
||||
(defn button
|
||||
"with label
|
||||
[button opts \"label\"]
|
||||
opts
|
||||
{:type :primary/:secondary/:grey/:dark-grey/:outline/:ghost/
|
||||
:danger/:photo-bg/:blur-bg/:blur-bg-ouline/:shell
|
||||
:danger/:photo-bg/:blur-bg/:blur-bg-ouline/:shell/:community
|
||||
:size 40/32/24
|
||||
:icon true/false
|
||||
:community-color '#FFFFFF'
|
||||
:community-text-color '#000000'
|
||||
:before :icon-keyword
|
||||
:after :icon-keyword}
|
||||
|
||||
@@ -184,13 +192,13 @@
|
||||
[button {:icon true} :main-icons/close-circle]"
|
||||
[_ _]
|
||||
(let [pressed (reagent/atom false)]
|
||||
(fn [{:keys [on-press disabled type size before after above width
|
||||
(fn [{:keys [on-press disabled type size community-color community-text-color before after above width
|
||||
override-theme override-background-color
|
||||
on-long-press accessibility-label icon icon-no-color style test-ID]
|
||||
:or {type :primary
|
||||
size 40}}
|
||||
children]
|
||||
(let [{:keys [icon-color icon-secondary-color background-color label border-color]}
|
||||
(let [{:keys [icon-color icon-secondary-color background-color label-color border-color]}
|
||||
(get-in themes [(or
|
||||
override-theme
|
||||
(theme/get-theme)) type])
|
||||
@@ -212,48 +220,65 @@
|
||||
(reset! pressed nil))})
|
||||
|
||||
[rn/view {:style (merge
|
||||
(style-container
|
||||
type
|
||||
size
|
||||
disabled
|
||||
(or override-background-color (get background-color state))
|
||||
(get border-color state)
|
||||
icon
|
||||
above
|
||||
width
|
||||
before
|
||||
after)
|
||||
(shape-style-container type icon size)
|
||||
{:background-color
|
||||
(if (= state :pressed)
|
||||
(colors/theme-colors colors/neutral-100 colors/white)
|
||||
:transparent)}
|
||||
style)}
|
||||
(when above
|
||||
[rn/view
|
||||
[quo2.icons/icon above {:container-style {:margin-bottom 2}
|
||||
:color icon-secondary-color
|
||||
:size icon-size}]])
|
||||
(when before
|
||||
[rn/view
|
||||
[quo2.icons/icon before {:container-style {:margin-left (if (= size 40) 12 8)
|
||||
:margin-right 4}
|
||||
[rn/view {:style (merge
|
||||
(style-container
|
||||
type
|
||||
size
|
||||
disabled
|
||||
(or override-background-color (get background-color state))
|
||||
(get border-color state)
|
||||
icon
|
||||
above
|
||||
width
|
||||
before
|
||||
after)
|
||||
(when
|
||||
(community-themed? type community-color)
|
||||
(merge
|
||||
{:background-color community-color}
|
||||
(when (= state :pressed) {:opacity 0.9}))))}
|
||||
(when above
|
||||
[rn/view
|
||||
[quo2.icons/icon above {:container-style {:margin-bottom 2}
|
||||
:color icon-secondary-color
|
||||
:size icon-size}]])
|
||||
[rn/view
|
||||
(cond
|
||||
(or icon icon-no-color)
|
||||
[quo2.icons/icon children {:color icon-color
|
||||
:no-color icon-no-color
|
||||
:size icon-size}]
|
||||
(when before
|
||||
[rn/view
|
||||
[quo2.icons/icon before {:container-style {:margin-left (if (= size 40) 12 8)
|
||||
:margin-right 4}
|
||||
:color icon-secondary-color
|
||||
:size icon-size}]])
|
||||
[rn/view
|
||||
(cond
|
||||
(or icon icon-no-color)
|
||||
[quo2.icons/icon children {:color icon-color
|
||||
:no-color icon-no-color
|
||||
:size icon-size}]
|
||||
|
||||
(string? children)
|
||||
[text/text (merge {:size (when (#{56 24} size) :paragraph-2)
|
||||
:weight :medium
|
||||
:number-of-lines 1}
|
||||
label)
|
||||
children]
|
||||
(string? children)
|
||||
[text/text {:size (when (#{56 24} size) :paragraph-2)
|
||||
:weight :medium
|
||||
:number-of-lines 1
|
||||
:style {:color (if
|
||||
(and
|
||||
(community-themed? type community-color)
|
||||
(string? community-text-color))
|
||||
community-text-color
|
||||
label-color)}}
|
||||
|
||||
(vector? children)
|
||||
children)]
|
||||
(when after
|
||||
[rn/view
|
||||
[quo2.icons/icon after {:container-style {:margin-left 4
|
||||
:margin-right (if (= size 40) 12 8)}
|
||||
:color icon-secondary-color
|
||||
:size icon-size}]])]]))))
|
||||
children]
|
||||
|
||||
(vector? children)
|
||||
children)]
|
||||
(when after
|
||||
[rn/view
|
||||
[quo2.icons/icon after {:container-style {:margin-left 4
|
||||
:margin-right (if (= size 40) 12 8)}
|
||||
:color icon-secondary-color
|
||||
:size icon-size}]])]]]))))
|
||||
|
||||
@@ -6,15 +6,11 @@
|
||||
|
||||
(defn community-card-view-item
|
||||
[{:keys [name description locked
|
||||
status tokens cover tags featured]} on-press]
|
||||
status tokens cover tags width]} 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
|
||||
[rn/view {:style (style/community-card 20)}
|
||||
[rn/view {:style {:width width
|
||||
:height 230
|
||||
:border-radius 20}
|
||||
:on-press on-press}
|
||||
[rn/view
|
||||
|
||||
@@ -69,10 +69,12 @@
|
||||
:style {:margin-top (if (= size :large) 8 2)}}
|
||||
description])])
|
||||
|
||||
(defn permission-tag-container [{:keys [locked? tokens]}]
|
||||
(defn permission-tag-container [{:keys [locked? tokens on-press]}]
|
||||
[permission/tag {:background-color (colors/theme-colors
|
||||
colors/neutral-10
|
||||
colors/neutral-80)
|
||||
:locked? locked?
|
||||
:tokens tokens
|
||||
:size 24}])
|
||||
|
||||
:size 24
|
||||
:on-press on-press}])
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
(ns quo2.components.community.token-gating
|
||||
(:require [quo.react-native :as rn]
|
||||
[quo2.components.avatars.channel-avatar :as channel-avatar]
|
||||
[quo2.components.buttons.button :as button]
|
||||
[quo2.components.icon :as icon]
|
||||
[quo2.components.info.information-box :as information-box]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.components.tags.token-tag :as token-tag]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.ui.components.fast-image :as fast-image]))
|
||||
|
||||
(def ^:private token-tag-horizontal-spacing 7)
|
||||
(def token-tag-vertical-spacing 5)
|
||||
|
||||
(def styles {:container {:flex 1
|
||||
:padding-horizontal 20}
|
||||
:header-container {:flex-direction :row
|
||||
:align-items :center}
|
||||
:header-spacing-community {:margin-bottom 16}
|
||||
:header-spacing-channel {:margin-bottom 24}
|
||||
:header-avatar {:margin-right 8}
|
||||
:header-community-avatar-image {:width 32 :height 32}
|
||||
:header-title-container {:flex 1
|
||||
:flex-direction :row
|
||||
:align-items :center}
|
||||
:header-title-lock {:margin-left 6}
|
||||
:header-info-button {:margin-left 8}
|
||||
:token-requirement-text-spacing {:margin-vertical 8}
|
||||
:token-requirement-list-spacing {:padding-vertical (- 8 token-tag-vertical-spacing)}
|
||||
:token-row {:flex-direction :row
|
||||
:flex-wrap :wrap
|
||||
:align-items :center}
|
||||
:token-row-container {:flex-direction :row
|
||||
:align-items :center
|
||||
:margin-horizontal (- token-tag-horizontal-spacing)}
|
||||
:token-row-container-no-shift {:margin-left 0}
|
||||
:token-row-or-text {:margin-right 3}
|
||||
:token-tag-spacing {:margin-horizontal token-tag-horizontal-spacing
|
||||
:margin-vertical token-tag-vertical-spacing}
|
||||
:divider {:height 1
|
||||
:margin-horizontal -20
|
||||
:margin-top (- 12 token-tag-vertical-spacing)
|
||||
:margin-bottom 8}
|
||||
:membership-request-denied {:margin-top 13}
|
||||
:enter-button {:margin-top 16}
|
||||
:info-text {:margin-top 12
|
||||
:text-align :center
|
||||
:padding-horizontal 20}})
|
||||
|
||||
(defn multiple-token-requirements? [token-requirement-lists]
|
||||
(when (vector? (first token-requirement-lists))
|
||||
(> (count token-requirement-lists) 1)))
|
||||
|
||||
(defn is-token-requirement-met? [token-requirement-list]
|
||||
(and (seq token-requirement-list)
|
||||
(every?
|
||||
(fn [token]
|
||||
(get token :is-sufficient?))
|
||||
token-requirement-list)))
|
||||
|
||||
(defn are-multiple-token-requirements-met? [token-requirement-lists]
|
||||
(if (multiple-token-requirements? token-requirement-lists)
|
||||
(some is-token-requirement-met? token-requirement-lists)
|
||||
(is-token-requirement-met? token-requirement-lists)))
|
||||
|
||||
(defn token-requirement-list-row [tokens community-color]
|
||||
[rn/view {:style (merge
|
||||
(get styles :token-row)
|
||||
(get styles :token-requirement-list-spacing))}
|
||||
(map-indexed (fn [token-index token]
|
||||
(let [{:keys [token-img-src token amount is-sufficient? is-purchasable?]} token]
|
||||
^{:key token-index}
|
||||
[rn/view {:style (get styles :token-tag-spacing)}
|
||||
[token-tag/token-tag {:token token
|
||||
:value amount
|
||||
:size 24
|
||||
:border-color community-color
|
||||
:is-required is-sufficient?
|
||||
:is-purchasable is-purchasable?
|
||||
:token-img-src token-img-src}]]))
|
||||
tokens)])
|
||||
|
||||
(defn token-requirement-list [props community-color]
|
||||
(let [{:keys [gate token-requirements-changed? required-tokens-lost?]} props
|
||||
[gate-type token-requirement-lists] gate
|
||||
multiple-token-requirements? (multiple-token-requirements? token-requirement-lists)
|
||||
is-sufficient? (are-multiple-token-requirements-met? token-requirement-lists)
|
||||
you-must-hold-label (if (= gate-type :join)
|
||||
(cond
|
||||
token-requirements-changed? :t/you-must-now-hold
|
||||
required-tokens-lost? :t/you-must-always-hold
|
||||
:else :t/you-must-hold)
|
||||
:t/you-must-hold)
|
||||
message-label (cond
|
||||
(= gate-type :join)
|
||||
(cond
|
||||
token-requirements-changed? :t/community-join-requirements-changed
|
||||
required-tokens-lost? :t/community-join-requirements-tokens-lost
|
||||
:else (if is-sufficient? :t/community-join-requirements-met :t/community-join-requirements-not-met))
|
||||
(= gate-type :read)
|
||||
(if is-sufficient?
|
||||
:t/community-channel-read-requirements-met
|
||||
:t/community-channel-read-requirements-not-met)
|
||||
(= gate-type :write)
|
||||
(if is-sufficient?
|
||||
:t/community-channel-write-requirements-met
|
||||
:t/community-channel-write-requirements-not-met))]
|
||||
[rn/view
|
||||
[rn/view {:style (get styles :token-requirement-text-spacing)}
|
||||
[text/text {:weight :medium
|
||||
:number-of-lines 1}
|
||||
(i18n/label
|
||||
message-label)]
|
||||
[text/text {:size :paragraph-2
|
||||
:number-of-lines 1}
|
||||
(i18n/label
|
||||
you-must-hold-label)]]
|
||||
|
||||
(if multiple-token-requirements?
|
||||
(map-indexed
|
||||
(fn [token-requirement-index tokens]
|
||||
^{:key token-requirement-index}
|
||||
[rn/view {:style (merge
|
||||
(get styles :token-row-container)
|
||||
(when-not (= token-requirement-index 0)
|
||||
(get styles :token-row-container-no-shift)))}
|
||||
(when-not (= token-requirement-index 0)
|
||||
[text/text {:style (get styles :token-row-or-text)
|
||||
:size :paragraph-2} "or"])
|
||||
[token-requirement-list-row tokens community-color]])
|
||||
token-requirement-lists)
|
||||
[rn/view {:style (get styles :token-row-container)}
|
||||
[token-requirement-list-row token-requirement-lists community-color]])]))
|
||||
|
||||
(defn is-community-locked? [community]
|
||||
(not (are-multiple-token-requirements-met? (get-in community [:gates :join]))))
|
||||
|
||||
(defn is-channel-locked? [channel]
|
||||
(not (are-multiple-token-requirements-met?
|
||||
(or (get-in channel [:gates :read])
|
||||
(get-in channel [:gates :write])))))
|
||||
|
||||
(defn token-gating
|
||||
"[token-gating opts]
|
||||
opts
|
||||
{
|
||||
:community {
|
||||
:name string
|
||||
:community-avatar-img-src string
|
||||
:community-color string
|
||||
:community-text-color string
|
||||
:token-requirements-changed? boolean
|
||||
:required-tokens-lost? boolean
|
||||
}
|
||||
:channel {
|
||||
:name string
|
||||
:emoji string
|
||||
:emoji-background-color :string
|
||||
:on-enter-channel callback
|
||||
:membership-request-denied? boolean
|
||||
}
|
||||
}"
|
||||
[_ _]
|
||||
(fn [{:keys [community channel]}]
|
||||
(let [type (if (some? community) :community :channel)
|
||||
{:keys [name
|
||||
emoji
|
||||
emoji-background-color
|
||||
on-enter-channel
|
||||
community-avatar-img-src
|
||||
community-color
|
||||
community-text-color
|
||||
membership-request-denied?
|
||||
token-requirements-changed?
|
||||
required-tokens-lost?
|
||||
gates]} (if (= type :community) community channel)
|
||||
locked? (if
|
||||
(= type :community)
|
||||
(is-community-locked? community)
|
||||
(is-channel-locked? channel))]
|
||||
|
||||
[rn/view {:style (merge (get styles :container) {:background-color (colors/theme-colors colors/white colors/neutral-90)})}
|
||||
[rn/view {:style (merge
|
||||
(get styles :header-container)
|
||||
(if
|
||||
(= type :community)
|
||||
(get styles :header-spacing-community)
|
||||
(get styles :header-spacing-channel)))}
|
||||
|
||||
[rn/view {:style (get styles :header-avatar)}
|
||||
(if (= type :community)
|
||||
[fast-image/fast-image {:source community-avatar-img-src
|
||||
:style (get styles :header-community-avatar-image)}]
|
||||
|
||||
[channel-avatar/channel-avatar {:big? true
|
||||
:locked? locked?
|
||||
:emoji emoji
|
||||
:emoji-background-color emoji-background-color}])]
|
||||
|
||||
[rn/view {:style (get styles :header-title-container)}
|
||||
[text/text {:weight :semi-bold
|
||||
:number-of-lines 1
|
||||
:size :heading-1
|
||||
:style (get styles :header-text)} (if (= type :community) name (str "# " name))]
|
||||
|
||||
(when (= type :community)
|
||||
[icon/icon (if locked?
|
||||
:main-icons2/locked
|
||||
:main-icons2/unlocked)
|
||||
{:container-style (get styles :header-title-lock)
|
||||
:color (colors/theme-colors
|
||||
colors/neutral-50
|
||||
colors/neutral-40)}])]
|
||||
|
||||
[button/button {:type :outline
|
||||
:size 32
|
||||
:icon true
|
||||
:style (get styles :header-info-button)} :main-icons2/info]]
|
||||
|
||||
(map-indexed (fn [gate-index gate]
|
||||
^{:key gate-index}
|
||||
[:<>
|
||||
(when-not (= gate-index 0)
|
||||
[rn/view {:style (merge
|
||||
(get styles :divider)
|
||||
{:background-color (colors/theme-colors
|
||||
colors/neutral-10
|
||||
colors/neutral-80)})}])
|
||||
[token-requirement-list {:gate gate :token-requirements-changed? token-requirements-changed? :required-tokens-lost? required-tokens-lost?} community-color]])
|
||||
gates)
|
||||
|
||||
(when (= type :channel)
|
||||
(if membership-request-denied?
|
||||
[information-box/information-box {:type :error
|
||||
:icon :main-icons2/untrustworthy
|
||||
:no-icon-color? true
|
||||
:style (get styles :membership-request-denied)}
|
||||
(i18n/label
|
||||
:t/membership-request-denied)]
|
||||
|
||||
[:<>
|
||||
[button/button {:type :community
|
||||
:community-color community-color
|
||||
:community-text-color community-text-color
|
||||
:style (get styles :enter-button)
|
||||
:disabled locked?
|
||||
:on-press on-enter-channel}
|
||||
(str "# " (i18n/label
|
||||
:t/enter-channel))]
|
||||
[text/text
|
||||
{:size :paragraph-2
|
||||
:style (merge
|
||||
(get styles :info-text)
|
||||
{:color (colors/theme-colors
|
||||
colors/neutral-50
|
||||
colors/neutral-40)})}
|
||||
(i18n/label
|
||||
:t/community-enter-channel-info)]]))])))
|
||||
@@ -20,7 +20,7 @@
|
||||
(defn counter
|
||||
"type: default, secondary, grey, outline
|
||||
value: integer"
|
||||
[{:keys [type override-text-color override-bg-color style]} value]
|
||||
[{:keys [type override-text-color override-bg-color style accessibility-label]} value]
|
||||
(let [type (or type :default)
|
||||
text-color (or override-text-color
|
||||
(if (or
|
||||
@@ -38,7 +38,9 @@
|
||||
1 16
|
||||
2 20
|
||||
28)]
|
||||
[rn/view {:style (cond-> (merge
|
||||
[rn/view {:accessible true
|
||||
:accessibility-label accessibility-label
|
||||
:style (cond-> (merge
|
||||
{:align-items :center
|
||||
:justify-content :center
|
||||
:border-radius 6
|
||||
|
||||
@@ -6,12 +6,13 @@
|
||||
|
||||
(defn- get-icon-color [danger?]
|
||||
(if danger?
|
||||
(colors/theme-colors colors/danger-50 colors/danger-60)
|
||||
colors/danger-50
|
||||
(colors/theme-colors colors/neutral-50 colors/neutral-40)))
|
||||
|
||||
(defn divider []
|
||||
[rn/view {:style {:border-top-width 1
|
||||
:border-top-color (colors/theme-colors colors/neutral-10 colors/neutral-80)
|
||||
:border-top-color (colors/theme-colors
|
||||
colors/neutral-10 colors/neutral-90)
|
||||
:margin-top 8
|
||||
:margin-bottom 7
|
||||
:align-items :center
|
||||
@@ -27,10 +28,15 @@
|
||||
(when action-props
|
||||
[:<> {:key label}
|
||||
(when add-divider? [divider])
|
||||
[rn/touchable-opacity {:on-press on-press}
|
||||
[rn/touchable-highlight {:style {:border-radius 12
|
||||
:height (if sub-label 58 50)
|
||||
:margin-horizontal 8}
|
||||
:underlay-color (colors/theme-colors colors/neutral-5 colors/neutral-90)
|
||||
:on-press on-press}
|
||||
[rn/view {:style
|
||||
{:height (if sub-label 56 47)
|
||||
:margin-horizontal 20
|
||||
{:height (if sub-label 58 50)
|
||||
|
||||
:margin-horizontal 12
|
||||
:flex-direction :row}}
|
||||
[rn/view {:style
|
||||
{:height 20
|
||||
|
||||
@@ -4,9 +4,14 @@
|
||||
|
||||
(def icon-path "./resources/images/icons2/")
|
||||
|
||||
(defn combine-path [path el]
|
||||
(if (System/getenv "COMPONENT_TEST")
|
||||
(str "." path el "@2x.png")
|
||||
(str "." path el ".png")))
|
||||
|
||||
(defn require-icon [size path]
|
||||
(fn [el]
|
||||
(let [s (str "." path el ".png")
|
||||
(let [s (combine-path path el)
|
||||
k (-> el
|
||||
(cstr/replace "_" "-")
|
||||
(cstr/replace " " "-")
|
||||
|
||||
@@ -19,19 +19,21 @@
|
||||
(defn info-message
|
||||
"[info-message opts \"message\"]
|
||||
opts
|
||||
{:type :default/:success/:error
|
||||
:size :default/:tiny
|
||||
:icon :main-icons/info ;; info message icon
|
||||
:text-color colors/white ;; text color override
|
||||
:icon-color colors/white ;; icon color override"
|
||||
[{:keys [type size icon text-color icon-color]} message]
|
||||
{:type :default/:success/:error
|
||||
:size :default/:tiny
|
||||
:icon :main-icons/info ;; info message icon
|
||||
:text-color colors/white ;; text color override
|
||||
:icon-color colors/white ;; icon color override
|
||||
:no-icon-color? false ;; disable tint color for icon"
|
||||
[{:keys [type size icon text-color icon-color no-icon-color?]} message]
|
||||
(let [weight (if (= size :default) :regular :medium)
|
||||
size (if (= size :default) :paragraph-2 :label)
|
||||
text-color (or text-color (get-color type))
|
||||
icon-color (or icon-color text-color)]
|
||||
[rn/view {:style {:flex-direction :row
|
||||
:flex 1}}
|
||||
[quo2.icons/icon icon {:color icon-color
|
||||
[quo2.icons/icon icon {:color icon-color
|
||||
:no-color no-icon-color?
|
||||
:size 12
|
||||
:container-style {:margin-top 3}}]
|
||||
[text/text {:size size
|
||||
|
||||
@@ -49,11 +49,12 @@
|
||||
:closed? true/false ;; information box's state
|
||||
:id :information-box-id ;; unique id (required for closable? information box)
|
||||
:icon :main-icons/info ;; information box icon
|
||||
:no-icon-color? false ;; disable tint color for icon
|
||||
:style style
|
||||
:button-label \"PressMe\" ;; add action button with label
|
||||
:on-button-press action ;; (required for information box with button-label)
|
||||
:on-close on-close ;; (optional on-close call)"
|
||||
[{:keys [type closable? closed? id icon style button-label on-button-press on-close]} message]
|
||||
[{:keys [type closable? closed? id icon style button-label on-button-press on-close no-icon-color?]} message]
|
||||
(let [background-color (get-color-by-type type :bg)
|
||||
border-color (get-color-by-type type :border)
|
||||
icon-color (get-color-by-type type :icon)
|
||||
@@ -73,7 +74,8 @@
|
||||
[info-message/info-message {:size :default
|
||||
:icon icon
|
||||
:text-color text-color
|
||||
:icon-color icon-color} message]
|
||||
:icon-color icon-color
|
||||
:no-icon-color? no-icon-color?} message]
|
||||
(when closable?
|
||||
[rn/touchable-opacity
|
||||
{:on-press on-close
|
||||
|
||||
@@ -1,56 +1,57 @@
|
||||
(ns quo2.components.list-items.channel
|
||||
(:require [react-native.core :as rn]
|
||||
[quo2.foundations.colors :as colors]
|
||||
(:require [quo2.components.avatars.channel-avatar :as channel-avatar]
|
||||
[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]
|
||||
[quo2.theme :as theme]))
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.theme :as theme]
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(defn list-item [{:keys [name locked? mentions-count unread-messages?
|
||||
muted? is-active-channel? emoji channel-color]
|
||||
muted? is-active-channel? emoji channel-color on-press]
|
||||
:or {channel-color colors/primary-50}}]
|
||||
[rn/view {:style (merge {:height 48
|
||||
:display :flex
|
||||
:border-radius 12
|
||||
:flex-direction :row
|
||||
:justify-content :space-between
|
||||
:align-items :center
|
||||
:width "100%"
|
||||
:padding-left 12
|
||||
: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
|
||||
:justify-content :flex-start
|
||||
:align-items :center}
|
||||
[channel-avatar/channel-avatar
|
||||
{:big? true
|
||||
: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)}))
|
||||
:weight :medium :size :paragraph-1} (str "# " name)]]
|
||||
[rn/view {:style {:height 20}}
|
||||
(when (and (not locked?)
|
||||
muted?)
|
||||
[quo2.icons/icon :i/muted
|
||||
{:size 20
|
||||
:no-color true}])
|
||||
(when (and (not locked?)
|
||||
(not muted?)
|
||||
(pos? (int mentions-count)))
|
||||
[rn/view {:style {:margin-right 2
|
||||
:margin-top 2}}
|
||||
[quo2.counter/counter {:override-bg-color channel-color} mentions-count]])
|
||||
(when (and (not locked?)
|
||||
(not muted?)
|
||||
(not (pos? (int mentions-count)))
|
||||
unread-messages?)
|
||||
[quo2.icons/icon :i/channel-notification
|
||||
{:size 20
|
||||
:no-color true}])]])
|
||||
[rn/touchable-opacity {:on-press on-press}
|
||||
[rn/view {:style (merge {:height 48
|
||||
:display :flex
|
||||
:border-radius 12
|
||||
:flex-direction :row
|
||||
:justify-content :space-between
|
||||
:align-items :center
|
||||
:width "100%"
|
||||
:padding-left 12
|
||||
: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
|
||||
:justify-content :flex-start
|
||||
:align-items :center}
|
||||
[channel-avatar/channel-avatar
|
||||
{:big? true
|
||||
: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)}))
|
||||
:weight :medium :size :paragraph-1} (str "# " name)]]
|
||||
[rn/view {:style {:height 20}}
|
||||
(when (and (not locked?)
|
||||
muted?)
|
||||
[quo2.icons/icon :i/muted
|
||||
{:size 20
|
||||
:no-color true}])
|
||||
(when (and (not locked?)
|
||||
(not muted?)
|
||||
(pos? (int mentions-count)))
|
||||
[rn/view {:style {:margin-right 2
|
||||
:margin-top 2}}
|
||||
[quo2.counter/counter {:override-bg-color channel-color} mentions-count]])
|
||||
(when (and (not locked?)
|
||||
(not muted?)
|
||||
(not (pos? (int mentions-count)))
|
||||
unread-messages?)
|
||||
[quo2.icons/icon :i/channel-notification
|
||||
{:size 20
|
||||
:no-color true}])]]])
|
||||
|
||||
@@ -9,6 +9,28 @@
|
||||
|
||||
(def middle-dot "·")
|
||||
|
||||
(defn display-name [{:keys [profile-name nickname ens-name text-style]}]
|
||||
(let [ens? (-> ens-name string/blank? not)
|
||||
nickname? (-> nickname string/blank? not)]
|
||||
(if ens?
|
||||
[text/text (merge {:weight :semi-bold
|
||||
:size :paragraph-2
|
||||
:style (style/ens-text)}
|
||||
text-style)
|
||||
ens-name]
|
||||
[:<>
|
||||
(if nickname?
|
||||
[text/text (merge {:weight :semi-bold
|
||||
:size :paragraph-2
|
||||
:style (style/nickname-text)}
|
||||
text-style)
|
||||
nickname]
|
||||
[text/text (merge {:weight :semi-bold
|
||||
:size :paragraph-2
|
||||
:style (style/profile-name-text nickname?)}
|
||||
text-style)
|
||||
profile-name])])))
|
||||
|
||||
(defn author [{:keys [profile-name nickname chat-key ens-name time-str contact? verified? untrustworthy?]}]
|
||||
[:f>
|
||||
(fn []
|
||||
@@ -63,4 +85,4 @@
|
||||
[text/text {:monospace true
|
||||
:size :paragraph-2
|
||||
:style (style/time-text ens?)}
|
||||
time-str]]))])
|
||||
time-str]]))])
|
||||
|
||||
@@ -14,31 +14,35 @@
|
||||
:customization-color customization-color}]))
|
||||
|
||||
(defn floating-shell-button
|
||||
"[floating-shell-button dynamic-buttons style]
|
||||
dynamic-buttons
|
||||
{:button-type {:on-press on-press :count count}}"
|
||||
[dynamic-buttons style opacity-anim pointer-anim]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [original-style (merge {:flex-direction :row
|
||||
:margin-horizontal 12} style)
|
||||
animated-style (reanimated/apply-animations-to-style
|
||||
{:opacity opacity-anim
|
||||
:pointer-events pointer-anim}
|
||||
original-style)]
|
||||
[reanimated/view {:style animated-style}
|
||||
;; Left Section
|
||||
[rn/view {:style {:flex 1}}
|
||||
[dynamic-button-view :search dynamic-buttons {:position :absolute
|
||||
:right 8}]]
|
||||
;; Mid Section (jump-to)
|
||||
[dynamic-button-view :jump-to dynamic-buttons nil]
|
||||
"[floating-shell-button dynamic-buttons style opacity-anim pointer-anim]
|
||||
dynamic-buttons {:button-type {:on-press on-press :count count}}
|
||||
style override style
|
||||
opacity-anim reanimated value (optional)"
|
||||
([dynamic-button style]
|
||||
(floating-shell-button dynamic-button style nil))
|
||||
([dynamic-buttons style opacity-anim]
|
||||
[:f>
|
||||
(fn []
|
||||
(let [original-style (merge {:flex-direction :row
|
||||
:margin-horizontal 12
|
||||
:pointer-events :box-none} style)
|
||||
animated-style (reanimated/apply-animations-to-style
|
||||
(if opacity-anim
|
||||
{:opacity opacity-anim} {})
|
||||
original-style)]
|
||||
[reanimated/view {:style animated-style}
|
||||
;; Left Section
|
||||
[rn/view {:style {:flex 1}}
|
||||
[dynamic-button-view :search dynamic-buttons {:position :absolute
|
||||
:right 8}]]
|
||||
;; Mid Section (jump-to)
|
||||
[dynamic-button-view :jump-to dynamic-buttons nil]
|
||||
;; Right Section
|
||||
[rn/view {:style {:flex 1}}
|
||||
[rn/view {:style {:position :absolute
|
||||
:flex-direction :row
|
||||
:right 0}}
|
||||
[dynamic-button-view :mention dynamic-buttons {:margin-left 8}]
|
||||
[dynamic-button-view :notification-down dynamic-buttons {:margin-left 8}]
|
||||
[dynamic-button-view :notification-up dynamic-buttons {:margin-left 8}]
|
||||
[dynamic-button-view :bottom dynamic-buttons {:margin-left 8}]]]]))])
|
||||
[rn/view {:style {:flex 1}}
|
||||
[rn/view {:style {:position :absolute
|
||||
:flex-direction :row
|
||||
:right 0}}
|
||||
[dynamic-button-view :mention dynamic-buttons {:margin-left 8}]
|
||||
[dynamic-button-view :notification-down dynamic-buttons {:margin-left 8}]
|
||||
[dynamic-button-view :notification-up dynamic-buttons {:margin-left 8}]
|
||||
[dynamic-button-view :bottom dynamic-buttons {:margin-left 8}]]]]))]))
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.components.icon :as icons]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[clojure.string :as string]))
|
||||
[clojure.string :as string]
|
||||
[quo2.components.buttons.button :as button]
|
||||
[quo2.components.avatars.user-avatar :as user-avatar]))
|
||||
|
||||
(def ^:private centrify-style
|
||||
{:display :flex
|
||||
@@ -12,12 +14,10 @@
|
||||
|
||||
(def ^:private align-left (assoc centrify-style :align-items :flex-start))
|
||||
|
||||
(def ^:private icon-styles (assoc centrify-style :width 32 :height 32 :border-radius 10))
|
||||
|
||||
(defn- big? [size] (= size :big))
|
||||
|
||||
(defn- icon-props [color size]
|
||||
(merge {:size 20
|
||||
(merge {:size 20
|
||||
:container-style {:width (if (big? size)
|
||||
20
|
||||
16)
|
||||
@@ -28,34 +28,33 @@
|
||||
{:color color}
|
||||
{:no-color true})))
|
||||
|
||||
(defn render-left-section [{:keys [on-press icon
|
||||
icon-color icon-background-color]}
|
||||
put-middle-section-on-left?]
|
||||
(defn left-section-view [{:keys [on-press icon accessibility-label type] :or {type :grey}}
|
||||
put-middle-section-on-left?]
|
||||
[rn/view {:style (merge
|
||||
icon-styles
|
||||
{:background-color icon-background-color
|
||||
:width 32
|
||||
:height 32}
|
||||
{:accessibility-label accessibility-label}
|
||||
(when put-middle-section-on-left? {:margin-right 5}))}
|
||||
[rn/touchable-opacity {:on-press on-press}
|
||||
[icons/icon icon (icon-props icon-color :big)]]])
|
||||
[button/button {:on-press on-press :icon true :type type :size 32}
|
||||
icon]])
|
||||
|
||||
(defn- mid-section-comp
|
||||
[{:keys [description-user-icon horizontal-description? text-secondary-color align-mid? text-color icon main-text type description]}]
|
||||
[rn/view {:style (assoc
|
||||
centrify-style
|
||||
:flex-direction :row
|
||||
:margin-horizontal 2)}
|
||||
[{:keys [description-img description-user-icon horizontal-description?
|
||||
text-secondary-color align-mid? text-color icon main-text type description]}]
|
||||
[rn/view {:style (assoc centrify-style
|
||||
:flex-direction :row
|
||||
:margin-horizontal 2)}
|
||||
(when (or (and (not horizontal-description?)
|
||||
align-mid?
|
||||
(not= :text-with-description type))
|
||||
(and description-user-icon
|
||||
(and (or description-img description-user-icon)
|
||||
(not icon)))
|
||||
[rn/image {:source {:uri description-user-icon}
|
||||
:style {:width 32
|
||||
:height 32
|
||||
:border-radius 32
|
||||
:margin-right 8}}])
|
||||
(if description-img
|
||||
[rn/view {:margin-right 8}
|
||||
[description-img]]
|
||||
[rn/image {:source {:uri description-user-icon}
|
||||
:style {:width 32
|
||||
:height 32
|
||||
:border-radius 32
|
||||
:margin-right 8}}]))
|
||||
[rn/view {:style {:flex-direction (if horizontal-description?
|
||||
:row
|
||||
:column)}}
|
||||
@@ -73,76 +72,69 @@
|
||||
horizontal-description? (assoc :margin-left 4 :margin-top 2))}
|
||||
description])]])
|
||||
|
||||
(defn- render-mid-section
|
||||
(defn- mid-section-view
|
||||
[{:keys [horizontal-description? one-icon-align-left? type left-align?
|
||||
main-text right-icon main-text-icon-color left-icon] :as props}]
|
||||
(let [text-color (if (colors/dark?) colors/neutral-5 colors/neutral-95)
|
||||
text-secondary-color (if (colors/dark?) colors/neutral-40 colors/neutral-50)
|
||||
main-text right-icon main-text-icon-color left-icon on-press avatar] :as props}]
|
||||
(let [text-color (if (colors/dark?) colors/neutral-5 colors/neutral-95)
|
||||
text-secondary-color (if (colors/dark?) colors/neutral-40 colors/neutral-50)
|
||||
component-instance [mid-section-comp (assoc props :text-secondary-color text-secondary-color)]]
|
||||
[rn/view {:style (merge
|
||||
(if left-align?
|
||||
align-left
|
||||
centrify-style)
|
||||
{:flex 1
|
||||
:margin-left 4
|
||||
:text-align-vertical :center})}
|
||||
(case type
|
||||
:text-only [text/text {:size :paragraph-1
|
||||
:weight :semi-bold
|
||||
:style {:color text-color}}
|
||||
main-text]
|
||||
:text-with-two-icons [rn/view {:style (assoc centrify-style :flex-direction :row)}
|
||||
[icons/icon left-icon
|
||||
(icon-props main-text-icon-color :big)]
|
||||
[text/text {:size :paragraph-1
|
||||
:weight :semi-bold
|
||||
:style {:padding-horizontal 4
|
||||
:color text-color}}
|
||||
main-text]
|
||||
[rn/touchable-opacity {:on-press on-press}
|
||||
[rn/view {:style (merge
|
||||
(if left-align?
|
||||
align-left
|
||||
centrify-style)
|
||||
{:flex 1
|
||||
:margin-left 4
|
||||
:text-align-vertical :center})}
|
||||
(case type
|
||||
:text-only [text/text {:size :paragraph-1
|
||||
:weight :semi-bold
|
||||
:style {:color text-color}}
|
||||
main-text]
|
||||
:user-avatar [rn/view {:style (assoc centrify-style :flex-direction :row)}
|
||||
[user-avatar/user-avatar avatar]
|
||||
[text/text {:size :paragraph-1
|
||||
:weight :semi-bold
|
||||
:style {:padding-horizontal 4
|
||||
:color text-color}}
|
||||
main-text]]
|
||||
:text-with-two-icons [rn/view {:style (assoc centrify-style :flex-direction :row)}
|
||||
[icons/icon left-icon
|
||||
(icon-props main-text-icon-color :big)]
|
||||
[text/text {:size :paragraph-1
|
||||
:weight :semi-bold
|
||||
:style {:padding-horizontal 4
|
||||
:color text-color}}
|
||||
main-text]
|
||||
[icons/icon right-icon
|
||||
(icon-props main-text-icon-color :big)]]
|
||||
:text-with-one-icon [rn/view {:style {:flex-direction :row}}
|
||||
(if one-icon-align-left?
|
||||
[rn/view {:style {:flex-direction :row
|
||||
:align-items :center}}
|
||||
(when horizontal-description?
|
||||
[icons/icon left-icon
|
||||
(icon-props main-text-icon-color :big)])
|
||||
component-instance]
|
||||
[rn/view {:style {:flex-direction :row
|
||||
:align-items :center}}
|
||||
component-instance
|
||||
(when horizontal-description?
|
||||
[icons/icon left-icon
|
||||
(icon-props main-text-icon-color :big)])])]
|
||||
:text-with-description component-instance)]]))
|
||||
|
||||
[icons/icon right-icon
|
||||
(icon-props main-text-icon-color :big)]]
|
||||
:text-with-one-icon [rn/view {:style {:flex-direction :row}}
|
||||
(if one-icon-align-left?
|
||||
[rn/view {:style {:flex-direction :row
|
||||
:align-items :center}}
|
||||
(when horizontal-description?
|
||||
[icons/icon left-icon
|
||||
(icon-props main-text-icon-color :big)])
|
||||
component-instance]
|
||||
[rn/view {:style {:flex-direction :row
|
||||
:align-items :center}}
|
||||
component-instance
|
||||
(when horizontal-description?
|
||||
[icons/icon left-icon
|
||||
(icon-props main-text-icon-color :big)])])]
|
||||
:text-with-description component-instance)]))
|
||||
|
||||
(defn- right-section-icon
|
||||
[{:keys [background-color icon icon-color push-to-the-left?] :or {push-to-the-left? false}}]
|
||||
[rn/view {:style (assoc
|
||||
icon-styles
|
||||
:background-color background-color
|
||||
:width 32
|
||||
:height 32
|
||||
:margin-right (if push-to-the-left? 8 0))}
|
||||
[icons/icon icon (icon-props icon-color :big)]])
|
||||
|
||||
(defn- render-right-section [right-section-buttons]
|
||||
[rn/view {:style (assoc
|
||||
centrify-style
|
||||
:flex-direction :row
|
||||
:flex 1
|
||||
:justify-content :flex-end)}
|
||||
(defn- right-section-view [right-section-buttons]
|
||||
[rn/view {:style (assoc centrify-style
|
||||
:flex-direction :row
|
||||
:flex 1
|
||||
:justify-content :flex-end)}
|
||||
(let [last-icon-index (-> right-section-buttons count dec)]
|
||||
(map-indexed (fn [index {:keys [icon background-color icon-color on-press]}]
|
||||
(map-indexed (fn [index {:keys [icon on-press type] :or {type :grey}}]
|
||||
^{:key index}
|
||||
[rn/touchable-opacity {:on-press on-press}
|
||||
[right-section-icon
|
||||
{:icon icon
|
||||
:background-color background-color
|
||||
:icon-color icon-color
|
||||
:push-to-the-left? (not= index last-icon-index)}]])
|
||||
[rn/view {:style {:margin-right (if (not= index last-icon-index) 8 0)}}
|
||||
[button/button {:on-press on-press :icon true :type type :size 32}
|
||||
icon]])
|
||||
right-section-buttons))])
|
||||
|
||||
(defn page-nav
|
||||
@@ -154,7 +146,7 @@
|
||||
:page-nav-color color
|
||||
:page-nav-background-uri image-uri
|
||||
:mid-section
|
||||
{:type one-of :text-only :text-with-two-icons :text-with-one-icon :text-with-description
|
||||
{:type one-of :text-only :text-with-two-icons :text-with-one-icon :text-with-description :user-avatar
|
||||
:icon icon
|
||||
:main-text string
|
||||
:left-icon icon
|
||||
@@ -163,16 +155,19 @@
|
||||
:description-color color
|
||||
:description-icon icon
|
||||
:description-user-icon icon
|
||||
:description-img a render prop which will be used in place of :description-user-icon
|
||||
:main-text-icon-color color
|
||||
}
|
||||
:left-section
|
||||
{:on-press event
|
||||
{:type button-type
|
||||
:on-press event
|
||||
:icon icon
|
||||
:icon-color color
|
||||
:icon-background-color color
|
||||
}
|
||||
:right-section-buttons vector of
|
||||
{:on-press event
|
||||
{:type button-type
|
||||
:on-press event
|
||||
:icon icon
|
||||
:icon-color color
|
||||
:icon-background-color color
|
||||
@@ -189,32 +184,35 @@
|
||||
mid-section-props
|
||||
{:type (:type mid-section)
|
||||
:horizontal-description? horizontal-description?
|
||||
:description-img (:description-img mid-section)
|
||||
:main-text (:main-text mid-section)
|
||||
:main-text-icon-color (:main-text-icon-color mid-section)
|
||||
:one-icon-align-left? one-icon-align-left?
|
||||
:right-icon (:right-icon mid-section)
|
||||
:icon (:icon mid-section)
|
||||
:left-icon (:left-icon mid-section)}]
|
||||
:left-icon (:left-icon mid-section)
|
||||
:avatar (:avatar mid-section)}]
|
||||
[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
|
||||
:height 56
|
||||
: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
|
||||
:flex-direction :row
|
||||
:align-items :center}}
|
||||
(render-left-section left-section put-middle-section-on-left?)
|
||||
[left-section-view left-section put-middle-section-on-left?]
|
||||
(when put-middle-section-on-left?
|
||||
[render-mid-section (assoc mid-section-props
|
||||
:left-align? true
|
||||
:description (:description mid-section)
|
||||
:description-color (:description-color mid-section)
|
||||
:description-icon (:description-icon mid-section)
|
||||
:align-mid? align-mid?
|
||||
:description-user-icon (:description-user-icon mid-section))])]
|
||||
[mid-section-view (assoc mid-section-props
|
||||
:left-align? true
|
||||
:description (:description mid-section)
|
||||
:description-color (:description-color mid-section)
|
||||
:description-icon (:description-icon mid-section)
|
||||
:align-mid? align-mid?
|
||||
:description-user-icon (:description-user-icon mid-section))])]
|
||||
(when-not put-middle-section-on-left?
|
||||
[render-mid-section mid-section-props])
|
||||
(render-right-section right-section-buttons)]))
|
||||
[mid-section-view mid-section-props])
|
||||
[right-section-view right-section-buttons]]))
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
(ns quo2.components.navigation.top-nav
|
||||
(: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]
|
||||
[react-native.hole-view :as hole-view]))
|
||||
|
||||
(defn- get-button-common-props [type]
|
||||
(let [default? (= type :default)
|
||||
dark? (colors/dark?)]
|
||||
{:icon true
|
||||
:size 32
|
||||
:style {:margin-left 12}
|
||||
:type (if default?
|
||||
(if dark? :grey :dark-grey)
|
||||
type)
|
||||
:override-background-color (when (and dark? default?)
|
||||
colors/neutral-90)}))
|
||||
|
||||
(defn- base-button [icon on-press accessibility-label button-common-props]
|
||||
[quo2.button/button
|
||||
(merge
|
||||
{:on-press on-press
|
||||
:accessibility-label accessibility-label}
|
||||
button-common-props)
|
||||
icon])
|
||||
|
||||
(defn top-nav
|
||||
"[top-nav opts]
|
||||
opts
|
||||
{:type :default/:blurred/:shell
|
||||
:new-notifications? true/false
|
||||
:notification-indicator :unread-dot/:counter
|
||||
:open-profile fn
|
||||
:open-search fn
|
||||
:open-scanner fn
|
||||
:show-qr fn
|
||||
:open-activity-center fn
|
||||
:style override-style
|
||||
:avatar user-avatar
|
||||
:counter-label number}
|
||||
"
|
||||
[{:keys [type new-notifications? notification-indicator open-profile open-search
|
||||
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}
|
||||
style)}
|
||||
;; Left Section
|
||||
[rn/touchable-without-feedback {:on-press open-profile}
|
||||
[rn/view {:style {:position :absolute
|
||||
:left 20
|
||||
:top 12}}
|
||||
[user-avatar/user-avatar
|
||||
(merge
|
||||
{:ring? true
|
||||
:status-indicator? true
|
||||
:size :small}
|
||||
avatar)]]]
|
||||
;; Right Section
|
||||
[rn/view {:style {:position :absolute
|
||||
:right 20
|
||||
:top 12
|
||||
:flex-direction :row}}
|
||||
;; TODO component shouldn't know anything about parent system, we should pass buttons as parameters
|
||||
[base-button :i/search open-search :open-search-button button-common-props]
|
||||
[base-button :i/scan open-scanner :open-scanner-button button-common-props]
|
||||
[base-button :i/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
|
||||
[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}]
|
||||
|
||||
:else
|
||||
[{:x 33 :y -7 :width 18 :height 18 :borderRadius 7}])}
|
||||
[base-button :i/activity-center open-activity-center
|
||||
:open-activity-center-button button-common-props]]
|
||||
(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 34
|
||||
:top -6}}
|
||||
counter-label]
|
||||
[rn/view {:style {:width 8
|
||||
:height 8
|
||||
:border-radius 4
|
||||
:top -2
|
||||
:left 38
|
||||
:position :absolute
|
||||
:background-color colors/primary-50}}]))]]]))
|
||||
@@ -10,6 +10,8 @@
|
||||
:position :absolute
|
||||
:right 22
|
||||
:border-radius 6
|
||||
:justify-content :center
|
||||
:align-items :center
|
||||
: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,14 @@
|
||||
(ns quo2.components.selectors.--tests--.selectors-component-spec
|
||||
(:require ["@testing-library/react-native" :as rtl]
|
||||
[quo2.components.selectors.selectors :as selectors]
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
(defn render-toggle
|
||||
([]
|
||||
(rtl/render (reagent/as-element [selectors/toggle {:disabled? (:disabled? false)}]))))
|
||||
|
||||
(js/global.test "default render of toggle component"
|
||||
(fn []
|
||||
(render-toggle)
|
||||
(-> (js/expect (rtl/screen.getByTestId "toggle-component"))
|
||||
(.toBeTruthy))))
|
||||
@@ -133,7 +133,8 @@
|
||||
:border-radius 20
|
||||
:background-color (get-color @checked? disabled? blurred-background?)})
|
||||
:accessibility-label (str "toggle-" (if @checked? "on" "off"))
|
||||
:accessibility-role :checkbox}
|
||||
:accessibility-role :checkbox
|
||||
:testID "toggle-component"}
|
||||
[rn/view {:style
|
||||
{:margin-left (if @checked? 12 2)
|
||||
:height 16
|
||||
|
||||
@@ -12,26 +12,29 @@
|
||||
|
||||
(def default-tab-size 32)
|
||||
|
||||
(defn indicator []
|
||||
[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]])
|
||||
|
||||
(defn tabs [{:keys [default-active on-change style]}]
|
||||
(let [active-tab-id (reagent/atom default-active)]
|
||||
(fn [{:keys [data size] :or {size default-tab-size}}]
|
||||
[rn/view (merge {:flex-direction :row} style)
|
||||
(doall
|
||||
(for [{:keys [label id new-info accessibility-label]} data]
|
||||
(for [{:keys [label id notification-dot? 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]])
|
||||
(when notification-dot?
|
||||
[indicator])
|
||||
[tab/tab
|
||||
{:id id
|
||||
:size size
|
||||
|
||||
@@ -104,11 +104,12 @@
|
||||
|
||||
(defn tag
|
||||
[_ _]
|
||||
(fn [{:keys [locked tokens size background-color]
|
||||
(fn [{:keys [locked tokens size background-color on-press]
|
||||
:or {size 24}}]
|
||||
[base-tag/base-tag {:background-color background-color
|
||||
:size size
|
||||
:type :permission}
|
||||
:type :permission
|
||||
:on-press on-press}
|
||||
[rn/view {:flex-direction :row
|
||||
:align-items :center
|
||||
:justify-content :flex-end}
|
||||
|
||||
@@ -35,11 +35,11 @@
|
||||
quo2.components.tags.context-tags
|
||||
quo2.components.tabs.tabs
|
||||
quo2.components.tabs.account-selector
|
||||
quo2.components.navigation.top-nav
|
||||
quo2.components.navigation.floating-shell-button
|
||||
quo2.components.tags.status-tags
|
||||
quo2.components.navigation.page-nav
|
||||
quo2.components.selectors.disclaimer))
|
||||
quo2.components.selectors.disclaimer
|
||||
quo2.components.selectors.selectors))
|
||||
|
||||
(def button quo2.components.buttons.button/button)
|
||||
(def dynamic-button quo2.components.buttons.dynamic-button/dynamic-button)
|
||||
@@ -58,15 +58,15 @@
|
||||
(def tags quo2.components.tags.tags/tags)
|
||||
(def user-avatar-tag quo2.components.tags.context-tags/user-avatar-tag)
|
||||
(def context-tag quo2.components.tags.context-tags/context-tag)
|
||||
(def group-avatar-tag quo2.components.tags.context-tags/group-avatar-tag)
|
||||
(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)
|
||||
(def top-nav quo2.components.navigation.top-nav/top-nav)
|
||||
(def floating-shell-button quo2.components.navigation.floating-shell-button/floating-shell-button)
|
||||
(def status-tag quo2.components.tags.status-tags/status-tag)
|
||||
(def page-nav quo2.components.navigation.page-nav/page-nav)
|
||||
(def disclaimer quo2.components.selectors.disclaimer/disclaimer)
|
||||
|
||||
(def checkbox quo2.components.selectors.selectors/checkbox)
|
||||
;;;; AVATAR
|
||||
(def account-avatar quo2.components.avatars.account-avatar/account-avatar)
|
||||
(def channel-avatar quo2.components.avatars.channel-avatar/channel-avatar)
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
(ns quo2.core-spec
|
||||
(:require [quo2.components.selectors.--tests--.selectors-component-spec]))
|
||||
@@ -1,10 +1,13 @@
|
||||
(ns react-native.core
|
||||
(:require [reagent.core :as reagent]
|
||||
["react-native" :as react-native]
|
||||
["@react-native-community/blur" :as blur]
|
||||
[react-native.flat-list :as flat-list]
|
||||
[react-native.section-list :as section-list]))
|
||||
[react-native.section-list :as section-list]
|
||||
[react-native.platform :as platform]))
|
||||
|
||||
(def app-state ^js (.-AppState ^js react-native))
|
||||
(def blur-view (reagent/adapt-react-class (.-BlurView blur)))
|
||||
|
||||
(def view (reagent/adapt-react-class (.-View ^js react-native)))
|
||||
(def scroll-view (reagent/adapt-react-class (.-ScrollView ^js react-native)))
|
||||
@@ -53,3 +56,17 @@
|
||||
(js->clj (.get (.-Dimensions ^js react-native) "window") :keywordize-keys true))
|
||||
|
||||
(def status-bar (.-StatusBar ^js react-native))
|
||||
|
||||
(defn hw-back-add-listener [callback]
|
||||
(.addEventListener (.-BackHandler ^js react-native) "hardwareBackPress" callback))
|
||||
|
||||
(defn hw-back-remove-listener [callback]
|
||||
(.removeEventListener (.-BackHandler ^js react-native) "hardwareBackPress" callback))
|
||||
|
||||
(def keyboard-avoiding-view-class (reagent/adapt-react-class (.-KeyboardAvoidingView react-native)))
|
||||
|
||||
(defn keyboard-avoiding-view [props & children]
|
||||
(into [keyboard-avoiding-view-class
|
||||
(merge (when platform/ios? {:behavior :padding})
|
||||
props)]
|
||||
children))
|
||||
|
||||
@@ -6,6 +6,14 @@
|
||||
[status-im.utils.fx :as fx]
|
||||
[taoensso.timbre :as log]))
|
||||
|
||||
(def defaults
|
||||
{:filter-status :unread
|
||||
:filter-type types/no-type
|
||||
;; Choose the maximum number of notifications that *usually/safely* fit on
|
||||
;; most screens, so that the UI doesn't have to needlessly render
|
||||
;; notifications.
|
||||
:notifications-per-page 8})
|
||||
|
||||
;;;; Navigation
|
||||
|
||||
(fx/defn open-activity-center
|
||||
@@ -75,6 +83,35 @@
|
||||
(map data-store.activities/<-rpc)
|
||||
(notifications-reconcile cofx)))
|
||||
|
||||
;;;; Mark notifications as read
|
||||
|
||||
(defn- get-notification
|
||||
[db notification-id]
|
||||
(->> (get-in db [:activity-center
|
||||
:notifications
|
||||
(get-in db [:activity-center :filter :type])
|
||||
(get-in db [:activity-center :filter :status])
|
||||
:data])
|
||||
(filter #(= notification-id (:id %)))
|
||||
first))
|
||||
|
||||
(fx/defn mark-as-read
|
||||
{:events [:activity-center.notifications/mark-as-read]}
|
||||
[{:keys [db]} notification-id]
|
||||
(when-let [notification (get-notification db notification-id)]
|
||||
{::json-rpc/call [{:method "wakuext_markActivityCenterNotificationsRead"
|
||||
:params [[notification-id]]
|
||||
:on-success #(rf/dispatch [:activity-center.notifications/mark-as-read-success notification])
|
||||
:on-error #(rf/dispatch [:activity-center/process-notification-failure
|
||||
notification-id
|
||||
:notification/mark-as-read
|
||||
%])}]}))
|
||||
|
||||
(fx/defn mark-as-read-success
|
||||
{:events [:activity-center.notifications/mark-as-read-success]}
|
||||
[cofx notification]
|
||||
(notifications-reconcile cofx [(assoc notification :read true)]))
|
||||
|
||||
;;;; Contact verification
|
||||
|
||||
(fx/defn contact-verification-decline
|
||||
@@ -123,14 +160,6 @@
|
||||
|
||||
;;;; Notifications fetching and pagination
|
||||
|
||||
(def defaults
|
||||
{:filter-status :unread
|
||||
:filter-type types/no-type
|
||||
;; Choose the maximum number of notifications that *usually/safely* fit on
|
||||
;; most screens, so that the UI doesn't have to needlessly render
|
||||
;; notifications.
|
||||
:notifications-per-page 8})
|
||||
|
||||
(def start-or-end-cursor
|
||||
"")
|
||||
|
||||
|
||||
@@ -9,13 +9,105 @@
|
||||
[status-im.test-helpers :as h]
|
||||
[status-im2.setup.config :as config]))
|
||||
|
||||
(def notification-id "0x1")
|
||||
|
||||
(defn setup []
|
||||
(h/register-helper-events)
|
||||
(rf/dispatch [:setup/app-started]))
|
||||
|
||||
;;;; Contact verification
|
||||
(defn test-log-on-failure
|
||||
[{:keys [before-test notification-id event action]}]
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(h/using-log-test-appender
|
||||
(fn [logs]
|
||||
(when before-test
|
||||
(before-test))
|
||||
(h/stub-fx-with-callbacks ::json-rpc/call :on-error (constantly :fake-error))
|
||||
|
||||
(def notification-id 24)
|
||||
(rf/dispatch event)
|
||||
|
||||
(is (= {:args [(str "Failed to " action)
|
||||
{:notification-id notification-id
|
||||
:error :fake-error}]
|
||||
:level :warn}
|
||||
(last @logs)))))))
|
||||
|
||||
;;;; Misc
|
||||
|
||||
(deftest mark-as-read-test
|
||||
(testing "does nothing if the notification ID cannot be found in the app db"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [spy-queue (atom [])]
|
||||
(h/spy-fx spy-queue ::json-rpc/call)
|
||||
(let [notifications {types/one-to-one-chat
|
||||
{:all {:cursor "" :data [{:id notification-id
|
||||
:read false
|
||||
:type types/one-to-one-chat}]}
|
||||
:unread {:cursor "" :data []}}}]
|
||||
(rf/dispatch [:test/assoc-in [:activity-center]
|
||||
{:notifications notifications
|
||||
:filter {:type types/one-to-one-chat
|
||||
:status :all}}])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/mark-as-read "0x666"])
|
||||
|
||||
(is (= [] @spy-queue))
|
||||
(is (= notifications (get-in (h/db) [:activity-center :notifications])))))))
|
||||
|
||||
(testing "marks notifications as read and updates app db"
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(let [notif-1 {:id "0x1" :read true :type types/one-to-one-chat}
|
||||
notif-2 {:id "0x2" :read false :type types/one-to-one-chat}
|
||||
notif-3 {:id "0x3" :read false :type types/one-to-one-chat}
|
||||
new-notif-3 (assoc notif-3 :read true)
|
||||
new-notif-2 (assoc notif-2 :read true)]
|
||||
(h/stub-fx-with-callbacks ::json-rpc/call :on-success (constantly nil))
|
||||
(rf/dispatch [:test/assoc-in [:activity-center]
|
||||
{:notifications {types/one-to-one-chat
|
||||
{:all {:cursor "" :data [notif-3 notif-2 notif-1]}
|
||||
:unread {:cursor "" :data [notif-3 notif-2]}}}
|
||||
:filter {:type types/one-to-one-chat
|
||||
:status :unread}}])
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/mark-as-read (:id notif-2)])
|
||||
(is (= {types/one-to-one-chat
|
||||
{:all {:cursor "" :data [notif-3 new-notif-2 notif-1]}
|
||||
:unread {:cursor "" :data [notif-3]}}
|
||||
|
||||
types/no-type
|
||||
{:all {:data [new-notif-2]}
|
||||
:unread {:data []}}}
|
||||
(get-in (h/db) [:activity-center :notifications])))
|
||||
|
||||
(rf/dispatch [:activity-center.notifications/mark-as-read (:id notif-3)])
|
||||
(is (= {types/one-to-one-chat
|
||||
{:all {:cursor "" :data [new-notif-3 new-notif-2 notif-1]}
|
||||
:unread {:cursor "" :data []}}
|
||||
|
||||
types/no-type
|
||||
{:all {:data [new-notif-3 new-notif-2]}
|
||||
:unread {:data []}}}
|
||||
(get-in (h/db) [:activity-center :notifications]))))))
|
||||
|
||||
(testing "logs on failure"
|
||||
(test-log-on-failure
|
||||
{:notification-id notification-id
|
||||
:event [:activity-center.notifications/mark-as-read notification-id]
|
||||
:action :notification/mark-as-read
|
||||
:before-test (fn []
|
||||
(rf/dispatch [:test/assoc-in [:activity-center]
|
||||
{:notifications {types/one-to-one-chat
|
||||
{:all {:cursor "" :data [{:id notification-id
|
||||
:read false
|
||||
:type types/one-to-one-chat}]}
|
||||
:unread {:cursor "" :data []}}}
|
||||
:filter {:type types/one-to-one-chat
|
||||
:status :all}}]))})))
|
||||
|
||||
;;;; Contact verification
|
||||
|
||||
(def contact-verification-rpc-response
|
||||
{:activityCenterNotifications
|
||||
@@ -59,22 +151,6 @@
|
||||
:timestamp 1666647286000
|
||||
:type types/contact-verification})
|
||||
|
||||
(defn test-log-on-failure
|
||||
[{:keys [notification-id event action]}]
|
||||
(rf-test/run-test-sync
|
||||
(setup)
|
||||
(h/using-log-test-appender
|
||||
(fn [logs]
|
||||
(h/stub-fx-with-callbacks ::json-rpc/call :on-error (constantly :fake-error))
|
||||
|
||||
(rf/dispatch event)
|
||||
|
||||
(is (= {:args [(str "Failed to " action)
|
||||
{:notification-id notification-id
|
||||
:error :fake-error}]
|
||||
:level :warn}
|
||||
(last @logs)))))))
|
||||
|
||||
(defn test-contact-verification-event
|
||||
[{:keys [event expected-rpc-call]}]
|
||||
(rf-test/run-test-sync
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
[status-im.bottom-sheet.core :as bottom-sheet]
|
||||
[status-im.browser.webview-ref :as webview-ref]
|
||||
["eth-phishing-detect" :as eth-phishing-detect]
|
||||
[status-im.utils.debounce :as debounce]
|
||||
[utils.debounce :as debounce]
|
||||
[status-im.browser.eip3085 :as eip3085]
|
||||
[status-im.browser.eip3326 :as eip3326]))
|
||||
|
||||
|
||||
@@ -265,12 +265,18 @@
|
||||
(fx/defn navigate-to-chat-nav2
|
||||
"Takes coeffects map and chat-id, returns effects necessary for navigation and preloading data"
|
||||
{:events [:chat.ui/navigate-to-chat-nav2]}
|
||||
[{db :db :as cofx} chat-id from-switcher?]
|
||||
[{db :db :as cofx} chat-id from-shell?]
|
||||
(fx/merge cofx
|
||||
{:db (assoc db :current-chat-id chat-id)}
|
||||
(offload-messages chat-id)
|
||||
{:dispatch [:navigate-to-nav2 :chat chat-id from-shell?]}
|
||||
(when-not (= (:view-id db) :community)
|
||||
(navigation/pop-to-root-tab :shell-stack))
|
||||
(close-chat)
|
||||
(force-close-chat chat-id)
|
||||
(fn [{:keys [db]}]
|
||||
{:db (assoc db :current-chat-id chat-id)})
|
||||
(preload-chat-data chat-id)
|
||||
(navigation/navigate-to-nav2 :chat chat-id nil from-switcher?)))
|
||||
#(when (group-chat? cofx chat-id)
|
||||
(loading/load-chat % chat-id))))
|
||||
|
||||
(fx/defn handle-clear-history-response
|
||||
{:events [::history-cleared]}
|
||||
|
||||
@@ -20,19 +20,19 @@
|
||||
|
||||
(defn- resize-and-call [uri cb]
|
||||
(react/image-get-size
|
||||
uri
|
||||
(fn [width height]
|
||||
(let [resize? (> (max width height) maximum-image-size-px)]
|
||||
(image-processing/resize
|
||||
uri
|
||||
(if resize? maximum-image-size-px width)
|
||||
(if resize? maximum-image-size-px height)
|
||||
60
|
||||
(fn [^js resized-image]
|
||||
(let [path (.-path resized-image)
|
||||
path (if (string/starts-with? path "file") path (str "file://" path))]
|
||||
(cb path)))
|
||||
#(log/error "could not resize image" %))))))
|
||||
uri
|
||||
(fn [width height]
|
||||
(let [resize? (> (max width height) maximum-image-size-px)]
|
||||
(image-processing/resize
|
||||
uri
|
||||
(if resize? maximum-image-size-px width)
|
||||
(if resize? maximum-image-size-px height)
|
||||
60
|
||||
(fn [^js resized-image]
|
||||
(let [path (.-path resized-image)
|
||||
path (if (string/starts-with? path "file") path (str "file://" path))]
|
||||
(cb path)))
|
||||
#(log/error "could not resize image" %))))))
|
||||
|
||||
(defn result->id [^js result]
|
||||
(if platform/ios?
|
||||
@@ -43,7 +43,7 @@
|
||||
|
||||
(defn download-image-http [base64-uri on-success]
|
||||
(-> (.config ReactNativeBlobUtil (clj->js {:trusty platform/ios?
|
||||
:path temp-image-url}))
|
||||
:path temp-image-url}))
|
||||
(.fetch "GET" base64-uri)
|
||||
(.then #(on-success (.path %)))
|
||||
(.catch #(log/error "could not save image"))))
|
||||
@@ -51,62 +51,64 @@
|
||||
(defn save-to-gallery [path] (.save CameraRoll path))
|
||||
|
||||
(re-frame/reg-fx
|
||||
::save-image-to-gallery
|
||||
(fn [base64-uri]
|
||||
(if platform/ios?
|
||||
(-> (download-image-http base64-uri save-to-gallery)
|
||||
(.catch #(utils/show-popup (i18n/label :t/error)
|
||||
(i18n/label :t/external-storage-denied))))
|
||||
(permissions/request-permissions
|
||||
{:permissions [:write-external-storage]
|
||||
:on-allowed #(download-image-http base64-uri save-to-gallery)
|
||||
:on-denied (fn []
|
||||
(utils/set-timeout
|
||||
#(utils/show-popup (i18n/label :t/error)
|
||||
(i18n/label :t/external-storage-denied))
|
||||
50))}))))
|
||||
::save-image-to-gallery
|
||||
(fn [base64-uri]
|
||||
(if platform/ios?
|
||||
(-> (download-image-http base64-uri save-to-gallery)
|
||||
(.catch #(utils/show-popup (i18n/label :t/error)
|
||||
(i18n/label :t/external-storage-denied))))
|
||||
(permissions/request-permissions
|
||||
{:permissions [:write-external-storage]
|
||||
:on-allowed #(download-image-http base64-uri save-to-gallery)
|
||||
:on-denied (fn []
|
||||
(utils/set-timeout
|
||||
#(utils/show-popup (i18n/label :t/error)
|
||||
(i18n/label :t/external-storage-denied))
|
||||
50))}))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
::chat-open-image-picker-camera
|
||||
(fn [current-chat-id]
|
||||
(react/show-image-picker-camera
|
||||
#(re-frame/dispatch [:chat.ui/image-captured current-chat-id (.-path %)]) {})))
|
||||
::chat-open-image-picker-camera
|
||||
(fn [current-chat-id]
|
||||
(react/show-image-picker-camera
|
||||
#(re-frame/dispatch [:chat.ui/image-captured current-chat-id (.-path %)]) {})))
|
||||
|
||||
(re-frame/reg-fx
|
||||
::chat-open-image-picker
|
||||
(fn [chat-id]
|
||||
(react/show-image-picker
|
||||
(fn [^js images]
|
||||
;; NOTE(Ferossgp): Because we can't highlight the already selected images inside
|
||||
;; gallery, we just clean previous state and set all newly picked images
|
||||
(when (and platform/ios? (pos? (count images)))
|
||||
(re-frame/dispatch [:chat.ui/clear-sending-images chat-id]))
|
||||
(doseq [^js result (if platform/ios?
|
||||
(take config/max-images-batch images)
|
||||
[images])]
|
||||
(resize-and-call (.-path result)
|
||||
#(re-frame/dispatch [:chat.ui/image-selected chat-id (result->id result) %]))))
|
||||
;; NOTE(Ferossgp): On android you cannot set max limit on images, when a user
|
||||
;; selects too many images the app crashes.
|
||||
{:media-type "photo"
|
||||
:multiple platform/ios?})))
|
||||
::chat-open-image-picker
|
||||
(fn [chat-id]
|
||||
(react/show-image-picker
|
||||
(fn [^js images]
|
||||
;; NOTE(Ferossgp): Because we can't highlight the already selected images inside
|
||||
;; gallery, we just clean previous state and set all newly picked images
|
||||
(when (and platform/ios? (pos? (count images)))
|
||||
(re-frame/dispatch [:chat.ui/clear-sending-images chat-id]))
|
||||
(doseq [^js result (if platform/ios?
|
||||
(take config/max-images-batch images)
|
||||
[images])]
|
||||
(resize-and-call (.-path result)
|
||||
#(re-frame/dispatch [:chat.ui/image-selected chat-id (result->id result) %]))))
|
||||
;; NOTE(Ferossgp): On android you cannot set max limit on images, when a user
|
||||
;; selects too many images the app crashes.
|
||||
{:media-type "photo"
|
||||
:multiple platform/ios?})))
|
||||
|
||||
(re-frame/reg-fx
|
||||
::image-selected
|
||||
(fn [[uri chat-id]]
|
||||
(resize-and-call
|
||||
uri
|
||||
#(re-frame/dispatch [:chat.ui/image-selected chat-id uri %]))))
|
||||
::image-selected
|
||||
(fn [[uri chat-id]]
|
||||
(resize-and-call
|
||||
uri
|
||||
#(re-frame/dispatch [:chat.ui/image-selected chat-id uri %]))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
::camera-roll-get-photos
|
||||
(fn [num]
|
||||
(permissions/request-permissions
|
||||
{:permissions [:read-external-storage]
|
||||
:on-allowed (fn []
|
||||
(-> (.getPhotos CameraRoll #js {:first num :assetType "Photos" :groupTypes "All"})
|
||||
(.then #(re-frame/dispatch [:on-camera-roll-get-photos (:edges (types/js->clj %))]))
|
||||
(.catch #(log/warn "could not get cameraroll photos"))))})))
|
||||
::camera-roll-get-photos
|
||||
(fn [[num end-cursor]]
|
||||
(permissions/request-permissions
|
||||
{:permissions [:read-external-storage]
|
||||
:on-allowed (fn []
|
||||
(-> (if end-cursor
|
||||
(.getPhotos CameraRoll #js {:first num :after end-cursor :assetType "Photos" :groupTypes "All"})
|
||||
(.getPhotos CameraRoll #js {:first num :assetType "Photos" :groupTypes "All"}))
|
||||
(.then #(re-frame/dispatch [:on-camera-roll-get-photos (:edges (types/js->clj %)) (:page_info (types/js->clj %)) end-cursor]))
|
||||
(.catch #(log/warn "could not get camera roll photos"))))})))
|
||||
|
||||
(fx/defn image-captured
|
||||
{:events [:chat.ui/image-captured]}
|
||||
@@ -119,13 +121,23 @@
|
||||
|
||||
(fx/defn camera-roll-get-photos
|
||||
{:events [:chat.ui/camera-roll-get-photos]}
|
||||
[_ num]
|
||||
{::camera-roll-get-photos num})
|
||||
[_ num end-cursor]
|
||||
{::camera-roll-get-photos [num end-cursor]})
|
||||
|
||||
(fx/defn camera-roll-loading-more
|
||||
{:events [:chat.ui/camera-roll-loading-more]}
|
||||
[{:keys [db]} is-loading]
|
||||
{:db (assoc db :camera-roll-loading-more is-loading)})
|
||||
|
||||
(fx/defn on-camera-roll-get-photos
|
||||
{:events [:on-camera-roll-get-photos]}
|
||||
[{db :db} photos]
|
||||
{:db (assoc db :camera-roll-photos (mapv #(get-in % [:node :image :uri]) photos))})
|
||||
[{:keys [db] :as cofx} photos page-info end-cursor]
|
||||
(let [photos_x (when end-cursor (:camera-roll-photos db))]
|
||||
{:db (-> db
|
||||
(assoc :camera-roll-photos (concat photos_x (map #(get-in % [:node :image :uri]) photos)))
|
||||
(assoc :camera-roll-end-cursor (:end_cursor page-info))
|
||||
(assoc :camera-roll-has-next-page (:has_next_page page-info))
|
||||
(assoc :camera-roll-loading-more false))}))
|
||||
|
||||
(fx/defn clear-sending-images
|
||||
{:events [:chat.ui/clear-sending-images]}
|
||||
|
||||
@@ -126,6 +126,7 @@
|
||||
|
||||
text (get-in message [:content :text])]
|
||||
{:dispatch [:chat.ui.input/set-chat-input-text text current-chat-id]
|
||||
:set-input-text [current-chat-id text]
|
||||
:db (-> db
|
||||
(assoc-in [:chat/inputs current-chat-id :metadata :editing-message]
|
||||
message)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
(ns status-im.chat.models-test
|
||||
(:require [cljs.test :refer-macros [deftest is testing]]
|
||||
[status-im.utils.clocks :as utils.clocks]
|
||||
[status-im.chat.models :as chat]))
|
||||
[status-im.chat.models :as chat]
|
||||
[status-im.chat.models.images :as images]))
|
||||
|
||||
(deftest clear-history-test
|
||||
(let [chat-id "1"
|
||||
@@ -91,3 +92,9 @@
|
||||
(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?])))))))
|
||||
|
||||
|
||||
|
||||
(deftest camera-roll-loading-more
|
||||
(let [cofx {:db {:camera-roll-loading-more false}}]
|
||||
(is (= true (images/camera-roll-loading-more cofx true)))))
|
||||
|
||||
@@ -26,12 +26,12 @@
|
||||
|
||||
(def featured
|
||||
[{:name "Status"
|
||||
:id constants/status-community-id}])
|
||||
:id constants/status-community-id}])
|
||||
|
||||
(defn <-request-to-join-community-rpc [r]
|
||||
(clojure.set/rename-keys r {:communityId :community-id
|
||||
:publicKey :public-key
|
||||
:chatId :chat-id}))
|
||||
:publicKey :public-key
|
||||
:chatId :chat-id}))
|
||||
|
||||
(defn <-requests-to-join-community-rpc [requests]
|
||||
(reduce (fn [acc r]
|
||||
@@ -60,11 +60,12 @@
|
||||
|
||||
(defn <-rpc [c]
|
||||
(-> c
|
||||
(clojure.set/rename-keys {:canRequestAccess :can-request-access?
|
||||
:canManageUsers :can-manage-users?
|
||||
:canJoin :can-join?
|
||||
:requestedToJoinAt :requested-to-join-at
|
||||
:isMember :is-member?})
|
||||
(clojure.set/rename-keys {:canRequestAccess :can-request-access?
|
||||
:canManageUsers :can-manage-users?
|
||||
:canDeleteMessageForEveryone :can-delete-message-for-everyone?
|
||||
:canJoin :can-join?
|
||||
:requestedToJoinAt :requested-to-join-at
|
||||
:isMember :is-member?})
|
||||
(update :members walk/stringify-keys)
|
||||
(update :chats <-chats-rpc)
|
||||
(update :categories <-categories-rpc)))
|
||||
@@ -125,67 +126,67 @@
|
||||
(fx/defn import-community
|
||||
{:events [::import]}
|
||||
[cofx community-key]
|
||||
{::json-rpc/call [{:method "wakuext_importCommunity"
|
||||
:params [community-key]
|
||||
{::json-rpc/call [{:method "wakuext_importCommunity"
|
||||
:params [community-key]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [::community-imported %])
|
||||
:on-error #(do
|
||||
(log/error "failed to import community" %)
|
||||
(re-frame/dispatch [::failed-to-import %]))}]})
|
||||
:on-success #(re-frame/dispatch [::community-imported %])
|
||||
:on-error #(do
|
||||
(log/error "failed to import community" %)
|
||||
(re-frame/dispatch [::failed-to-import %]))}]})
|
||||
|
||||
(fx/defn join
|
||||
{:events [:communities/join]}
|
||||
[cofx community-id]
|
||||
{::json-rpc/call [{:method "wakuext_joinCommunity"
|
||||
:params [community-id]
|
||||
{::json-rpc/call [{:method "wakuext_joinCommunity"
|
||||
:params [community-id]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [::joined %])
|
||||
:on-error #(do
|
||||
(log/error "failed to join community" community-id %)
|
||||
(re-frame/dispatch [::failed-to-join %]))}]})
|
||||
:on-success #(re-frame/dispatch [::joined %])
|
||||
:on-error #(do
|
||||
(log/error "failed to join community" community-id %)
|
||||
(re-frame/dispatch [::failed-to-join %]))}]})
|
||||
|
||||
(fx/defn request-to-join
|
||||
{:events [::request-to-join]}
|
||||
[cofx community-id]
|
||||
{::json-rpc/call [{:method "wakuext_requestToJoinCommunity"
|
||||
:params [{:communityId community-id}]
|
||||
{::json-rpc/call [{:method "wakuext_requestToJoinCommunity"
|
||||
:params [{:communityId community-id}]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [::requested-to-join %])
|
||||
:on-error #(do
|
||||
(log/error "failed to request to join community" community-id %)
|
||||
(re-frame/dispatch [::failed-to-request-to-join %]))}]})
|
||||
:on-success #(re-frame/dispatch [::requested-to-join %])
|
||||
:on-error #(do
|
||||
(log/error "failed to request to join community" community-id %)
|
||||
(re-frame/dispatch [::failed-to-request-to-join %]))}]})
|
||||
|
||||
(fx/defn leave
|
||||
{:events [:communities/leave]}
|
||||
[{:keys [db]} community-id]
|
||||
(let [community-chat-ids (map #(str community-id %)
|
||||
(keys (get-in db [:communities community-id :chats])))]
|
||||
{:clear-message-notifications [community-chat-ids
|
||||
(get-in db [:multiaccount :remote-push-notifications-enabled?])]
|
||||
::json-rpc/call [{:method "wakuext_leaveCommunity"
|
||||
:params [community-id]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [::left %])
|
||||
:on-error #(do
|
||||
(log/error "failed to leave community" community-id %)
|
||||
(re-frame/dispatch [::failed-to-leave %]))}]}))
|
||||
{:clear-message-notifications [community-chat-ids
|
||||
(get-in db [:multiaccount :remote-push-notifications-enabled?])]
|
||||
::json-rpc/call [{:method "wakuext_leaveCommunity"
|
||||
:params [community-id]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [::left %])
|
||||
:on-error #(do
|
||||
(log/error "failed to leave community" community-id %)
|
||||
(re-frame/dispatch [::failed-to-leave %]))}]}))
|
||||
|
||||
(fx/defn fetch [_]
|
||||
{::json-rpc/call [{:method "wakuext_communities"
|
||||
:params []
|
||||
{::json-rpc/call [{:method "wakuext_communities"
|
||||
:params []
|
||||
:on-success #(re-frame/dispatch [::fetched %])
|
||||
:on-error #(do
|
||||
(log/error "failed to fetch communities" %)
|
||||
(re-frame/dispatch [::failed-to-fetch %]))}]})
|
||||
:on-error #(do
|
||||
(log/error "failed to fetch communities" %)
|
||||
(re-frame/dispatch [::failed-to-fetch %]))}]})
|
||||
|
||||
(fx/defn chat-created
|
||||
{:events [::chat-created]}
|
||||
[_ community-id user-pk]
|
||||
{::json-rpc/call [{:method "wakuext_sendChatMessage"
|
||||
:params [{:chatId user-pk
|
||||
:text "Upgrade here to see an invitation to community"
|
||||
:communityId community-id
|
||||
:contentType constants/content-type-community}]
|
||||
{::json-rpc/call [{:method "wakuext_sendChatMessage"
|
||||
:params [{:chatId user-pk
|
||||
:text "Upgrade here to see an invitation to community"
|
||||
:communityId community-id
|
||||
:contentType constants/content-type-community}]
|
||||
:js-response true
|
||||
:on-success
|
||||
#(re-frame/dispatch [:transport/message-sent %])
|
||||
@@ -196,56 +197,56 @@
|
||||
{:events [::invite-people-confirmation-pressed]}
|
||||
[cofx user-pk contacts]
|
||||
(let [community-id (fetch-community-id-input cofx)
|
||||
pks (if (seq user-pk)
|
||||
(conj contacts user-pk)
|
||||
contacts)]
|
||||
pks (if (seq user-pk)
|
||||
(conj contacts user-pk)
|
||||
contacts)]
|
||||
(when (seq pks)
|
||||
{::json-rpc/call [{:method "wakuext_inviteUsersToCommunity"
|
||||
:params [{:communityId community-id
|
||||
:users pks}]
|
||||
{::json-rpc/call [{:method "wakuext_inviteUsersToCommunity"
|
||||
:params [{:communityId community-id
|
||||
:users pks}]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [::people-invited %])
|
||||
:on-error #(do
|
||||
(log/error "failed to invite-user community" %)
|
||||
(re-frame/dispatch [::failed-to-invite-people %]))}]})))
|
||||
:on-success #(re-frame/dispatch [::people-invited %])
|
||||
:on-error #(do
|
||||
(log/error "failed to invite-user community" %)
|
||||
(re-frame/dispatch [::failed-to-invite-people %]))}]})))
|
||||
(fx/defn share-community
|
||||
{:events [::share-community-confirmation-pressed]}
|
||||
[cofx user-pk contacts]
|
||||
(let [community-id (fetch-community-id-input cofx)
|
||||
pks (if (seq user-pk)
|
||||
(conj contacts user-pk)
|
||||
contacts)]
|
||||
pks (if (seq user-pk)
|
||||
(conj contacts user-pk)
|
||||
contacts)]
|
||||
(when (seq pks)
|
||||
{::json-rpc/call [{:method "wakuext_shareCommunity"
|
||||
:params [{:communityId community-id
|
||||
:users pks}]
|
||||
{::json-rpc/call [{:method "wakuext_shareCommunity"
|
||||
:params [{:communityId community-id
|
||||
:users pks}]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [::people-invited %])
|
||||
:on-error #(do
|
||||
(log/error "failed to invite-user community" %)
|
||||
(re-frame/dispatch [::failed-to-share-community %]))}]})))
|
||||
:on-success #(re-frame/dispatch [::people-invited %])
|
||||
:on-error #(do
|
||||
(log/error "failed to invite-user community" %)
|
||||
(re-frame/dispatch [::failed-to-share-community %]))}]})))
|
||||
|
||||
(fx/defn create
|
||||
{:events [::create-confirmation-pressed]}
|
||||
[{:keys [db]}]
|
||||
(let [{:keys [name description membership image]} (get db :communities/create)]
|
||||
(let [params {:name name
|
||||
(let [params {:name name
|
||||
:description description
|
||||
:membership membership
|
||||
:color (rand-nth colors/chat-colors)
|
||||
:image (string/replace-first (str image) #"file://" "")
|
||||
:imageAx 0
|
||||
:imageAy 0
|
||||
:imageBx crop-size
|
||||
:imageBy crop-size}]
|
||||
:membership membership
|
||||
:color (rand-nth colors/chat-colors)
|
||||
:image (string/replace-first (str image) #"file://" "")
|
||||
:imageAx 0
|
||||
:imageAy 0
|
||||
:imageBx crop-size
|
||||
:imageBy crop-size}]
|
||||
|
||||
{::json-rpc/call [{:method "wakuext_createCommunity"
|
||||
:params [params]
|
||||
{::json-rpc/call [{:method "wakuext_createCommunity"
|
||||
:params [params]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [::community-created %])
|
||||
:on-error #(do
|
||||
(log/error "failed to create community" %)
|
||||
(re-frame/dispatch [::failed-to-create-community %]))}]})))
|
||||
:on-success #(re-frame/dispatch [::community-created %])
|
||||
:on-error #(do
|
||||
(log/error "failed to create community" %)
|
||||
(re-frame/dispatch [::failed-to-create-community %]))}]})))
|
||||
|
||||
(fx/defn edit
|
||||
{:events [::edit-confirmation-pressed]}
|
||||
@@ -253,15 +254,15 @@
|
||||
(let [{:keys [id name description membership new-image color]} (get db :communities/create)]
|
||||
{::json-rpc/call [{:method "wakuext_editCommunity"
|
||||
:params [{:communityID id
|
||||
:name name
|
||||
:name name
|
||||
:description description
|
||||
:color color
|
||||
:image (string/replace-first (str new-image) #"file://" "")
|
||||
:imageAx 0
|
||||
:imageAy 0
|
||||
:imageBx crop-size
|
||||
:imageBy crop-size
|
||||
:membership membership}]
|
||||
:color color
|
||||
:image (string/replace-first (str new-image) #"file://" "")
|
||||
:imageAx 0
|
||||
:imageAy 0
|
||||
:imageBx crop-size
|
||||
:imageBy crop-size
|
||||
:membership membership}]
|
||||
:on-success #(re-frame/dispatch [::community-edited %])
|
||||
:on-error #(do
|
||||
(log/error "failed to edit community" %)
|
||||
@@ -272,18 +273,18 @@
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [community-id (fetch-community-id-input cofx)
|
||||
{:keys [name description color emoji]} (get db :communities/create-channel)]
|
||||
{::json-rpc/call [{:method "wakuext_createCommunityChat"
|
||||
:params [community-id
|
||||
{:identity {:display_name name
|
||||
:description description
|
||||
:color color
|
||||
:emoji emoji}
|
||||
:permissions {:access constants/community-channel-access-no-membership}}]
|
||||
{::json-rpc/call [{:method "wakuext_createCommunityChat"
|
||||
:params [community-id
|
||||
{:identity {:display_name name
|
||||
:description description
|
||||
:color color
|
||||
:emoji emoji}
|
||||
:permissions {:access constants/community-channel-access-no-membership}}]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [::community-channel-created %])
|
||||
:on-error #(do
|
||||
(log/error "failed to create community channel" %)
|
||||
(re-frame/dispatch [::failed-to-create-community-channel %]))}]}))
|
||||
:on-success #(re-frame/dispatch [::community-channel-created %])
|
||||
:on-error #(do
|
||||
(log/error "failed to create community channel" %)
|
||||
(re-frame/dispatch [::failed-to-create-community-channel %]))}]}))
|
||||
|
||||
(def community-chat-id-length 68)
|
||||
|
||||
@@ -295,21 +296,21 @@
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [{:keys [name description color community-id emoji edit-channel-id category-id position]}
|
||||
(get db :communities/create-channel)]
|
||||
{::json-rpc/call [{:method "wakuext_editCommunityChat"
|
||||
:params [community-id
|
||||
edit-channel-id
|
||||
{:identity {:display_name name
|
||||
:description description
|
||||
:color color
|
||||
:emoji emoji}
|
||||
:category_id category-id
|
||||
:position position
|
||||
:permissions {:access constants/community-channel-access-no-membership}}]
|
||||
{::json-rpc/call [{:method "wakuext_editCommunityChat"
|
||||
:params [community-id
|
||||
edit-channel-id
|
||||
{:identity {:display_name name
|
||||
:description description
|
||||
:color color
|
||||
:emoji emoji}
|
||||
:category_id category-id
|
||||
:position position
|
||||
:permissions {:access constants/community-channel-access-no-membership}}]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [::community-channel-edited %])
|
||||
:on-error #(do
|
||||
(log/error "failed to edit community channel" %)
|
||||
(re-frame/dispatch [::failed-to-edit-community-channel %]))}]}))
|
||||
:on-success #(re-frame/dispatch [::community-channel-edited %])
|
||||
:on-error #(do
|
||||
(log/error "failed to edit community channel" %)
|
||||
(re-frame/dispatch [::failed-to-edit-community-channel %]))}]}))
|
||||
|
||||
(defn require-membership? [permissions]
|
||||
(not= constants/community-no-membership-access (:access permissions)))
|
||||
@@ -461,12 +462,12 @@
|
||||
(fx/defn member-ban
|
||||
{:events [::member-ban]}
|
||||
[cofx community-id public-key]
|
||||
{::json-rpc/call [{:method "wakuext_banUserFromCommunity"
|
||||
:params [{:communityId community-id
|
||||
:user public-key}]
|
||||
{::json-rpc/call [{:method "wakuext_banUserFromCommunity"
|
||||
:params [{:communityId community-id
|
||||
:user public-key}]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [::member-banned %])
|
||||
:on-error #(log/error "failed to ban user from community" community-id public-key %)}]})
|
||||
:on-success #(re-frame/dispatch [::member-banned %])
|
||||
:on-error #(log/error "failed to ban user from community" community-id public-key %)}]})
|
||||
|
||||
(fx/defn member-kicked
|
||||
{:events [::member-kicked]}
|
||||
@@ -478,11 +479,11 @@
|
||||
(fx/defn member-kick
|
||||
{:events [::member-kick]}
|
||||
[cofx community-id public-key]
|
||||
{::json-rpc/call [{:method "wakuext_removeUserFromCommunity"
|
||||
:params [community-id public-key]
|
||||
{::json-rpc/call [{:method "wakuext_removeUserFromCommunity"
|
||||
:params [community-id public-key]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [::member-kicked %])
|
||||
:on-error #(log/error "failed to remove user from community" community-id public-key %)}]})
|
||||
:on-success #(re-frame/dispatch [::member-kicked %])
|
||||
:on-error #(log/error "failed to remove user from community" community-id public-key %)}]})
|
||||
|
||||
(fx/defn delete-community
|
||||
{:events [::delete-community]}
|
||||
@@ -522,88 +523,88 @@
|
||||
(fx/defn accept-request-to-join-pressed
|
||||
{:events [:communities.ui/accept-request-to-join-pressed]}
|
||||
[cofx community-id request-id]
|
||||
{::json-rpc/call [{:method "wakuext_acceptRequestToJoinCommunity"
|
||||
:params [{:id request-id}]
|
||||
{::json-rpc/call [{:method "wakuext_acceptRequestToJoinCommunity"
|
||||
:params [{:id request-id}]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [::request-to-join-accepted community-id request-id %])
|
||||
:on-error #(log/error "failed to accept requests-to-join" community-id request-id %)}]})
|
||||
:on-success #(re-frame/dispatch [::request-to-join-accepted community-id request-id %])
|
||||
:on-error #(log/error "failed to accept requests-to-join" community-id request-id %)}]})
|
||||
|
||||
(fx/defn decline-request-to-join-pressed
|
||||
{:events [:communities.ui/decline-request-to-join-pressed]}
|
||||
[cofx community-id request-id]
|
||||
{::json-rpc/call [{:method "wakuext_declineRequestToJoinCommunity"
|
||||
:params [{:id request-id}]
|
||||
{::json-rpc/call [{:method "wakuext_declineRequestToJoinCommunity"
|
||||
:params [{:id request-id}]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [::request-to-join-declined community-id request-id %])
|
||||
:on-error #(log/error "failed to decline requests-to-join" community-id request-id)}]})
|
||||
:on-success #(re-frame/dispatch [::request-to-join-declined community-id request-id %])
|
||||
:on-error #(log/error "failed to decline requests-to-join" community-id request-id)}]})
|
||||
|
||||
(fx/defn switch-communities-enabled
|
||||
{:events [:multiaccounts.ui/switch-communities-enabled]}
|
||||
[{:keys [db]} enabled?]
|
||||
{::async-storage/set! {:communities-enabled? enabled?}
|
||||
:db (assoc db :communities/enabled? enabled?)})
|
||||
:db (assoc db :communities/enabled? enabled?)})
|
||||
|
||||
(fx/defn create-category
|
||||
{:events [::create-category-confirmation-pressed]}
|
||||
[_ community-id category-title chat-ids]
|
||||
{::json-rpc/call [{:method "wakuext_createCommunityCategory"
|
||||
:params [{:communityId community-id
|
||||
:categoryName category-title
|
||||
:chatIds (map #(string/replace % community-id "") chat-ids)}]
|
||||
{::json-rpc/call [{:method "wakuext_createCommunityCategory"
|
||||
:params [{:communityId community-id
|
||||
:categoryName category-title
|
||||
:chatIds (map #(string/replace % community-id "") chat-ids)}]
|
||||
:js-response true
|
||||
:on-success #(do
|
||||
(re-frame/dispatch [:navigate-back])
|
||||
(re-frame/dispatch [:sanitize-messages-and-process-response %]))
|
||||
:on-error #(log/error "failed to create community category" %)}]})
|
||||
:on-success #(do
|
||||
(re-frame/dispatch [:navigate-back])
|
||||
(re-frame/dispatch [:sanitize-messages-and-process-response %]))
|
||||
:on-error #(log/error "failed to create community category" %)}]})
|
||||
|
||||
(fx/defn remove-chat-from-category
|
||||
{:events [:remove-chat-from-community-category]}
|
||||
[{:keys [db]} community-id id categoryID]
|
||||
(let [category (get-in db [:communities community-id :categories categoryID])
|
||||
(let [category (get-in db [:communities community-id :categories categoryID])
|
||||
category-chats (map :id (filter #(and (= (:categoryID %) categoryID) (not= id (:id %)))
|
||||
(vals (get-in db [:communities community-id :chats]))))]
|
||||
{::json-rpc/call [{:method "wakuext_editCommunityCategory"
|
||||
:params [{:communityId community-id
|
||||
:categoryId categoryID
|
||||
:categoryName (:name category)
|
||||
:chatIds category-chats}]
|
||||
{::json-rpc/call [{:method "wakuext_editCommunityCategory"
|
||||
:params [{:communityId community-id
|
||||
:categoryId categoryID
|
||||
:categoryName (:name category)
|
||||
:chatIds category-chats}]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
|
||||
:on-error #(log/error "failed to remove chat from community" %)}]}))
|
||||
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
|
||||
:on-error #(log/error "failed to remove chat from community" %)}]}))
|
||||
|
||||
(fx/defn delete-community-chat
|
||||
{:events [:delete-community-chat]}
|
||||
[_ community-id chat-id]
|
||||
{::json-rpc/call [{:method "wakuext_deleteCommunityChat"
|
||||
:params [community-id chat-id]
|
||||
{::json-rpc/call [{:method "wakuext_deleteCommunityChat"
|
||||
:params [community-id chat-id]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
|
||||
:on-error #(log/error "failed to delete community chat" %)}]})
|
||||
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
|
||||
:on-error #(log/error "failed to delete community chat" %)}]})
|
||||
|
||||
(fx/defn delete-category
|
||||
{:events [:delete-community-category]}
|
||||
[_ community-id category-id]
|
||||
{::json-rpc/call [{:method "wakuext_deleteCommunityCategory"
|
||||
:params [{:communityId community-id
|
||||
:categoryId category-id}]
|
||||
{::json-rpc/call [{:method "wakuext_deleteCommunityCategory"
|
||||
:params [{:communityId community-id
|
||||
:categoryId category-id}]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
|
||||
:on-error #(log/error "failed to delete community category" %)}]})
|
||||
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
|
||||
:on-error #(log/error "failed to delete community category" %)}]})
|
||||
|
||||
(fx/defn change-category
|
||||
{:events [::change-category-confirmation-pressed]}
|
||||
[cofx community-id category-id {:keys [id position categoryID]}]
|
||||
(if (not (string/blank? category-id))
|
||||
{::json-rpc/call [{:method "wakuext_reorderCommunityChat"
|
||||
:params [{:communityId community-id
|
||||
:categoryId category-id
|
||||
:chatId id
|
||||
:position position}]
|
||||
{::json-rpc/call [{:method "wakuext_reorderCommunityChat"
|
||||
:params [{:communityId community-id
|
||||
:categoryId category-id
|
||||
:chatId id
|
||||
:position position}]
|
||||
:js-response true
|
||||
:on-success #(do
|
||||
(re-frame/dispatch [:navigate-back])
|
||||
(re-frame/dispatch [:sanitize-messages-and-process-response %]))
|
||||
:on-error #(log/error "failed to change community category" %)}]}
|
||||
:on-success #(do
|
||||
(re-frame/dispatch [:navigate-back])
|
||||
(re-frame/dispatch [:sanitize-messages-and-process-response %]))
|
||||
:on-error #(log/error "failed to change community category" %)}]}
|
||||
(fx/merge cofx
|
||||
(navigation/navigate-back)
|
||||
(remove-chat-from-category community-id id categoryID))))
|
||||
@@ -611,22 +612,22 @@
|
||||
(fx/defn reorder-category-chat
|
||||
{:events [::reorder-community-category-chat]}
|
||||
[_ community-id category-id chat-id new-position]
|
||||
{::json-rpc/call [{:method "wakuext_reorderCommunityChat"
|
||||
:params [{:communityId community-id
|
||||
:categoryId category-id
|
||||
:chatId chat-id
|
||||
:position new-position}]
|
||||
{::json-rpc/call [{:method "wakuext_reorderCommunityChat"
|
||||
:params [{:communityId community-id
|
||||
:categoryId category-id
|
||||
:chatId chat-id
|
||||
:position new-position}]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
|
||||
:on-error #(log/error "failed to reorder community category chat" %)}]})
|
||||
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
|
||||
:on-error #(log/error "failed to reorder community category chat" %)}]})
|
||||
|
||||
(fx/defn reorder-category
|
||||
{:events [::reorder-community-category]}
|
||||
[_ community-id category-id new-position]
|
||||
{::json-rpc/call [{:method "wakuext_reorderCommunityCategories"
|
||||
:params [{:communityId community-id
|
||||
:categoryId category-id
|
||||
:position new-position}]
|
||||
{::json-rpc/call [{:method "wakuext_reorderCommunityCategories"
|
||||
:params [{:communityId community-id
|
||||
:categoryId category-id
|
||||
:position new-position}]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])
|
||||
:on-error #(log/error "failed to reorder community category" %)}]})
|
||||
@@ -672,3 +673,45 @@
|
||||
{::async-storage/get {:keys keys
|
||||
:cb #(re-frame/dispatch
|
||||
[::category-states-loaded community-id hashes %])}}))
|
||||
|
||||
(fx/defn navigate-to-community
|
||||
{:events [:communities/navigate-to-community]}
|
||||
[cofx community-id]
|
||||
(fx/merge cofx
|
||||
(navigation/pop-to-root-tab :shell-stack)
|
||||
(navigation/navigate-to-nav2 :community community-id true)))
|
||||
|
||||
(fx/defn member-role-updated
|
||||
{:events [:community.member/role-updated]}
|
||||
[cofx response-js]
|
||||
(fx/merge cofx
|
||||
(bottom-sheet/hide-bottom-sheet)
|
||||
(handle-response response-js)))
|
||||
|
||||
(fx/defn add-role-to-member
|
||||
{:events [:community.member/add-role]}
|
||||
[cofx community-id public-key role-id]
|
||||
{::json-rpc/call [{:method "wakuext_addRoleToMember"
|
||||
:params [{:communityId community-id
|
||||
:user public-key
|
||||
:role role-id}]
|
||||
:on-success #(re-frame/dispatch [:community.member/role-updated %])
|
||||
:on-error #(log/error "failed to add role to member"
|
||||
{:error %
|
||||
:community-id community-id
|
||||
:public-key public-key
|
||||
:role-id role-id})}]})
|
||||
|
||||
(fx/defn remove-role-from-member
|
||||
{:events [:community.member/remove-role]}
|
||||
[_ community-id public-key role-id]
|
||||
{::json-rpc/call [{:method "wakuext_removeRoleFromMember"
|
||||
:params [{:communityId community-id
|
||||
:user public-key
|
||||
:role role-id}]}
|
||||
:on-success #(re-frame/dispatch [:community.member/role-updated %])
|
||||
:on-error #(log/error "failed to remove role from member"
|
||||
{:error %
|
||||
:community-id community-id
|
||||
:public-key public-key
|
||||
:role-id role-id})]})
|
||||
|
||||
@@ -198,3 +198,7 @@
|
||||
(def ^:const sticker-pack-status-owned 3)
|
||||
|
||||
(def ^:const delete-message-for-me-undo-time-limit-ms 4000)
|
||||
|
||||
(def ^:const community-member-role-all 1)
|
||||
(def ^:const community-member-role-manage-users 2)
|
||||
(def ^:const community-member-role-moderator 3)
|
||||
|
||||
@@ -151,3 +151,8 @@
|
||||
:mutual-contact-enabled?
|
||||
enabled?
|
||||
nil))
|
||||
|
||||
(fx/defn set-search-query
|
||||
{:events [:contacts/set-search-query]}
|
||||
[{:keys [db] :as cofx} value]
|
||||
{:db (assoc db :contacts/search-query value)})
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
status-im.mailserver.constants
|
||||
[status-im.mailserver.core :as mailserver]
|
||||
[status-im.multiaccounts.biometric.core :as biometric]
|
||||
[status-im.multiaccounts.core :as multiaccounts]
|
||||
status-im.multiaccounts.login.core
|
||||
status-im.multiaccounts.logout.core
|
||||
status-im.multiaccounts.update.core
|
||||
@@ -57,7 +56,10 @@
|
||||
status-im.wallet.custom-tokens.core
|
||||
status-im.wallet-connect.core
|
||||
status-im.wallet-connect-legacy.core
|
||||
status-im.network.net-info))
|
||||
status-im.network.net-info
|
||||
status-im.visibility-status-popover.core
|
||||
status-im2.contexts.shell.events
|
||||
[status-im.multiaccounts.model :as multiaccounts.model]))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:dismiss-keyboard
|
||||
@@ -111,9 +113,10 @@
|
||||
|
||||
(fx/defn system-theme-mode-changed
|
||||
{:events [:system-theme-mode-changed]}
|
||||
[_ theme]
|
||||
{::multiaccounts/switch-theme (if (= :dark theme) 2 1)
|
||||
:dispatch [:reload-new-ui]})
|
||||
[cofx _]
|
||||
(when (multiaccounts.model/logged-in? cofx)
|
||||
{:multiaccounts.ui/switch-theme (get-in cofx [:db :multiaccount :appearance])
|
||||
:dispatch [:reload-new-ui]}))
|
||||
|
||||
(def authentication-options
|
||||
{:reason (i18n/label :t/biometric-auth-reason-login)})
|
||||
|
||||
@@ -21,89 +21,89 @@
|
||||
{:events [:chat-removed]}
|
||||
[cofx response]
|
||||
(fx/merge cofx
|
||||
{:db (dissoc (:db cofx) :current-chat-id)
|
||||
{:db (dissoc (:db cofx) :current-chat-id)
|
||||
:dispatch-n [[:sanitize-messages-and-process-response response]
|
||||
[:pop-to-root-tab :chat-stack]]}
|
||||
(notification-center/get-activity-center-notifications-count)))
|
||||
|
||||
(fx/defn handle-chat-update
|
||||
{:events [:chat-updated]}
|
||||
[_ response]
|
||||
[_ response do-not-navigate?]
|
||||
{:dispatch-n [[:sanitize-messages-and-process-response response]
|
||||
[:navigate-chat-updated (.-id (aget (.-chats response) 0))]]})
|
||||
(when-not do-not-navigate? [:navigate-chat-updated (.-id (aget (.-chats response) 0))])]})
|
||||
|
||||
(fx/defn remove-member
|
||||
"Format group update message and sign membership"
|
||||
{:events [:group-chats.ui/remove-member-pressed]}
|
||||
[_ chat-id member]
|
||||
{::json-rpc/call [{:method "wakuext_removeMemberFromGroupChat"
|
||||
:params [nil chat-id member]
|
||||
[_ chat-id member do-not-navigate?]
|
||||
{::json-rpc/call [{:method "wakuext_removeMemberFromGroupChat"
|
||||
:params [nil chat-id member]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:chat-updated %])}]})
|
||||
:on-success #(re-frame/dispatch [:chat-updated % do-not-navigate?])}]})
|
||||
|
||||
(fx/defn join-chat
|
||||
{:events [:group-chats.ui/join-pressed]}
|
||||
[_ chat-id]
|
||||
{::json-rpc/call [{:method "wakuext_confirmJoiningGroup"
|
||||
:params [chat-id]
|
||||
{::json-rpc/call [{:method "wakuext_confirmJoiningGroup"
|
||||
:params [chat-id]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:chat-updated %])}]})
|
||||
:on-success #(re-frame/dispatch [:chat-updated %])}]})
|
||||
|
||||
(fx/defn create
|
||||
{:events [:group-chats.ui/create-pressed]
|
||||
{:events [:group-chats.ui/create-pressed]
|
||||
:interceptors [(re-frame/inject-cofx :random-guid-generator)]}
|
||||
[{:keys [db] :as cofx} group-name]
|
||||
(let [selected-contacts (:group/selected-contacts db)]
|
||||
{::json-rpc/call [{:method "wakuext_createGroupChatWithMembers"
|
||||
:params [nil group-name (into [] selected-contacts)]
|
||||
{::json-rpc/call [{:method "wakuext_createGroupChatWithMembers"
|
||||
:params [nil group-name (into [] selected-contacts)]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:chat-updated %])}]}))
|
||||
:on-success #(re-frame/dispatch [:chat-updated %])}]}))
|
||||
|
||||
(fx/defn create-from-link
|
||||
[cofx {:keys [chat-id invitation-admin chat-name]}]
|
||||
(if (get-in cofx [:db :chats chat-id])
|
||||
{:dispatch-n [[:accept-all-activity-center-notifications-from-chat chat-id]
|
||||
[:chat.ui/navigate-to-chat chat-id]]}
|
||||
{::json-rpc/call [{:method "wakuext_createGroupChatFromInvitation"
|
||||
:params [chat-name chat-id invitation-admin]
|
||||
{::json-rpc/call [{:method "wakuext_createGroupChatFromInvitation"
|
||||
:params [chat-name chat-id invitation-admin]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:chat-updated %])}]}))
|
||||
:on-success #(re-frame/dispatch [:chat-updated %])}]}))
|
||||
|
||||
(fx/defn make-admin
|
||||
{:events [:group-chats.ui/make-admin-pressed]}
|
||||
[_ chat-id member]
|
||||
{::json-rpc/call [{:method "wakuext_addAdminsToGroupChat"
|
||||
:params [nil chat-id [member]]
|
||||
{::json-rpc/call [{:method "wakuext_addAdminsToGroupChat"
|
||||
:params [nil chat-id [member]]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:chat-updated %])}]})
|
||||
:on-success #(re-frame/dispatch [:chat-updated %])}]})
|
||||
|
||||
(fx/defn add-members
|
||||
"Add members to a group chat"
|
||||
{:events [:group-chats.ui/add-members-pressed]}
|
||||
[{{:keys [current-chat-id selected-participants]} :db :as cofx}]
|
||||
{::json-rpc/call [{:method "wakuext_addMembersToGroupChat"
|
||||
:params [nil current-chat-id selected-participants]
|
||||
{::json-rpc/call [{:method "wakuext_addMembersToGroupChat"
|
||||
:params [nil current-chat-id selected-participants]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:chat-updated %])}]})
|
||||
:on-success #(re-frame/dispatch [:chat-updated %])}]})
|
||||
|
||||
(fx/defn add-members-from-invitation
|
||||
"Add members to a group chat"
|
||||
{:events [:group-chats.ui/add-members-from-invitation]}
|
||||
[{{:keys [current-chat-id] :as db} :db :as cofx} id participant]
|
||||
{:db (assoc-in db [:group-chat/invitations id :state] constants/invitation-state-approved)
|
||||
::json-rpc/call [{:method "wakuext_addMembersToGroupChat"
|
||||
:params [nil current-chat-id [participant]]
|
||||
::json-rpc/call [{:method "wakuext_addMembersToGroupChat"
|
||||
:params [nil current-chat-id [participant]]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:chat-updated %])}]})
|
||||
:on-success #(re-frame/dispatch [:chat-updated %])}]})
|
||||
|
||||
(fx/defn leave
|
||||
"Leave chat"
|
||||
{:events [:group-chats.ui/leave-chat-confirmed]}
|
||||
[{:keys [db] :as cofx} chat-id]
|
||||
{::json-rpc/call [{:method "wakuext_leaveGroupChat"
|
||||
:params [nil chat-id true]
|
||||
{::json-rpc/call [{:method "wakuext_leaveGroupChat"
|
||||
:params [nil chat-id true]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:chat-removed %])}]})
|
||||
:on-success #(re-frame/dispatch [:chat-removed %])}]})
|
||||
|
||||
(fx/defn remove
|
||||
"Remove chat"
|
||||
@@ -125,10 +125,10 @@
|
||||
[{:keys [db] :as cofx} chat-id new-name]
|
||||
(when (valid-name? new-name)
|
||||
{:db (assoc-in db [:chats chat-id :name] new-name)
|
||||
::json-rpc/call [{:method "wakuext_changeGroupChatName"
|
||||
:params [nil chat-id new-name]
|
||||
::json-rpc/call [{:method "wakuext_changeGroupChatName"
|
||||
:params [nil chat-id new-name]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:chat-updated %])}]}))
|
||||
:on-success #(re-frame/dispatch [:chat-updated %])}]}))
|
||||
|
||||
(fx/defn membership-retry
|
||||
{:events [:group-chats.ui/membership-retry]}
|
||||
@@ -147,19 +147,19 @@
|
||||
(let [{:keys [invitation-admin]} (get chats current-chat-id)
|
||||
message (get-in db [:chat/memberships current-chat-id :message])]
|
||||
{:db (assoc-in db [:chat/memberships current-chat-id] nil)
|
||||
::json-rpc/call [{:method "wakuext_sendGroupChatInvitationRequest"
|
||||
:params [nil current-chat-id invitation-admin message]
|
||||
::json-rpc/call [{:method "wakuext_sendGroupChatInvitationRequest"
|
||||
:params [nil current-chat-id invitation-admin message]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]}))
|
||||
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]}))
|
||||
|
||||
(fx/defn send-group-chat-membership-rejection
|
||||
"Send group chat membership rejection"
|
||||
{:events [:send-group-chat-membership-rejection]}
|
||||
[cofx invitation-id]
|
||||
{::json-rpc/call [{:method "wakuext_sendGroupChatInvitationRejection"
|
||||
:params [nil invitation-id]
|
||||
{::json-rpc/call [{:method "wakuext_sendGroupChatInvitationRejection"
|
||||
:params [nil invitation-id]
|
||||
:js-response true
|
||||
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]})
|
||||
:on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %])}]})
|
||||
|
||||
(fx/defn handle-invitations
|
||||
[{db :db} invitations]
|
||||
|
||||
@@ -5,8 +5,11 @@
|
||||
;;TODO (14/11/22 flexsurfer) this namespace has been moved to the status-im2 namespace, we keep this only for old (status 1.0) code,
|
||||
;; can be removed with old code later
|
||||
|
||||
(def get-keyword
|
||||
(if js/global.__TEST__ str keyword))
|
||||
|
||||
(def default-device-language
|
||||
(keyword (.-language react-native-languages)))
|
||||
(get-keyword (.-language react-native-languages)))
|
||||
|
||||
(def languages #{:ar :bn :de :el :en :es :es_419 :es_AR :fil :fr :hi :id :in :it :ja :ko :ms :nl :pl :pt :pt_BR :ru :tr :vi :zh :zh_Hant :zh_TW})
|
||||
|
||||
@@ -16,17 +19,17 @@
|
||||
|
||||
(defn valid-language [lang]
|
||||
(if (contains? languages lang)
|
||||
(keyword lang)
|
||||
(get-keyword lang)
|
||||
(let [parts (string/split (name lang) #"[\-\_]")
|
||||
short-lang (keyword (str (first parts) "_" (second parts)))
|
||||
shortest-lang (keyword (first parts))]
|
||||
short-lang (get-keyword (str (first parts) "_" (second parts)))
|
||||
shortest-lang (get-keyword (first parts))]
|
||||
(if (and (> (count parts) 2) (contains? languages short-lang))
|
||||
short-lang
|
||||
(when (contains? languages shortest-lang)
|
||||
shortest-lang)))))
|
||||
|
||||
(defn require-translation [lang-key]
|
||||
(when-let [lang (valid-language (keyword lang-key))]
|
||||
(when-let [lang (valid-language (get-keyword lang-key))]
|
||||
(case lang
|
||||
:ar (js/require "../translations/ar.json")
|
||||
:bn (js/require "../translations/bn.json")
|
||||
@@ -63,10 +66,10 @@
|
||||
(assoc default-device-language
|
||||
(require-translation (-> (name default-device-language)
|
||||
(string/replace "-" "_")
|
||||
keyword)))))
|
||||
get-keyword)))))
|
||||
|
||||
(defn load-language [lang]
|
||||
(when-let [lang-key (valid-language (keyword lang))]
|
||||
(when-let [lang-key (valid-language (get-keyword lang))]
|
||||
(when-not (contains? @loaded-languages lang-key)
|
||||
(aset (.-translations i18n-js)
|
||||
lang
|
||||
|
||||
@@ -162,7 +162,7 @@
|
||||
{::blank-preview-flag-changed private?}))
|
||||
|
||||
(re-frame/reg-fx
|
||||
::switch-theme
|
||||
:multiaccounts.ui/switch-theme
|
||||
(fn [theme-id]
|
||||
(let [theme (if (or (= 2 theme-id) (and (= 0 theme-id) (utils.theme/dark-mode?)))
|
||||
:dark
|
||||
@@ -173,7 +173,7 @@
|
||||
{:events [:multiaccounts.ui/appearance-switched]}
|
||||
[cofx theme]
|
||||
(fx/merge cofx
|
||||
{::switch-theme theme}
|
||||
{:multiaccounts.ui/switch-theme theme}
|
||||
(multiaccounts.update/multiaccount-update :appearance theme {})))
|
||||
|
||||
(fx/defn switch-profile-picture-show-to
|
||||
|
||||
@@ -336,7 +336,7 @@
|
||||
:on-success #(re-frame/dispatch [::initialize-dapp-permissions %])}]})
|
||||
|
||||
(fx/defn initialize-appearance [cofx]
|
||||
{::multiaccounts/switch-theme (get-in cofx [:db :multiaccount :appearance])})
|
||||
{:multiaccounts.ui/switch-theme (get-in cofx [:db :multiaccount :appearance])})
|
||||
|
||||
(fx/defn get-group-chat-invitations [_]
|
||||
{::json-rpc/call
|
||||
@@ -392,6 +392,7 @@
|
||||
(get-node-config)
|
||||
(communities/fetch)
|
||||
(logging/set-log-level (:log-level multiaccount))
|
||||
(notifications-center/get-activity-center-notifications)
|
||||
(notifications-center/get-activity-center-notifications-count))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
{:init-root-fx :progress
|
||||
:chat.ui/clear-inputs nil
|
||||
:chat.ui/clear-inputs-old nil
|
||||
:new-ui/reset-bottom-tabs nil
|
||||
:shell/reset-bottom-tabs nil
|
||||
:hide-popover nil
|
||||
::logout nil
|
||||
::multiaccounts/webview-debug-changed false
|
||||
|
||||
@@ -47,7 +47,6 @@
|
||||
:collectible-dark (js/require "../resources/images/ui/collectible-dark.png")
|
||||
:hand-wave (js/require "../resources/images/ui/hand-wave.png")
|
||||
:graph (js/require "../resources/images/ui/graph.png")
|
||||
:switcher (js/require "../resources/images/ui/switcher.png")
|
||||
:discover (js/require "../resources/images/ui/discover.png")
|
||||
:community-cover (js/require "../resources/images/ui/community-cover.png")
|
||||
:lifestyle (js/require "../resources/images/ui/lifestyle.png")
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
(ns status-im.ui.screens.activity-center.notification.common.style
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
|
||||
(def user-avatar-tag
|
||||
{:background-color colors/white-opa-10})
|
||||
|
||||
(def user-avatar-tag-text
|
||||
{:color colors/white})
|
||||
@@ -0,0 +1,17 @@
|
||||
(ns status-im.ui.screens.activity-center.notification.common.view
|
||||
(:require [utils.re-frame :as rf]
|
||||
[quo2.core :as quo2]
|
||||
[status-im.multiaccounts.core :as multiaccounts]
|
||||
[status-im.ui.screens.activity-center.notification.common.style :as style]
|
||||
[status-im.ui.screens.activity-center.utils :as activity-center.utils]))
|
||||
|
||||
(defn user-avatar-tag [user]
|
||||
(let [contact (rf/sub [:contacts/contact-by-identity user])]
|
||||
[quo2/user-avatar-tag
|
||||
{:color :purple
|
||||
:override-theme :dark
|
||||
:size :small
|
||||
:style style/user-avatar-tag
|
||||
:text-style style/user-avatar-tag-text}
|
||||
(activity-center.utils/contact-name contact)
|
||||
(multiaccounts/displayed-photo contact)]))
|
||||
@@ -1,11 +0,0 @@
|
||||
(ns status-im.ui.screens.activity-center.notification.contact-request.style
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
|
||||
(def context-tag-text
|
||||
{:color colors/white})
|
||||
|
||||
(def user-avatar-tag-text
|
||||
{:color colors/white})
|
||||
|
||||
(def user-avatar-tag
|
||||
{:background-color colors/white-opa-10})
|
||||
@@ -3,16 +3,13 @@
|
||||
[quo2.core :as quo2]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.multiaccounts.core :as multiaccounts]
|
||||
[status-im.ui.screens.activity-center.notification.contact-request.style :as style]
|
||||
[status-im.ui.screens.activity-center.utils :as activity-center.utils]
|
||||
[status-im.ui.screens.activity-center.notification.common.view :as common]
|
||||
[status-im.utils.datetime :as datetime]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn view
|
||||
[{:keys [id author message last-message] :as notification}]
|
||||
(let [message (or message last-message)
|
||||
contact (rf/sub [:contacts/contact-by-identity author])
|
||||
pressable (case (:contact-request-state message)
|
||||
constants/contact-request-message-state-accepted
|
||||
;; NOTE(2022-09-21): We need to dispatch to
|
||||
@@ -30,14 +27,7 @@
|
||||
:icon :main-icons2/add-user
|
||||
:timestamp (datetime/timestamp->relative (:timestamp notification))
|
||||
:unread? (not (:read notification))
|
||||
:context [[quo2/user-avatar-tag
|
||||
{:color :purple
|
||||
:override-theme :dark
|
||||
:size :small
|
||||
:style style/user-avatar-tag
|
||||
:text-style style/user-avatar-tag-text}
|
||||
(activity-center.utils/contact-name contact)
|
||||
(multiaccounts/displayed-photo contact)]
|
||||
:context [[common/user-avatar-tag author]
|
||||
(i18n/label :t/contact-request-sent)]
|
||||
:message {:body (get-in message [:content :text])}
|
||||
:status (case (:contact-request-state message)
|
||||
@@ -51,9 +41,13 @@
|
||||
{:button-1 {:label (i18n/label :t/decline)
|
||||
:accessibility-label :decline-contact-request
|
||||
:type :danger
|
||||
:on-press #(rf/dispatch [:contact-requests.ui/decline-request id])}
|
||||
:on-press (fn []
|
||||
(rf/dispatch [:contact-requests.ui/decline-request id])
|
||||
(rf/dispatch [:activity-center.notifications/mark-as-read id]))}
|
||||
:button-2 {:label (i18n/label :t/accept)
|
||||
:accessibility-label :accept-contact-request
|
||||
:type :positive
|
||||
:on-press #(rf/dispatch [:contact-requests.ui/accept-request id])}}
|
||||
:on-press (fn []
|
||||
(rf/dispatch [:contact-requests.ui/accept-request id])
|
||||
(rf/dispatch [:activity-center.notifications/mark-as-read id]))}}
|
||||
nil))])))
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
(ns status-im.ui.screens.activity-center.notification.contact-verification.style
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
|
||||
(def context-tag-text
|
||||
{:color colors/white})
|
||||
|
||||
(def user-avatar-tag-text
|
||||
{:color colors/white})
|
||||
|
||||
(def user-avatar-tag
|
||||
{:background-color colors/white-opa-10})
|
||||
@@ -3,9 +3,7 @@
|
||||
[quo2.core :as quo2]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.multiaccounts.core :as multiaccounts]
|
||||
[status-im.ui.screens.activity-center.notification.contact-verification.style :as style]
|
||||
[status-im.ui.screens.activity-center.utils :as activity-center.utils]
|
||||
[status-im.ui.screens.activity-center.notification.common.view :as common]
|
||||
[status-im.utils.datetime :as datetime]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
@@ -17,24 +15,16 @@
|
||||
|
||||
(defn- context-tags
|
||||
[challenger? {:keys [author contact-verification-status]}]
|
||||
(let [contact (rf/sub [:contacts/contact-by-identity author])]
|
||||
[[quo2/user-avatar-tag
|
||||
{:color :purple
|
||||
:override-theme :dark
|
||||
:size :small
|
||||
:style style/user-avatar-tag
|
||||
:text-style style/user-avatar-tag-text}
|
||||
(activity-center.utils/contact-name contact)
|
||||
(multiaccounts/displayed-photo contact)]
|
||||
(if challenger?
|
||||
(when (or (= contact-verification-status constants/contact-verification-status-accepted)
|
||||
(= contact-verification-status constants/contact-verification-status-trusted)
|
||||
(= contact-verification-status constants/contact-verification-status-untrustworthy))
|
||||
(str (str/lower-case (i18n/label :t/replied)) ":"))
|
||||
(when (or (= contact-verification-status constants/contact-verification-status-accepted)
|
||||
(= contact-verification-status constants/contact-verification-status-pending)
|
||||
(= contact-verification-status constants/contact-verification-status-declined))
|
||||
(str (i18n/label :t/identity-verification-request-sent) ":")))]))
|
||||
[[common/user-avatar-tag author]
|
||||
(if challenger?
|
||||
(when (or (= contact-verification-status constants/contact-verification-status-accepted)
|
||||
(= contact-verification-status constants/contact-verification-status-trusted)
|
||||
(= contact-verification-status constants/contact-verification-status-untrustworthy))
|
||||
(str (str/lower-case (i18n/label :t/replied)) ":"))
|
||||
(when (or (= contact-verification-status constants/contact-verification-status-accepted)
|
||||
(= contact-verification-status constants/contact-verification-status-pending)
|
||||
(= contact-verification-status constants/contact-verification-status-declined))
|
||||
(str (i18n/label :t/identity-verification-request-sent) ":")))])
|
||||
|
||||
(defn- activity-message
|
||||
[challenger? {:keys [contact-verification-status message reply-message]}]
|
||||
@@ -85,21 +75,29 @@
|
||||
{:button-1 {:label (i18n/label :t/untrustworthy)
|
||||
:accessibility-label :mark-contact-verification-as-untrustworthy
|
||||
:type :danger
|
||||
:on-press #(rf/dispatch [:activity-center.contact-verification/mark-as-untrustworthy id])}
|
||||
:on-press (fn []
|
||||
(rf/dispatch [:activity-center.contact-verification/mark-as-untrustworthy id])
|
||||
(rf/dispatch [:activity-center.notifications/mark-as-read id]))}
|
||||
:button-2 {:label (i18n/label :t/accept)
|
||||
:accessibility-label :mark-contact-verification-as-trusted
|
||||
:type :positive
|
||||
:on-press #(rf/dispatch [:activity-center.contact-verification/mark-as-trusted id])}})
|
||||
:on-press (fn []
|
||||
(rf/dispatch [:activity-center.contact-verification/mark-as-trusted id])
|
||||
(rf/dispatch [:activity-center.notifications/mark-as-read id]))}})
|
||||
(when (= contact-verification-status constants/contact-verification-status-pending)
|
||||
{:button-1 {:label (i18n/label :t/decline)
|
||||
:accessibility-label :decline-contact-verification
|
||||
:type :danger
|
||||
:on-press #(hide-bottom-sheet-and-dispatch [:activity-center.contact-verification/decline id])}
|
||||
:on-press (fn []
|
||||
(hide-bottom-sheet-and-dispatch [:activity-center.contact-verification/decline id])
|
||||
(rf/dispatch [:activity-center.notifications/mark-as-read id]))}
|
||||
:button-2 (if replying?
|
||||
{:label (i18n/label :t/send-reply)
|
||||
:accessibility-label :reply-to-contact-verification
|
||||
:type :primary
|
||||
:on-press #(hide-bottom-sheet-and-dispatch [:activity-center.contact-verification/reply id @reply])}
|
||||
:on-press (fn []
|
||||
(hide-bottom-sheet-and-dispatch [:activity-center.contact-verification/reply id @reply])
|
||||
(rf/dispatch [:activity-center.notifications/mark-as-read id]))}
|
||||
{:label (i18n/label :t/message-reply)
|
||||
:accessibility-label :send-reply-to-contact-verification
|
||||
:type :primary
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
(ns status-im.ui.screens.activity-center.notification.mentions.style
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
|
||||
(def tag
|
||||
{:background-color colors/white-opa-10})
|
||||
|
||||
(def tag-text
|
||||
{:color colors/white})
|
||||
|
||||
(def mention-text
|
||||
{:color colors/white
|
||||
:border-radius 6
|
||||
:padding-horizontal 3
|
||||
:background-color colors/white-opa-10})
|
||||
@@ -0,0 +1,48 @@
|
||||
(ns status-im.ui.screens.activity-center.notification.mentions.view
|
||||
(:require [quo.components.animated.pressable :as animation]
|
||||
[quo2.core :as quo2]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.ui.screens.activity-center.notification.common.view :as common]
|
||||
[status-im.ui.screens.activity-center.notification.mentions.style :as style]
|
||||
[status-im.utils.datetime :as datetime]
|
||||
[utils.re-frame :as rf]
|
||||
[clojure.string :as str]))
|
||||
|
||||
(defn message-body [message]
|
||||
(let [parsed-text (get-in message [:content :parsed-text])
|
||||
parsed-text-children (:children (first parsed-text))]
|
||||
(into [quo2/text {:number-of-lines 2
|
||||
:style style/tag-text
|
||||
:accessibility-label :activity-message-body
|
||||
:size :paragraph-1}]
|
||||
(map-indexed (fn [index {:keys [type literal]}]
|
||||
^{:key index}
|
||||
(case type
|
||||
"mention" [quo2/text {:style style/mention-text
|
||||
:size :paragraph-1}
|
||||
(str "@" (rf/sub [:contacts/contact-name-by-identity literal]))]
|
||||
literal)) parsed-text-children))))
|
||||
|
||||
(defn view
|
||||
[{:keys [author chat-name chat-id message] :as notification}]
|
||||
[animation/pressable
|
||||
{:on-press (fn []
|
||||
(rf/dispatch [:hide-popover])
|
||||
(rf/dispatch [:chat.ui/navigate-to-chat chat-id]))}
|
||||
[quo2/activity-log
|
||||
{:title (i18n/label :t/mention)
|
||||
:icon :i/mention
|
||||
:timestamp (datetime/timestamp->relative (:timestamp notification))
|
||||
:unread? (not (:read notification))
|
||||
:context [[common/user-avatar-tag author]
|
||||
[quo2/text {:style style/tag-text} (str/lower-case (i18n/label :t/on))]
|
||||
;; TODO (@smohamedjavid): The `group-avatar-tag` component
|
||||
;; does NOT support displaying channel name along with community/chat name.
|
||||
;; Need to update the component to support it.
|
||||
[quo2/group-avatar-tag chat-name {:size :small
|
||||
:override-theme :dark
|
||||
:color colors/primary-50
|
||||
:style style/tag
|
||||
:text-style style/tag-text}]]
|
||||
:message {:body (message-body message)}}]])
|
||||
@@ -8,6 +8,7 @@
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.ui.screens.activity-center.notification.contact-request.view :as contact-request]
|
||||
[status-im.ui.screens.activity-center.notification.contact-verification.view :as contact-verification]
|
||||
[status-im.ui.screens.activity-center.notification.mentions.view :as mentions]
|
||||
[status-im.ui.screens.activity-center.style :as style]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
@@ -112,6 +113,9 @@
|
||||
types/contact-request
|
||||
[contact-request/view notification]
|
||||
|
||||
types/mention
|
||||
[mentions/view notification]
|
||||
|
||||
nil)])
|
||||
|
||||
(defn activity-center
|
||||
@@ -124,8 +128,9 @@
|
||||
(react/effect! #(rf/dispatch [:activity-center.notifications/fetch-first-page]))
|
||||
[rn/view {:style (style/screen-container window-width top bottom)}
|
||||
[header]
|
||||
[rn/flat-list {:data notifications
|
||||
:empty-component [empty-tab]
|
||||
:key-fn :id
|
||||
:on-end-reached #(rf/dispatch [:activity-center.notifications/fetch-next-page])
|
||||
:render-fn render-notification}]]))])
|
||||
[rn/flat-list {:data notifications
|
||||
:empty-component [empty-tab]
|
||||
:key-fn :id
|
||||
:on-scroll-to-index-failed identity
|
||||
:on-end-reached #(rf/dispatch [:activity-center.notifications/fetch-next-page])
|
||||
:render-fn render-notification}]]))])
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
[status-im.ui.components.list.views :as list]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.components.topbar :as topbar]
|
||||
[status-im.utils.debounce :as debounce]
|
||||
[utils.debounce :as debounce]
|
||||
[status-im.utils.utils :as utils]
|
||||
[reagent.core :as reagent]
|
||||
[quo.react-native :as rn]
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
[status-im.ui.components.icons.icons :as icons]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.screens.browser.styles :as styles]
|
||||
[status-im.utils.debounce :as debounce]
|
||||
[utils.debounce :as debounce]
|
||||
[status-im.network.core :as network]
|
||||
[quo.design-system.colors :as colors]
|
||||
[quo.core :as quo])
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
[status-im.ui.screens.browser.permissions.views :as permissions.views]
|
||||
[status-im.ui.screens.browser.site-blocked.views :as site-blocked.views]
|
||||
[status-im.ui.screens.browser.styles :as styles]
|
||||
[status-im.utils.debounce :as debounce]
|
||||
[utils.debounce :as debounce]
|
||||
[status-im.utils.http :as http]
|
||||
[status-im.utils.js-resources :as js-res]
|
||||
[status-im.ui.components.permissions :as components.permissions]
|
||||
|
||||
@@ -187,16 +187,6 @@
|
||||
(when platform/ios?
|
||||
(re-frame/dispatch [::mentions/calculate-suggestions mentionable-users]))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:set-input-text
|
||||
(fn [[chat-id text]]
|
||||
;; We enable mentions
|
||||
(swap! mentions-enabled assoc chat-id true)
|
||||
(on-text-change text chat-id)
|
||||
;; We update the key so that we force a refresh of the text input, as those
|
||||
;; are not ratoms
|
||||
(force-text-input-update!)))
|
||||
|
||||
(fx/defn set-input-text
|
||||
"Set input text for current-chat. Takes db and input text and cofx
|
||||
as arguments and returns new fx. Always clear all validation messages."
|
||||
@@ -380,7 +370,7 @@
|
||||
contact-request @(re-frame/subscribe [:chats/sending-contact-request])]
|
||||
[rn/view {:style (styles/toolbar)
|
||||
:on-layout on-chat-toolbar-layout}
|
||||
;;EXTENSIONS and IMAGE buttons
|
||||
;; EXTENSIONS and IMAGE buttons
|
||||
[actions extensions image show-send actions-ref active-panel set-active-panel contact-request]
|
||||
[rn/view {:style (styles/input-container contact-request)}
|
||||
[send-image]
|
||||
@@ -389,14 +379,14 @@
|
||||
:sending-image sending-image
|
||||
:refs refs
|
||||
:set-active-panel set-active-panel}]
|
||||
;;SEND button
|
||||
;; SEND button
|
||||
[rn/view {:ref send-ref :style (when-not show-send {:width 0 :right -100})}
|
||||
(when send
|
||||
[send-button #(do (clear-input chat-id refs)
|
||||
(re-frame/dispatch [:chat.ui/send-current-message]))
|
||||
contact-request])]
|
||||
|
||||
;;STICKERS and AUDIO buttons
|
||||
;; STICKERS and AUDIO buttons
|
||||
(when-not @(re-frame/subscribe [:chats/edit-message])
|
||||
[rn/view {:style (merge {:flex-direction :row} (when show-send {:width 0 :right -100}))
|
||||
:ref sticker-ref}
|
||||
@@ -411,4 +401,4 @@
|
||||
:accessibility-label :show-audio-message-icon
|
||||
:active active-panel
|
||||
:input-focus #(input-focus text-input-ref)
|
||||
:set-active set-active-panel}])])]]]))))
|
||||
:set-active set-active-panel}])])]]]))))
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.ui.components.list-selection :as list-selection]
|
||||
[quo.design-system.colors :as colors]
|
||||
[status-im.utils.debounce :as debounce]
|
||||
[utils.debounce :as debounce]
|
||||
[status-im.utils.platform :as platform])
|
||||
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[quo.core :as quo]
|
||||
[status-im.ui.screens.chat.stickers.styles :as styles]
|
||||
[status-im.utils.debounce :as debounce]
|
||||
[utils.debounce :as debounce]
|
||||
[status-im.ui.components.fast-image :as fast-image]))
|
||||
|
||||
(def icon-size 28)
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
|
||||
(defn pin-indicator-container []
|
||||
{:margin-top 4
|
||||
:margin-left 68
|
||||
:margin-left 54
|
||||
:top 4
|
||||
:justify-content :center
|
||||
:align-self :flex-start
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
[status-im.utils.platform :as platform]
|
||||
[status-im.utils.utils :as utils]
|
||||
[status-im.ui.screens.chat.sheets :as sheets]
|
||||
[status-im.utils.debounce :as debounce]
|
||||
[utils.debounce :as debounce]
|
||||
[status-im2.navigation.state :as navigation.state]
|
||||
[status-im.react-native.resources :as resources]))
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
[status-im.ui.components.accordion :as accordion]
|
||||
[status-im.ui.components.list.views :as list]
|
||||
[status-im.ui.components.react :as react]
|
||||
[quo2.components.navigation.floating-shell-button :as floating-shell-button]
|
||||
[quo.core :as quo]
|
||||
[quo.design-system.colors :as colors]))
|
||||
|
||||
@@ -155,7 +156,7 @@
|
||||
(assoc home-item :public? true)
|
||||
{:on-press (fn []
|
||||
(rf/dispatch [:dismiss-keyboard])
|
||||
(rf/dispatch [:chat.ui/navigate-to-chat chat-id])
|
||||
(rf/dispatch [:chat.ui/navigate-to-chat-nav2 chat-id])
|
||||
(rf/dispatch [:search/home-filter-changed nil])
|
||||
(rf/dispatch [:accept-all-activity-center-notifications-from-chat chat-id]))
|
||||
:on-long-press #(rf/dispatch [:bottom-sheet/show-sheet
|
||||
@@ -244,7 +245,7 @@
|
||||
(i18n/label :t/fetch-community))]]]))
|
||||
|
||||
(defn community []
|
||||
(let [{:keys [community-id from-chat]} (rf/sub [:get-screen-params])]
|
||||
(let [{:keys [community-id from-chat]} (rf/sub [:get-screen-params :community])]
|
||||
(fn []
|
||||
(let [{:keys [id chats name images members permissions color joined
|
||||
can-request-access? can-join? requested-to-join-at admin]
|
||||
@@ -301,5 +302,10 @@
|
||||
{:show-border? true
|
||||
:center [quo/button {:on-press #(rf/dispatch [:communities/join id])
|
||||
:type :secondary}
|
||||
(i18n/label :t/follow)]}]))]
|
||||
(i18n/label :t/follow)]}]))
|
||||
[floating-shell-button/floating-shell-button
|
||||
{:jump-to {:on-press #(rf/dispatch [:shell/navigate-to-jump-to])
|
||||
:label (i18n/label :t/jump-to)}}
|
||||
{:position :absolute
|
||||
:bottom 70}]]
|
||||
[unknown-community community-id])))))
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.screens.communities.membership :as memberships]
|
||||
[status-im.ui.components.icons.icons :as icons]
|
||||
[status-im.utils.debounce :as debounce]))
|
||||
[utils.debounce :as debounce]))
|
||||
|
||||
(def max-name-length 30)
|
||||
(def max-description-length 140)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
[status-im.ui.components.toolbar :as toolbar]
|
||||
[status-im.ui.components.react :as react]
|
||||
[quo.core :as quo]
|
||||
[status-im.utils.debounce :as debounce]
|
||||
[utils.debounce :as debounce]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[clojure.string :as str]
|
||||
[status-im.ui.components.list.views :as list]
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.ui.components.toolbar :as toolbar]
|
||||
[status-im.communities.core :as communities]
|
||||
[status-im.utils.debounce :as debounce]
|
||||
[utils.debounce :as debounce]
|
||||
[status-im.utils.handlers :refer [>evt <sub]]
|
||||
[status-im.ui.screens.communities.create :as create]
|
||||
[status-im.ui.components.keyboard-avoid-presentation :as kb-presentation]
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.ui.components.toolbar :as toolbar]
|
||||
[status-im.communities.core :as communities]
|
||||
[status-im.utils.debounce :as debounce]
|
||||
[utils.debounce :as debounce]
|
||||
[status-im.utils.handlers :refer [<sub]]
|
||||
[status-im.ui.screens.communities.create-channel :as create-channel]))
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
thumbnail-image (get-in images [:thumbnail :uri])]
|
||||
(cond
|
||||
(= id constants/status-community-id)
|
||||
[react/image {:source (resources/get-image :status-logo)
|
||||
[react/image {:source (resources/get-image :status-logo) ;; TODO replace with real data (or remove this code)
|
||||
:style {:width 40
|
||||
:height 40}}]
|
||||
(seq thumbnail-image)
|
||||
@@ -27,7 +27,7 @@
|
||||
thumbnail-image (get-in images [:thumbnail :uri])]
|
||||
(cond
|
||||
(= id constants/status-community-id)
|
||||
[react/image {:source (resources/get-image :status-logo)
|
||||
[react/image {:source (resources/get-image :status-logo) ;; TODO replace with real data
|
||||
:style {:width size
|
||||
:height size}}]
|
||||
(seq thumbnail-image)
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
(>evt [:bottom-sheet/hide])
|
||||
(>evt event))
|
||||
|
||||
(defn member-sheet [first-name {:keys [public-key] :as member} community-id can-kick-users? can-manage-users?]
|
||||
(defn member-sheet [first-name {:keys [public-key] :as member} community-id can-kick-users? can-manage-users? admin?]
|
||||
[:<>
|
||||
[quo/list-item
|
||||
{:theme :accent
|
||||
@@ -26,7 +26,7 @@
|
||||
:subtitle (i18n/label :t/view-profile)
|
||||
:accessibility-label :view-chat-details-button
|
||||
:chevron true
|
||||
:on-press #(hide-sheet-and-dispatch [:chat.ui/show-profile public-key])}]
|
||||
:on-press #(hide-sheet-and-dispatch [:chat.ui/show-profile public-key])}]
|
||||
(when can-kick-users?
|
||||
[:<>
|
||||
[quo/separator {:style {:margin-vertical 8}}]
|
||||
@@ -39,13 +39,20 @@
|
||||
[quo/list-item {:theme :negative
|
||||
:icon :main-icons/cancel
|
||||
:title (i18n/label :t/member-ban)
|
||||
:on-press #(>evt [::communities/member-ban community-id public-key])}]])])
|
||||
:on-press #(>evt [::communities/member-ban community-id public-key])}]])
|
||||
(when admin?
|
||||
[:<>
|
||||
[quo/list-item {:theme :accent
|
||||
:icon :main-icons/make-admin
|
||||
:title (i18n/label :t/make-moderator)
|
||||
:on-press #(>evt [:community.member/add-role community-id public-key constants/community-member-role-moderator])}]])])
|
||||
|
||||
(defn render-member [public-key _ _ {:keys [community-id
|
||||
my-public-key
|
||||
can-manage-users?
|
||||
can-kick-users?]}]
|
||||
(let [member (<sub [:contacts/contact-by-identity public-key])
|
||||
can-kick-users?
|
||||
admin?]}]
|
||||
(let [member (<sub [:contacts/contact-by-identity public-key])
|
||||
[first-name second-name] (<sub [:contacts/contact-two-names-by-identity public-key])]
|
||||
[quo/list-item
|
||||
{:title first-name
|
||||
@@ -58,7 +65,7 @@
|
||||
[quo/button {:on-press
|
||||
#(>evt [:bottom-sheet/show-sheet
|
||||
{:content (fn []
|
||||
[member-sheet first-name member community-id can-kick-users? can-manage-users?])}])
|
||||
[member-sheet first-name member community-id can-kick-users? can-manage-users? admin?])}])
|
||||
:type :icon
|
||||
:theme :icon
|
||||
:accessibility-label :menu-option}
|
||||
@@ -74,16 +81,16 @@
|
||||
[quo/separator {:style {:margin-vertical 8}}]])
|
||||
|
||||
(defn requests-to-join [community-id]
|
||||
(let [requests (<sub [:communities/requests-to-join-for-community community-id])
|
||||
(let [requests (<sub [:communities/requests-to-join-for-community community-id])
|
||||
requests-count (count requests)]
|
||||
[:<>
|
||||
[quo/list-item {:chevron true
|
||||
[quo/list-item {:chevron true
|
||||
:accessory
|
||||
[react/view {:flex-direction :row}
|
||||
(when (pos? requests-count)
|
||||
[unviewed-indicator/unviewed-indicator requests-count])]
|
||||
:on-press #(>evt [:navigate-to :community-requests-to-join {:community-id community-id}])
|
||||
:title (i18n/label :t/membership-requests)}]
|
||||
:on-press #(>evt [:navigate-to :community-requests-to-join {:community-id community-id}])
|
||||
:title (i18n/label :t/membership-requests)}]
|
||||
[quo/separator {:style {:margin-vertical 8}}]]))
|
||||
|
||||
(defn members []
|
||||
@@ -91,7 +98,8 @@
|
||||
(fn []
|
||||
(let [my-public-key (<sub [:multiaccount/public-key])
|
||||
{:keys [permissions
|
||||
can-manage-users?]} (<sub [:communities/community community-id])
|
||||
can-manage-users?
|
||||
admin]} (<sub [:communities/community community-id])
|
||||
sorted-members (<sub [:communities/sorted-community-members
|
||||
community-id])]
|
||||
[:<>
|
||||
@@ -101,18 +109,19 @@
|
||||
(when (and can-manage-users? (= constants/community-on-request-access (:access permissions)))
|
||||
[requests-to-join community-id])
|
||||
[rn/flat-list {:data (keys sorted-members)
|
||||
:render-data {:community-id community-id
|
||||
:my-public-key my-public-key
|
||||
:can-kick-users? (and can-manage-users?
|
||||
(not= (:access permissions)
|
||||
constants/community-no-membership-access))
|
||||
:can-manage-users? can-manage-users?}
|
||||
:render-data {:community-id community-id
|
||||
:my-public-key my-public-key
|
||||
:can-kick-users? (and can-manage-users?
|
||||
(not= (:access permissions)
|
||||
constants/community-no-membership-access))
|
||||
:can-manage-users? can-manage-users?
|
||||
:admin? admin}
|
||||
:key-fn identity
|
||||
:render-fn render-member}]]))))
|
||||
|
||||
(defn members-container []
|
||||
(reagent/create-class
|
||||
{:display-name "community-members-view"
|
||||
{:display-name "community-members-view"
|
||||
:component-did-mount (fn []
|
||||
(communities/fetch-requests-to-join! (get (<sub [:get-screen-params]) :community-id)))
|
||||
:reagent-render members}))
|
||||
:reagent-render members}))
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
[status-im.ui.components.toolbar :as toolbar]
|
||||
[quo.core :as quo]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.utils.debounce :as debounce]
|
||||
[utils.debounce :as debounce]
|
||||
[status-im.ui.components.list.views :as list]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.utils.handlers :refer [>evt <sub]]
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
[status-im.ui.screens.chat.message.message :as message]
|
||||
[status-im.ui.screens.chat.photos :as photos]
|
||||
[status-im.ui.screens.profile.components.views :as profile.components]
|
||||
[status-im.utils.debounce :as debounce]
|
||||
[utils.debounce :as debounce]
|
||||
[clojure.string :as string]
|
||||
[status-im.ethereum.tokens :as tokens]
|
||||
[quo.core :as quo]
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
[status-im.ui.components.topbar :as topbar]
|
||||
[status-im.ui.screens.group.styles :as styles]
|
||||
[quo.core :as quo]
|
||||
[status-im.utils.debounce :as debounce])
|
||||
[utils.debounce :as debounce])
|
||||
(:require-macros [status-im.utils.views :as views]))
|
||||
|
||||
(defn- render-contact [row]
|
||||
|
||||
@@ -12,11 +12,10 @@
|
||||
[status-im.ui.screens.home.views.inner-item :as inner-item]
|
||||
[quo.design-system.colors :as colors]
|
||||
[quo.core :as quo]
|
||||
[quo.platform :as platform]
|
||||
[status-im.add-new.core :as new-chat]
|
||||
[status-im.ui.components.search-input.view :as search-input]
|
||||
[status-im.add-new.db :as db]
|
||||
[status-im.utils.debounce :as debounce]
|
||||
[utils.debounce :as debounce]
|
||||
[status-im.utils.utils :as utils]
|
||||
[status-im.ui.components.topbar :as topbar]
|
||||
[status-im.ui.components.plus-button :as components.plus-button]
|
||||
@@ -131,7 +130,7 @@
|
||||
home-item
|
||||
{:on-press (fn []
|
||||
(re-frame/dispatch [:dismiss-keyboard])
|
||||
(if (and config/new-ui-enabled? platform/android?)
|
||||
(if config/new-ui-enabled?
|
||||
(re-frame/dispatch [:chat.ui/navigate-to-chat-nav2 chat-id])
|
||||
(re-frame/dispatch [:chat.ui/navigate-to-chat chat-id]))
|
||||
(re-frame/dispatch [:search/home-filter-changed nil])
|
||||
@@ -148,7 +147,7 @@
|
||||
home-item
|
||||
{:on-press (fn []
|
||||
(re-frame/dispatch [:dismiss-keyboard])
|
||||
(if (and config/new-ui-enabled? platform/android?)
|
||||
(if config/new-ui-enabled?
|
||||
(re-frame/dispatch [:chat.ui/navigate-to-chat-nav2 chat-id])
|
||||
(re-frame/dispatch [:chat.ui/navigate-to-chat chat-id]))
|
||||
(re-frame/dispatch [:search/home-filter-changed nil])
|
||||
@@ -256,11 +255,11 @@
|
||||
:width 32
|
||||
:style {:margin-left 12}
|
||||
:accessibility-label :notifications-button
|
||||
:on-press #(do
|
||||
(re-frame/dispatch [:mark-all-activity-center-notifications-as-read])
|
||||
(if config/new-activity-center-enabled?
|
||||
(re-frame/dispatch [:activity-center/open])
|
||||
(re-frame/dispatch [:navigate-to :notifications-center])))}
|
||||
:on-press #(do (if config/new-activity-center-enabled?
|
||||
(re-frame/dispatch [:activity-center/open])
|
||||
(do
|
||||
(re-frame/dispatch [:mark-all-activity-center-notifications-as-read])
|
||||
(re-frame/dispatch [:navigate-to :notifications-center]))))}
|
||||
[icons/icon :main-icons/notification2 {:color (quo2.colors/theme-colors quo2.colors/neutral-100 quo2.colors/white)}]]
|
||||
(when (pos? notif-count)
|
||||
[react/view {:style (merge (styles/counter-public-container) {:top 5 :right 5})
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
[status-im.ui.screens.network.styles :as st]
|
||||
[status-im.ui.screens.network.views :as network-settings]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.utils.debounce :refer [dispatch-and-chill]])
|
||||
[utils.debounce :refer [dispatch-and-chill]])
|
||||
(:require-macros [status-im.utils.views :as views]))
|
||||
|
||||
(views/defview network-details []
|
||||
|
||||