forked from Ivasoft/mattermost-mobile
Compare commits
30 Commits
MM-47655-a
...
mm-44652-c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a4b61b9c7 | ||
|
|
2da4f45a31 | ||
|
|
da3f396f5b | ||
|
|
5d94ccf06b | ||
|
|
1b5b1b4591 | ||
|
|
b2cbe8b570 | ||
|
|
3176c2fed5 | ||
|
|
b27c72e965 | ||
|
|
7b1a4d6275 | ||
|
|
701c010bdf | ||
|
|
e5db26ed35 | ||
|
|
9d34726bc3 | ||
|
|
8581765196 | ||
|
|
9f3e35a9bc | ||
|
|
89387bf36b | ||
|
|
f1261535b7 | ||
|
|
dde547a919 | ||
|
|
b46ba9aa5d | ||
|
|
d9b4d18d69 | ||
|
|
cae2b818f3 | ||
|
|
3a56209562 | ||
|
|
eef1e90bf2 | ||
|
|
aa814243b5 | ||
|
|
fc16af723a | ||
|
|
273f098bee | ||
|
|
385105b621 | ||
|
|
bbcbca1675 | ||
|
|
898942b60d | ||
|
|
461bbbbfad | ||
|
|
ae0689a05b |
@@ -1,4 +1,4 @@
|
||||
Submit feature requests to https://mattermost.com/suggestions/. File non-security related bugs here in the following format:
|
||||
Submit feature requests to https://portal.productboard.com/mattermost/33-what-matters-to-you. File non-security related bugs here in the following format:
|
||||
|
||||
#### Summary
|
||||
Issue in one concise sentence.
|
||||
|
||||
@@ -112,7 +112,7 @@ android {
|
||||
applicationId "com.mattermost.rnbeta"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 455
|
||||
versionCode 454
|
||||
versionName "2.0.0"
|
||||
testBuildType System.getProperty('testBuildType', 'debug')
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
@@ -147,7 +147,6 @@ android {
|
||||
release {
|
||||
minifyEnabled enableProguardInReleaseBuilds
|
||||
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
|
||||
proguardFile "${rootProject.projectDir}/../node_modules/detox/android/detox/proguard-rules-app.pro"
|
||||
if (useReleaseKey) {
|
||||
signingConfig signingConfigs.release
|
||||
} else {
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
package com.mattermost.helpers
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.util.LruCache
|
||||
|
||||
class BitmapCache {
|
||||
private var memoryCache: LruCache<String, Bitmap>
|
||||
|
||||
init {
|
||||
val maxMemory = (Runtime.getRuntime().maxMemory() / 1024).toInt()
|
||||
val cacheSize = maxMemory / 8
|
||||
memoryCache = object : LruCache<String, Bitmap>(cacheSize) {
|
||||
override fun sizeOf(key: String, bitmap: Bitmap): Int {
|
||||
return bitmap.byteCount / 1024
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getBitmapFromMemCache(key: String): Bitmap? {
|
||||
return memoryCache.get(key)
|
||||
}
|
||||
|
||||
fun addBitmapToMemoryCache(key: String, bitmap: Bitmap) {
|
||||
if (getBitmapFromMemCache(key) == null) {
|
||||
memoryCache.put(key, bitmap)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
@@ -27,6 +28,7 @@ import androidx.core.app.Person;
|
||||
import androidx.core.app.RemoteInput;
|
||||
import androidx.core.graphics.drawable.IconCompat;
|
||||
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.mattermost.rnbeta.*;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -51,11 +53,7 @@ public class CustomPushNotificationHelper {
|
||||
private static NotificationChannel mHighImportanceChannel;
|
||||
private static NotificationChannel mMinImportanceChannel;
|
||||
|
||||
private static final OkHttpClient client = new OkHttpClient();
|
||||
|
||||
private static final BitmapCache bitmapCache = new BitmapCache();
|
||||
|
||||
private static void addMessagingStyleMessages(NotificationCompat.MessagingStyle messagingStyle, String conversationTitle, Bundle bundle) {
|
||||
private static void addMessagingStyleMessages(Context context, NotificationCompat.MessagingStyle messagingStyle, String conversationTitle, Bundle bundle) {
|
||||
String message = bundle.getString("message", bundle.getString("body"));
|
||||
String senderId = bundle.getString("sender_id");
|
||||
String serverUrl = bundle.getString("server_url");
|
||||
@@ -77,7 +75,7 @@ public class CustomPushNotificationHelper {
|
||||
|
||||
if (serverUrl != null && !type.equals(CustomPushNotificationHelper.PUSH_TYPE_SESSION)) {
|
||||
try {
|
||||
Bitmap avatar = userAvatar(serverUrl, senderId, urlOverride);
|
||||
Bitmap avatar = userAvatar(context, serverUrl, senderId, urlOverride);
|
||||
if (avatar != null) {
|
||||
sender.setIcon(IconCompat.createWithBitmap(avatar));
|
||||
}
|
||||
@@ -179,12 +177,12 @@ public class CustomPushNotificationHelper {
|
||||
String groupId = is_crt_enabled && !android.text.TextUtils.isEmpty(rootId) ? rootId : channelId;
|
||||
|
||||
addNotificationExtras(notification, bundle);
|
||||
setNotificationIcons(notification, bundle);
|
||||
setNotificationMessagingStyle(notification, bundle);
|
||||
setNotificationIcons(context, notification, bundle);
|
||||
setNotificationMessagingStyle(context, notification, bundle);
|
||||
setNotificationGroup(notification, groupId, createSummary);
|
||||
setNotificationBadgeType(notification);
|
||||
|
||||
setNotificationChannel(context, notification);
|
||||
setNotificationChannel(context, notification, bundle);
|
||||
setNotificationDeleteIntent(context, notification, bundle, notificationId);
|
||||
addNotificationReplyAction(context, notification, bundle, notificationId);
|
||||
|
||||
@@ -256,7 +254,15 @@ public class CustomPushNotificationHelper {
|
||||
return title;
|
||||
}
|
||||
|
||||
private static NotificationCompat.MessagingStyle getMessagingStyle(Bundle bundle) {
|
||||
private static int getIconResourceId(Context context, String iconName) {
|
||||
final Resources res = context.getResources();
|
||||
String packageName = context.getPackageName();
|
||||
String defType = "mipmap";
|
||||
|
||||
return res.getIdentifier(iconName, defType, packageName);
|
||||
}
|
||||
|
||||
private static NotificationCompat.MessagingStyle getMessagingStyle(Context context, Bundle bundle) {
|
||||
NotificationCompat.MessagingStyle messagingStyle;
|
||||
final String senderId = "me";
|
||||
final String serverUrl = bundle.getString("server_url");
|
||||
@@ -269,7 +275,7 @@ public class CustomPushNotificationHelper {
|
||||
|
||||
if (serverUrl != null && !type.equals(CustomPushNotificationHelper.PUSH_TYPE_SESSION)) {
|
||||
try {
|
||||
Bitmap avatar = userAvatar(serverUrl, "me", urlOverride);
|
||||
Bitmap avatar = userAvatar(context, serverUrl, "me", urlOverride);
|
||||
if (avatar != null) {
|
||||
sender.setIcon(IconCompat.createWithBitmap(avatar));
|
||||
}
|
||||
@@ -282,7 +288,7 @@ public class CustomPushNotificationHelper {
|
||||
|
||||
String conversationTitle = getConversationTitle(bundle);
|
||||
setMessagingStyleConversationTitle(messagingStyle, conversationTitle, bundle);
|
||||
addMessagingStyleMessages(messagingStyle, conversationTitle, bundle);
|
||||
addMessagingStyleMessages(context, messagingStyle, conversationTitle, bundle);
|
||||
|
||||
return messagingStyle;
|
||||
}
|
||||
@@ -309,6 +315,25 @@ public class CustomPushNotificationHelper {
|
||||
return getConversationTitle(bundle);
|
||||
}
|
||||
|
||||
public static int getSmallIconResourceId(Context context, String iconName) {
|
||||
if (iconName == null) {
|
||||
iconName = "ic_notification";
|
||||
}
|
||||
|
||||
int resourceId = getIconResourceId(context, iconName);
|
||||
|
||||
if (resourceId == 0) {
|
||||
iconName = "ic_launcher";
|
||||
resourceId = getIconResourceId(context, iconName);
|
||||
|
||||
if (resourceId == 0) {
|
||||
resourceId = android.R.drawable.ic_dialog_info;
|
||||
}
|
||||
}
|
||||
|
||||
return resourceId;
|
||||
}
|
||||
|
||||
private static String removeSenderNameFromMessage(String message, String senderName) {
|
||||
int index = message.indexOf(senderName);
|
||||
if (index == 0) {
|
||||
@@ -340,7 +365,7 @@ public class CustomPushNotificationHelper {
|
||||
}
|
||||
}
|
||||
|
||||
private static void setNotificationChannel(Context context, NotificationCompat.Builder notification) {
|
||||
private static void setNotificationChannel(Context context, NotificationCompat.Builder notification, Bundle bundle) {
|
||||
// If Android Oreo or above we need to register a channel
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
return;
|
||||
@@ -364,8 +389,8 @@ public class CustomPushNotificationHelper {
|
||||
notification.setDeleteIntent(deleteIntent);
|
||||
}
|
||||
|
||||
private static void setNotificationMessagingStyle(NotificationCompat.Builder notification, Bundle bundle) {
|
||||
NotificationCompat.MessagingStyle messagingStyle = getMessagingStyle(bundle);
|
||||
private static void setNotificationMessagingStyle(Context context, NotificationCompat.Builder notification, Bundle bundle) {
|
||||
NotificationCompat.MessagingStyle messagingStyle = getMessagingStyle(context, bundle);
|
||||
notification.setStyle(messagingStyle);
|
||||
}
|
||||
|
||||
@@ -378,18 +403,20 @@ public class CustomPushNotificationHelper {
|
||||
}
|
||||
}
|
||||
|
||||
private static void setNotificationIcons(NotificationCompat.Builder notification, Bundle bundle) {
|
||||
private static void setNotificationIcons(Context context, NotificationCompat.Builder notification, Bundle bundle) {
|
||||
String smallIcon = bundle.getString("smallIcon");
|
||||
String channelName = getConversationTitle(bundle);
|
||||
String senderName = bundle.getString("sender_name");
|
||||
String serverUrl = bundle.getString("server_url");
|
||||
String urlOverride = bundle.getString("override_icon_url");
|
||||
|
||||
notification.setSmallIcon(R.mipmap.ic_notification);
|
||||
int smallIconResId = getSmallIconResourceId(context, smallIcon);
|
||||
notification.setSmallIcon(smallIconResId);
|
||||
|
||||
if (serverUrl != null && channelName.equals(senderName)) {
|
||||
try {
|
||||
String senderId = bundle.getString("sender_id");
|
||||
Bitmap avatar = userAvatar(serverUrl, senderId, urlOverride);
|
||||
Bitmap avatar = userAvatar(context, serverUrl, senderId, urlOverride);
|
||||
if (avatar != null) {
|
||||
notification.setLargeIcon(avatar);
|
||||
}
|
||||
@@ -399,31 +426,29 @@ public class CustomPushNotificationHelper {
|
||||
}
|
||||
}
|
||||
|
||||
private static Bitmap userAvatar(final String serverUrl, final String userId, final String urlOverride) throws IOException {
|
||||
private static Bitmap userAvatar(Context context, final String serverUrl, final String userId, final String urlOverride) throws IOException {
|
||||
try {
|
||||
Response response;
|
||||
final OkHttpClient client = new OkHttpClient();
|
||||
Request request;
|
||||
String url;
|
||||
if (!TextUtils.isEmpty(urlOverride)) {
|
||||
Request request = new Request.Builder().url(urlOverride).build();
|
||||
request = new Request.Builder().url(urlOverride).build();
|
||||
Log.i("ReactNative", String.format("Fetch override profile image %s", urlOverride));
|
||||
response = client.newCall(request).execute();
|
||||
} else {
|
||||
Bitmap cached = bitmapCache.getBitmapFromMemCache(userId);
|
||||
if (cached != null) {
|
||||
Bitmap bitmap = cached.copy(cached.getConfig(), false);
|
||||
return getCircleBitmap(bitmap);
|
||||
}
|
||||
String url = String.format("api/v4/users/%s/image", userId);
|
||||
final ReactApplicationContext reactApplicationContext = new ReactApplicationContext(context);
|
||||
final String token = Credentials.getCredentialsForServerSync(reactApplicationContext, serverUrl);
|
||||
url = String.format("%s/api/v4/users/%s/image", serverUrl, userId);
|
||||
Log.i("ReactNative", String.format("Fetch profile image %s", url));
|
||||
response = Network.getSync(serverUrl, url, null);
|
||||
request = new Request.Builder()
|
||||
.header("Authorization", String.format("Bearer %s", token))
|
||||
.url(url)
|
||||
.build();
|
||||
}
|
||||
|
||||
Response response = client.newCall(request).execute();
|
||||
if (response.code() == 200) {
|
||||
assert response.body() != null;
|
||||
byte[] bytes = Objects.requireNonNull(response.body()).bytes();
|
||||
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
|
||||
if (TextUtils.isEmpty(urlOverride) && !TextUtils.isEmpty(userId)) {
|
||||
bitmapCache.addBitmapToMemoryCache(userId, bitmap.copy(bitmap.getConfig(), false));
|
||||
}
|
||||
return getCircleBitmap(bitmap);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ import com.mattermost.networkclient.APIClientModule;
|
||||
import com.mattermost.networkclient.enums.RetryTypes;
|
||||
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.Response;
|
||||
|
||||
|
||||
public class Network {
|
||||
@@ -36,16 +35,6 @@ public class Network {
|
||||
clientModule.post(baseUrl, endpoint, options, promise);
|
||||
}
|
||||
|
||||
public static Response getSync(String baseUrl, String endpoint, ReadableMap options) {
|
||||
createClientIfNeeded(baseUrl);
|
||||
return clientModule.getSync(baseUrl, endpoint, options);
|
||||
}
|
||||
|
||||
public static Response postSync(String baseUrl, String endpoint, ReadableMap options) {
|
||||
createClientIfNeeded(baseUrl);
|
||||
return clientModule.postSync(baseUrl, endpoint, options);
|
||||
}
|
||||
|
||||
private static void createClientOptions() {
|
||||
WritableMap headers = Arguments.createMap();
|
||||
headers.putString("X-Requested-With", "XMLHttpRequest");
|
||||
|
||||
@@ -7,6 +7,7 @@ import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
|
||||
import java.util.Objects;
|
||||
@@ -16,6 +17,7 @@ import com.mattermost.helpers.DatabaseHelper;
|
||||
import com.mattermost.helpers.Network;
|
||||
import com.mattermost.helpers.NotificationHelper;
|
||||
import com.mattermost.helpers.PushNotificationDataHelper;
|
||||
import com.mattermost.helpers.ResolvePromise;
|
||||
import com.mattermost.share.ShareModule;
|
||||
import com.wix.reactnativenotifications.core.NotificationIntentAdapter;
|
||||
import com.wix.reactnativenotifications.core.notification.PushNotification;
|
||||
@@ -54,31 +56,27 @@ public class CustomPushNotification extends PushNotification {
|
||||
boolean isReactInit = mAppLifecycleFacade.isReactInitialized();
|
||||
|
||||
if (ackId != null && serverUrl != null) {
|
||||
Bundle response = ReceiptDelivery.send(ackId, serverUrl, postId, type, isIdLoaded);
|
||||
if (isIdLoaded && response != null) {
|
||||
Bundle current = mNotificationProps.asBundle();
|
||||
if (!current.containsKey("server_url")) {
|
||||
response.putString("server_url", serverUrl);
|
||||
notificationReceiptDelivery(ackId, serverUrl, postId, type, isIdLoaded, new ResolvePromise() {
|
||||
@Override
|
||||
public void resolve(@Nullable Object value) {
|
||||
if (isIdLoaded) {
|
||||
Bundle response = (Bundle) value;
|
||||
if (value != null) {
|
||||
response.putString("server_url", serverUrl);
|
||||
Bundle current = mNotificationProps.asBundle();
|
||||
current.putAll(response);
|
||||
mNotificationProps = createProps(current);
|
||||
}
|
||||
}
|
||||
}
|
||||
current.putAll(response);
|
||||
mNotificationProps = createProps(current);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reject(String code, String message) {
|
||||
Log.e("ReactNative", code + ": " + message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
finishProcessingNotification(serverUrl, type, channelId, notificationId, isReactInit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpened() {
|
||||
if (mNotificationProps != null) {
|
||||
digestNotification();
|
||||
|
||||
Bundle data = mNotificationProps.asBundle();
|
||||
NotificationHelper.clearChannelOrThreadNotifications(mContext, data);
|
||||
}
|
||||
}
|
||||
|
||||
private void finishProcessingNotification(String serverUrl, String type, String channelId, int notificationId, Boolean isReactInit) {
|
||||
switch (type) {
|
||||
case CustomPushNotificationHelper.PUSH_TYPE_MESSAGE:
|
||||
case CustomPushNotificationHelper.PUSH_TYPE_SESSION:
|
||||
@@ -119,6 +117,16 @@ public class CustomPushNotification extends PushNotification {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpened() {
|
||||
if (mNotificationProps != null) {
|
||||
digestNotification();
|
||||
|
||||
Bundle data = mNotificationProps.asBundle();
|
||||
NotificationHelper.clearChannelOrThreadNotifications(mContext, data);
|
||||
}
|
||||
}
|
||||
|
||||
private void buildNotification(Integer notificationId, boolean createSummary) {
|
||||
final PendingIntent pendingIntent = NotificationIntentAdapter.createPendingNotificationIntent(mContext, mNotificationProps);
|
||||
final Notification notification = buildNotification(pendingIntent);
|
||||
@@ -140,6 +148,10 @@ public class CustomPushNotification extends PushNotification {
|
||||
return CustomPushNotificationHelper.createNotificationBuilder(mContext, intent, bundle, true);
|
||||
}
|
||||
|
||||
private void notificationReceiptDelivery(String ackId, String serverUrl, String postId, String type, boolean isIdLoaded, ResolvePromise promise) {
|
||||
ReceiptDelivery.send(mContext, ackId, serverUrl, postId, type, isIdLoaded, promise);
|
||||
}
|
||||
|
||||
private void notifyReceivedToJS() {
|
||||
mJsIOHelper.sendEventToJS(NOTIFICATION_RECEIVED_EVENT_NAME, mNotificationProps.asBundle(), mAppLifecycleFacade.getRunningReactContext());
|
||||
}
|
||||
|
||||
@@ -8,16 +8,28 @@ import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.Person;
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import android.util.Log;
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
import okhttp3.Call;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONException;
|
||||
|
||||
import com.mattermost.helpers.*;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
|
||||
import com.wix.reactnativenotifications.core.NotificationIntentAdapter;
|
||||
import com.wix.reactnativenotifications.core.notification.PushNotificationProps;
|
||||
|
||||
@@ -41,7 +53,12 @@ public class NotificationReplyBroadcastReceiver extends BroadcastReceiver {
|
||||
final int notificationId = intent.getIntExtra(CustomPushNotificationHelper.NOTIFICATION_ID, -1);
|
||||
final String serverUrl = bundle.getString("server_url");
|
||||
if (serverUrl != null) {
|
||||
replyToMessage(serverUrl, notificationId, message);
|
||||
final ReactApplicationContext reactApplicationContext = new ReactApplicationContext(context);
|
||||
final String token = Credentials.getCredentialsForServerSync(reactApplicationContext, serverUrl);
|
||||
|
||||
if (token != null) {
|
||||
replyToMessage(serverUrl, token, notificationId, message);
|
||||
}
|
||||
} else {
|
||||
onReplyFailed(notificationId);
|
||||
}
|
||||
@@ -50,7 +67,7 @@ public class NotificationReplyBroadcastReceiver extends BroadcastReceiver {
|
||||
}
|
||||
}
|
||||
|
||||
protected void replyToMessage(final String serverUrl, final int notificationId, final CharSequence message) {
|
||||
protected void replyToMessage(final String serverUrl, final String token, final int notificationId, final CharSequence message) {
|
||||
final String channelId = bundle.getString("channel_id");
|
||||
final String postId = bundle.getString("post_id");
|
||||
String rootId = bundle.getString("root_id");
|
||||
@@ -58,53 +75,63 @@ public class NotificationReplyBroadcastReceiver extends BroadcastReceiver {
|
||||
rootId = postId;
|
||||
}
|
||||
|
||||
if (serverUrl == null) {
|
||||
if (token == null || serverUrl == null) {
|
||||
onReplyFailed(notificationId);
|
||||
return;
|
||||
}
|
||||
|
||||
WritableMap headers = Arguments.createMap();
|
||||
headers.putString("Content-Type", "application/json");
|
||||
|
||||
|
||||
WritableMap body = Arguments.createMap();
|
||||
body.putString("channel_id", channelId);
|
||||
body.putString("message", message.toString());
|
||||
body.putString("root_id", rootId);
|
||||
|
||||
WritableMap options = Arguments.createMap();
|
||||
options.putMap("headers", headers);
|
||||
options.putMap("body", body);
|
||||
final OkHttpClient client = new OkHttpClient();
|
||||
final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
|
||||
String json = buildReplyPost(channelId, rootId, message.toString());
|
||||
RequestBody body = RequestBody.create(json, JSON);
|
||||
|
||||
String postsEndpoint = "/api/v4/posts?set_online=false";
|
||||
Network.post(serverUrl, postsEndpoint, options, new ResolvePromise() {
|
||||
String url = String.format("%s%s", serverUrl.replaceAll("/$", ""), postsEndpoint);
|
||||
Log.i("ReactNative", String.format("Reply URL=%s", url));
|
||||
Request request = new Request.Builder()
|
||||
.header("Authorization", String.format("Bearer %s", token))
|
||||
.header("Content-Type", "application/json")
|
||||
.url(url)
|
||||
.post(body)
|
||||
.build();
|
||||
|
||||
client.newCall(request).enqueue(new okhttp3.Callback() {
|
||||
@Override
|
||||
public void resolve(@Nullable Object value) {
|
||||
if (value != null) {
|
||||
public void onFailure(@NonNull Call call, @NonNull IOException e) {
|
||||
Log.i("ReactNative", String.format("Reply FAILED exception %s", e.getMessage()));
|
||||
onReplyFailed(notificationId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(@NonNull Call call, @NonNull final Response response) throws IOException {
|
||||
if (response.isSuccessful()) {
|
||||
onReplySuccess(notificationId, message);
|
||||
Log.i("ReactNative", "Reply SUCCESS");
|
||||
} else {
|
||||
Log.i("ReactNative", "Reply FAILED resolved without value");
|
||||
Log.i("ReactNative",
|
||||
String.format("Reply FAILED status %s BODY %s",
|
||||
response.code(),
|
||||
Objects.requireNonNull(response.body()).string()
|
||||
)
|
||||
);
|
||||
onReplyFailed(notificationId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reject(Throwable reason) {
|
||||
Log.i("ReactNative", String.format("Reply FAILED exception %s", reason.getMessage()));
|
||||
onReplyFailed(notificationId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reject(String code, String message) {
|
||||
Log.i("ReactNative",
|
||||
String.format("Reply FAILED status %s BODY %s", code, message)
|
||||
);
|
||||
onReplyFailed(notificationId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected String buildReplyPost(String channelId, String rootId, String message) {
|
||||
try {
|
||||
JSONObject json = new JSONObject();
|
||||
json.put("channel_id", channelId);
|
||||
json.put("message", message);
|
||||
json.put("root_id", rootId);
|
||||
return json.toString();
|
||||
} catch(JSONException e) {
|
||||
return "{}";
|
||||
}
|
||||
}
|
||||
|
||||
protected void onReplyFailed(int notificationId) {
|
||||
recreateNotification(notificationId, "Message failed to send.");
|
||||
}
|
||||
|
||||
@@ -1,60 +1,135 @@
|
||||
package com.mattermost.rnbeta;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import java.lang.System;
|
||||
import java.util.Objects;
|
||||
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.HttpUrl;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONException;
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.mattermost.helpers.*;
|
||||
|
||||
import okhttp3.Response;
|
||||
|
||||
public class ReceiptDelivery {
|
||||
private static final String[] ackKeys = new String[]{"post_id", "root_id", "category", "message", "team_id", "channel_id", "channel_name", "type", "sender_id", "sender_name", "version"};
|
||||
private static final int[] FIBONACCI_BACKOFF = new int[] { 0, 1, 2, 3, 5, 8 };
|
||||
|
||||
public static Bundle send(final String ackId, final String serverUrl, final String postId, final String type, final boolean isIdLoaded) {
|
||||
public static void send(Context context, final String ackId, final String serverUrl, final String postId, final String type, final boolean isIdLoaded, ResolvePromise promise) {
|
||||
final ReactApplicationContext reactApplicationContext = new ReactApplicationContext(context);
|
||||
final String token = Credentials.getCredentialsForServerSync(reactApplicationContext, serverUrl);
|
||||
Log.i("ReactNative", String.format("Send receipt delivery ACK=%s TYPE=%s to URL=%s with ID-LOADED=%s", ackId, type, serverUrl, isIdLoaded));
|
||||
WritableMap options = Arguments.createMap();
|
||||
WritableMap headers = Arguments.createMap();
|
||||
WritableMap body = Arguments.createMap();
|
||||
headers.putString("Content-Type", "application/json");
|
||||
options.putMap("headers", headers);
|
||||
body.putString("id", ackId);
|
||||
body.putDouble("received_at", System.currentTimeMillis());
|
||||
body.putString("platform", "android");
|
||||
body.putString("type", type);
|
||||
body.putString("post_id", postId);
|
||||
body.putBoolean("is_id_loaded", isIdLoaded);
|
||||
options.putMap("body", body);
|
||||
execute(serverUrl, postId, token, ackId, type, isIdLoaded, promise);
|
||||
}
|
||||
|
||||
try (Response response = Network.postSync(serverUrl, "api/v4/notifications/ack", options)) {
|
||||
String responseBody = Objects.requireNonNull(response.body()).string();
|
||||
JSONObject jsonResponse = new JSONObject(responseBody);
|
||||
return parseAckResponse(jsonResponse);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
protected static void execute(String serverUrl, String postId, String token, String ackId, String type, boolean isIdLoaded, ResolvePromise promise) {
|
||||
if (token == null) {
|
||||
promise.reject("Receipt delivery failure", "Invalid token");
|
||||
return;
|
||||
}
|
||||
|
||||
if (serverUrl == null) {
|
||||
promise.reject("Receipt delivery failure", "Invalid server URL");
|
||||
}
|
||||
|
||||
JSONObject json;
|
||||
long receivedAt = System.currentTimeMillis();
|
||||
|
||||
try {
|
||||
json = new JSONObject();
|
||||
json.put("id", ackId);
|
||||
json.put("received_at", receivedAt);
|
||||
json.put("platform", "android");
|
||||
json.put("type", type);
|
||||
json.put("post_id", postId);
|
||||
json.put("is_id_loaded", isIdLoaded);
|
||||
} catch (JSONException e) {
|
||||
Log.e("ReactNative", "Receipt delivery failed to build json payload");
|
||||
promise.reject("Receipt delivery failure", e.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
final HttpUrl url;
|
||||
if (serverUrl != null) {
|
||||
url = HttpUrl.parse(
|
||||
String.format("%s/api/v4/notifications/ack", serverUrl.replaceAll("/$", "")));
|
||||
if (url != null) {
|
||||
final OkHttpClient client = new OkHttpClient();
|
||||
final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
|
||||
RequestBody body = RequestBody.create(json.toString(), JSON);
|
||||
Request request = new Request.Builder()
|
||||
.header("Authorization", String.format("Bearer %s", token))
|
||||
.header("Content-Type", "application/json")
|
||||
.url(url)
|
||||
.post(body)
|
||||
.build();
|
||||
|
||||
makeServerRequest(client, request, isIdLoaded, 0, promise);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Bundle parseAckResponse(JSONObject jsonResponse) {
|
||||
private static void makeServerRequest(OkHttpClient client, Request request, Boolean isIdLoaded, int reRequestCount, ResolvePromise promise) {
|
||||
try {
|
||||
Response response = client.newCall(request).execute();
|
||||
String responseBody = Objects.requireNonNull(response.body()).string();
|
||||
if (response.code() != 200) {
|
||||
switch (response.code()) {
|
||||
case 302:
|
||||
promise.reject("Receipt delivery failure", "StatusFound");
|
||||
return;
|
||||
case 400:
|
||||
promise.reject("Receipt delivery failure", "StatusBadRequest");
|
||||
return;
|
||||
case 401:
|
||||
promise.reject("Receipt delivery failure", "Unauthorized");
|
||||
case 403:
|
||||
promise.reject("Receipt delivery failure", "Forbidden");
|
||||
return;
|
||||
case 500:
|
||||
promise.reject("Receipt delivery failure", "StatusInternalServerError");
|
||||
return;
|
||||
case 501:
|
||||
promise.reject("Receipt delivery failure", "StatusNotImplemented");
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Exception(responseBody);
|
||||
}
|
||||
|
||||
JSONObject jsonResponse = new JSONObject(responseBody);
|
||||
Bundle bundle = new Bundle();
|
||||
for (String key : ackKeys) {
|
||||
String[] keys = new String[]{"post_id", "root_id", "category", "message", "team_id", "channel_id", "channel_name", "type", "sender_id", "sender_name", "version"};
|
||||
for (String key : keys) {
|
||||
if (jsonResponse.has(key)) {
|
||||
bundle.putString(key, jsonResponse.getString(key));
|
||||
}
|
||||
}
|
||||
return bundle;
|
||||
promise.resolve(bundle);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
Log.e("ReactNative", "Receipt delivery failed to send");
|
||||
if (isIdLoaded) {
|
||||
try {
|
||||
reRequestCount++;
|
||||
if (reRequestCount < FIBONACCI_BACKOFF.length) {
|
||||
Log.i("ReactNative", "Retry attempt " + reRequestCount + " with backoff delay: " + FIBONACCI_BACKOFF[reRequestCount] + " seconds");
|
||||
Thread.sleep(FIBONACCI_BACKOFF[reRequestCount] * 1000);
|
||||
makeServerRequest(client, request, true, reRequestCount, promise);
|
||||
}
|
||||
} catch(InterruptedException ie) {
|
||||
ie.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
promise.reject("Receipt delivery failure", e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import {queryMyTeams} from '@queries/servers/team';
|
||||
import {isDMorGM} from '@utils/channel';
|
||||
import {logError} from '@utils/log';
|
||||
|
||||
import type {Model} from '@nozbe/watermelondb';
|
||||
import type ChannelModel from '@typings/database/models/servers/channel';
|
||||
|
||||
export const deleteCategory = async (serverUrl: string, categoryId: string) => {
|
||||
@@ -39,7 +40,7 @@ export async function storeCategories(serverUrl: string, categories: CategoryWit
|
||||
}
|
||||
|
||||
if (models.length > 0) {
|
||||
await operator.batchRecords(models, 'storeCategories');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
|
||||
return {models};
|
||||
@@ -79,6 +80,7 @@ export async function addChannelToDefaultCategory(serverUrl: string, channel: Ch
|
||||
return {error: 'no current user id'};
|
||||
}
|
||||
|
||||
const models: Model[] = [];
|
||||
const categoriesWithChannels: CategoryWithChannels[] = [];
|
||||
|
||||
if (isDMorGM(channel)) {
|
||||
@@ -98,12 +100,13 @@ export async function addChannelToDefaultCategory(serverUrl: string, channel: Ch
|
||||
cwc.channel_ids.unshift(channel.id);
|
||||
categoriesWithChannels.push(cwc);
|
||||
}
|
||||
|
||||
const ccModels = await prepareCategoryChannels(operator, categoriesWithChannels);
|
||||
models.push(...ccModels);
|
||||
}
|
||||
|
||||
const models = await prepareCategoryChannels(operator, categoriesWithChannels);
|
||||
|
||||
if (models.length && !prepareRecordsOnly) {
|
||||
await operator.batchRecords(models, 'addChannelToDefaultCategory');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
|
||||
return {models};
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
prepareDeleteChannel, prepareMyChannelsForTeam, queryAllMyChannel,
|
||||
getMyChannel, getChannelById, queryUsersOnChannel, queryUserChannelsByTypes,
|
||||
} from '@queries/servers/channel';
|
||||
import {queryDisplayNamePreferences} from '@queries/servers/preference';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
import {prepareCommonSystemValues, PrepareCommonSystemValuesArgs, getCommonSystemValues, getCurrentTeamId, setCurrentChannelId, getCurrentUserId, getConfig, getLicense} from '@queries/servers/system';
|
||||
import {addChannelToTeamHistory, addTeamToTeamHistory, getTeamById, removeChannelFromTeamHistory} from '@queries/servers/team';
|
||||
import {getCurrentUser, queryUsersById} from '@queries/servers/user';
|
||||
@@ -82,7 +82,7 @@ export async function switchToChannel(serverUrl: string, channelId: string, team
|
||||
}
|
||||
|
||||
if (models.length && !prepareRecordsOnly) {
|
||||
await operator.batchRecords(models, 'switchToChannel');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
|
||||
if (isTabletDevice) {
|
||||
@@ -124,7 +124,7 @@ export async function removeCurrentUserFromChannel(serverUrl: string, channelId:
|
||||
await removeChannelFromTeamHistory(operator, teamId, channel.id, false);
|
||||
|
||||
if (models.length && !prepareRecordsOnly) {
|
||||
await operator.batchRecords(models, 'removeCurrentUserFromChannel');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
}
|
||||
return {models};
|
||||
@@ -145,7 +145,7 @@ export async function setChannelDeleteAt(serverUrl: string, channelId: string, d
|
||||
const model = channel.prepareUpdate((c) => {
|
||||
c.deleteAt = deleteAt;
|
||||
});
|
||||
await operator.batchRecords([model], 'setChannelDeleteAt');
|
||||
await operator.batchRecords([model]);
|
||||
} catch (error) {
|
||||
logError('FAILED TO BATCH CHANGES FOR CHANNEL DELETE AT', error);
|
||||
}
|
||||
@@ -179,7 +179,7 @@ export async function markChannelAsViewed(serverUrl: string, channelId: string,
|
||||
});
|
||||
PushNotifications.removeChannelNotifications(serverUrl, channelId);
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords([member], 'markChannelAsViewed');
|
||||
await operator.batchRecords([member]);
|
||||
}
|
||||
|
||||
return {member};
|
||||
@@ -206,7 +206,7 @@ export async function markChannelAsUnread(serverUrl: string, channelId: string,
|
||||
m.isUnread = true;
|
||||
});
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords([member], 'markChannelAsUnread');
|
||||
await operator.batchRecords([member]);
|
||||
}
|
||||
|
||||
return {member};
|
||||
@@ -226,7 +226,7 @@ export async function resetMessageCount(serverUrl: string, channelId: string) {
|
||||
member.prepareUpdate((m) => {
|
||||
m.messageCount = 0;
|
||||
});
|
||||
await operator.batchRecords([member], 'resetMessageCount');
|
||||
await operator.batchRecords([member]);
|
||||
|
||||
return member;
|
||||
} catch (error) {
|
||||
@@ -254,7 +254,7 @@ export async function storeMyChannelsForTeam(serverUrl: string, teamId: string,
|
||||
}
|
||||
|
||||
if (flattenedModels.length) {
|
||||
await operator.batchRecords(flattenedModels, 'storeMyChannelsForTeam');
|
||||
await operator.batchRecords(flattenedModels);
|
||||
}
|
||||
|
||||
return {models: flattenedModels};
|
||||
@@ -273,7 +273,7 @@ export async function updateMyChannelFromWebsocket(serverUrl: string, channelMem
|
||||
m.roles = channelMember.roles;
|
||||
});
|
||||
if (!prepareRecordsOnly) {
|
||||
operator.batchRecords([member], 'updateMyChannelFromWebsocket');
|
||||
operator.batchRecords([member]);
|
||||
}
|
||||
}
|
||||
return {model: member};
|
||||
@@ -293,7 +293,7 @@ export async function updateChannelInfoFromChannel(serverUrl: string, channel: C
|
||||
}],
|
||||
prepareRecordsOnly: true});
|
||||
if (!prepareRecordsOnly) {
|
||||
operator.batchRecords(newInfo, 'updateChannelInfoFromChannel');
|
||||
operator.batchRecords(newInfo);
|
||||
}
|
||||
return {model: newInfo};
|
||||
} catch (error) {
|
||||
@@ -317,7 +317,7 @@ export async function updateLastPostAt(serverUrl: string, channelId: string, las
|
||||
});
|
||||
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords([myChannel], 'updateLastPostAt');
|
||||
await operator.batchRecords([myChannel]);
|
||||
}
|
||||
|
||||
return {member: myChannel};
|
||||
@@ -345,7 +345,7 @@ export async function updateMyChannelLastFetchedAt(serverUrl: string, channelId:
|
||||
});
|
||||
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords([myChannel], 'updateMyChannelLastFetchedAt');
|
||||
await operator.batchRecords([myChannel]);
|
||||
}
|
||||
|
||||
return {member: myChannel};
|
||||
@@ -369,7 +369,7 @@ export async function updateChannelsDisplayName(serverUrl: string, channels: Cha
|
||||
|
||||
const license = await getLicense(database);
|
||||
const config = await getConfig(database);
|
||||
const preferences = await queryDisplayNamePreferences(database, Preferences.NAME_NAME_FORMAT).fetch();
|
||||
const preferences = await queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.NAME_NAME_FORMAT).fetch();
|
||||
const displaySettings = getTeammateNameDisplaySetting(preferences, config.LockTeammateNameDisplay, config.TeammateNameDisplay, license);
|
||||
const models: Model[] = [];
|
||||
for await (const channel of channels) {
|
||||
@@ -403,7 +403,7 @@ export async function updateChannelsDisplayName(serverUrl: string, channels: Cha
|
||||
}
|
||||
|
||||
if (models.length && !prepareRecordsOnly) {
|
||||
await operator.batchRecords(models, 'updateChannelsDisplayName');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
|
||||
return {models};
|
||||
|
||||
@@ -26,7 +26,7 @@ export async function updateDraftFile(serverUrl: string, channelId: string, root
|
||||
});
|
||||
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords([draft], 'updateDraftFile');
|
||||
await operator.batchRecords([draft]);
|
||||
}
|
||||
|
||||
return {draft};
|
||||
@@ -58,7 +58,7 @@ export async function removeDraftFile(serverUrl: string, channelId: string, root
|
||||
}
|
||||
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords([draft], 'removeDraftFile');
|
||||
await operator.batchRecords([draft]);
|
||||
}
|
||||
|
||||
return {draft};
|
||||
@@ -99,7 +99,7 @@ export async function updateDraftMessage(serverUrl: string, channelId: string, r
|
||||
}
|
||||
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords([draft], 'updateDraftMessage');
|
||||
await operator.batchRecords([draft]);
|
||||
}
|
||||
|
||||
return {draft};
|
||||
@@ -129,7 +129,7 @@ export async function addFilesToDraft(serverUrl: string, channelId: string, root
|
||||
});
|
||||
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords([draft], 'addFilesToDraft');
|
||||
await operator.batchRecords([draft]);
|
||||
}
|
||||
|
||||
return {draft};
|
||||
|
||||
@@ -127,14 +127,14 @@ export async function removePost(serverUrl: string, post: PostModel | Post) {
|
||||
}
|
||||
|
||||
if (removeModels.length) {
|
||||
await operator.batchRecords(removeModels, 'removePost (combined user activity)');
|
||||
await operator.batchRecords(removeModels);
|
||||
}
|
||||
} else {
|
||||
const postModel = await getPostById(database, post.id);
|
||||
if (postModel) {
|
||||
const preparedPost = await prepareDeletePost(postModel);
|
||||
if (preparedPost.length) {
|
||||
await operator.batchRecords(preparedPost, 'removePost');
|
||||
await operator.batchRecords(preparedPost);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -162,7 +162,7 @@ export async function markPostAsDeleted(serverUrl: string, post: Post, prepareRe
|
||||
});
|
||||
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords([dbPost], 'markPostAsDeleted');
|
||||
await operator.batchRecords([dbPost]);
|
||||
}
|
||||
return {model};
|
||||
} catch (error) {
|
||||
@@ -229,7 +229,7 @@ export async function storePostsForChannel(
|
||||
}
|
||||
|
||||
if (models.length && !prepareRecordsOnly) {
|
||||
await operator.batchRecords(models, 'storePostsForChannel');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
|
||||
return {models};
|
||||
|
||||
@@ -22,7 +22,7 @@ export async function removeUserFromTeam(serverUrl: string, teamId: string) {
|
||||
models.push(...system);
|
||||
}
|
||||
if (models.length) {
|
||||
await operator.batchRecords(models, 'removeUserFromTeam');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ export async function addSearchToTeamSearchHistory(serverUrl: string, teamId: st
|
||||
}
|
||||
}
|
||||
|
||||
await operator.batchRecords(models, 'addSearchToTeamHistory');
|
||||
await operator.batchRecords(models);
|
||||
return {searchModel};
|
||||
} catch (error) {
|
||||
logError('Failed addSearchToTeamSearchHistory', error);
|
||||
|
||||
@@ -40,7 +40,7 @@ export const switchToGlobalThreads = async (serverUrl: string, teamId?: string,
|
||||
models.push(...history);
|
||||
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords(models, 'switchToGlobalThreads');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
|
||||
const isTabletDevice = await isTablet();
|
||||
@@ -84,7 +84,7 @@ export const switchToThread = async (serverUrl: string, rootId: string, isFromNo
|
||||
currentChannelId: channel.id,
|
||||
});
|
||||
if (models.length) {
|
||||
await operator.batchRecords(models, 'switchToThread');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
} else {
|
||||
const modelPromises: Array<Promise<Model[]>> = [];
|
||||
@@ -97,7 +97,7 @@ export const switchToThread = async (serverUrl: string, rootId: string, isFromNo
|
||||
modelPromises.push(prepareCommonSystemValues(operator, commonValues));
|
||||
const models = (await Promise.all(modelPromises)).flat();
|
||||
if (models.length) {
|
||||
await operator.batchRecords(models, 'switchToThread');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,7 +201,7 @@ export async function createThreadFromNewPost(serverUrl: string, post: Post, pre
|
||||
}
|
||||
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords(models, 'createThreadFromNewPost');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
|
||||
return {models};
|
||||
@@ -257,7 +257,7 @@ export async function processReceivedThreads(serverUrl: string, threads: Thread[
|
||||
}
|
||||
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords(models, 'processReceivedThreads');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
return {models};
|
||||
} catch (error) {
|
||||
@@ -277,7 +277,7 @@ export async function markTeamThreadsAsRead(serverUrl: string, teamId: string, p
|
||||
record.viewedAt = Date.now();
|
||||
}));
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords(models, 'markTeamThreadsAsRead');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
return {models};
|
||||
} catch (error) {
|
||||
@@ -300,7 +300,7 @@ export async function markThreadAsViewed(serverUrl: string, threadId: string, pr
|
||||
});
|
||||
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords([thread], 'markThreadAsViewed');
|
||||
await operator.batchRecords([thread]);
|
||||
}
|
||||
|
||||
return {model: thread};
|
||||
@@ -327,7 +327,7 @@ export async function updateThread(serverUrl: string, threadId: string, updatedT
|
||||
record.unreadReplies = updatedThread.unread_replies ?? record.unreadReplies;
|
||||
});
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords([model], 'updateThread');
|
||||
await operator.batchRecords([model]);
|
||||
}
|
||||
return {model};
|
||||
} catch (error) {
|
||||
@@ -341,7 +341,7 @@ export async function updateTeamThreadsSync(serverUrl: string, data: TeamThreads
|
||||
const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
||||
const models = await operator.handleTeamThreadsSync({data: [data], prepareRecordsOnly});
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords(models, 'updateTeamThreadsSync');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
return {models};
|
||||
} catch (error) {
|
||||
|
||||
@@ -22,7 +22,7 @@ export async function setCurrentUserStatusOffline(serverUrl: string) {
|
||||
}
|
||||
|
||||
user.prepareStatus(General.OFFLINE);
|
||||
await operator.batchRecords([user], 'setCurrentUserStatusOffline');
|
||||
await operator.batchRecords([user]);
|
||||
return null;
|
||||
} catch (error) {
|
||||
logError('Failed setCurrentUserStatusOffline', error);
|
||||
@@ -54,7 +54,7 @@ export async function updateLocalCustomStatus(serverUrl: string, user: UserModel
|
||||
}
|
||||
}
|
||||
|
||||
await operator.batchRecords(models, 'updateLocalCustomStatus');
|
||||
await operator.batchRecords(models);
|
||||
|
||||
return {};
|
||||
} catch (error) {
|
||||
@@ -97,37 +97,27 @@ export const updateRecentCustomStatuses = async (serverUrl: string, customStatus
|
||||
export const updateLocalUser = async (
|
||||
serverUrl: string,
|
||||
userDetails: Partial<UserProfile> & { status?: string},
|
||||
userId?: string,
|
||||
) => {
|
||||
try {
|
||||
const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
||||
|
||||
let user: UserModel | undefined;
|
||||
|
||||
if (userId) {
|
||||
user = await getUserById(database, userId);
|
||||
} else {
|
||||
user = await getCurrentUser(database);
|
||||
}
|
||||
|
||||
const user = await getCurrentUser(database);
|
||||
if (user) {
|
||||
const u = user;
|
||||
await database.write(async () => {
|
||||
await u.update((userRecord: UserModel) => {
|
||||
userRecord.authService = userDetails.auth_service ?? u.authService;
|
||||
userRecord.email = userDetails.email ?? u.email;
|
||||
userRecord.firstName = userDetails.first_name ?? u.firstName;
|
||||
userRecord.lastName = userDetails.last_name ?? u.lastName;
|
||||
userRecord.lastPictureUpdate = userDetails.last_picture_update ?? u.lastPictureUpdate;
|
||||
userRecord.locale = userDetails.locale ?? u.locale;
|
||||
userRecord.nickname = userDetails.nickname ?? u.nickname;
|
||||
userRecord.notifyProps = userDetails.notify_props ?? u.notifyProps;
|
||||
userRecord.position = userDetails?.position ?? u.position;
|
||||
userRecord.props = userDetails.props ?? u.props;
|
||||
userRecord.roles = userDetails.roles ?? u.roles;
|
||||
userRecord.status = userDetails?.status ?? u.status;
|
||||
userRecord.timezone = userDetails.timezone ?? u.timezone;
|
||||
userRecord.username = userDetails.username ?? u.username;
|
||||
await user.update((userRecord: UserModel) => {
|
||||
userRecord.authService = userDetails.auth_service ?? user.authService;
|
||||
userRecord.email = userDetails.email ?? user.email;
|
||||
userRecord.firstName = userDetails.first_name ?? user.firstName;
|
||||
userRecord.lastName = userDetails.last_name ?? user.lastName;
|
||||
userRecord.lastPictureUpdate = userDetails.last_picture_update ?? user.lastPictureUpdate;
|
||||
userRecord.locale = userDetails.locale ?? user.locale;
|
||||
userRecord.nickname = userDetails.nickname ?? user.nickname;
|
||||
userRecord.notifyProps = userDetails.notify_props ?? user.notifyProps;
|
||||
userRecord.position = userDetails?.position ?? user.position;
|
||||
userRecord.props = userDetails.props ?? user.props;
|
||||
userRecord.roles = userDetails.roles ?? user.roles;
|
||||
userRecord.status = userDetails?.status ?? user.status;
|
||||
userRecord.timezone = userDetails.timezone ?? user.timezone;
|
||||
userRecord.username = userDetails.username ?? user.username;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import {DeviceEventEmitter} from 'react-native';
|
||||
import {addChannelToDefaultCategory, storeCategories} from '@actions/local/category';
|
||||
import {removeCurrentUserFromChannel, setChannelDeleteAt, storeMyChannelsForTeam, switchToChannel} from '@actions/local/channel';
|
||||
import {switchToGlobalThreads} from '@actions/local/thread';
|
||||
import {updateLocalUser} from '@actions/local/user';
|
||||
import {loadCallForChannel} from '@calls/actions/calls';
|
||||
import {DeepLink, Events, General, Preferences, Screens} from '@constants';
|
||||
import DatabaseManager from '@database/manager';
|
||||
@@ -16,8 +15,8 @@ import {getTeammateNameDisplaySetting} from '@helpers/api/preference';
|
||||
import AppsManager from '@managers/apps_manager';
|
||||
import NetworkManager from '@managers/network_manager';
|
||||
import {getActiveServer} from '@queries/app/servers';
|
||||
import {prepareMyChannelsForTeam, getChannelById, getChannelByName, getMyChannel, getChannelInfo, queryMyChannelSettingsByIds, getMembersCountByChannelsId, deleteChannelMembership, queryChannelsById} from '@queries/servers/channel';
|
||||
import {queryDisplayNamePreferences} from '@queries/servers/preference';
|
||||
import {prepareMyChannelsForTeam, getChannelById, getChannelByName, getMyChannel, getChannelInfo, queryMyChannelSettingsByIds, getMembersCountByChannelsId} from '@queries/servers/channel';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
import {getCommonSystemValues, getConfig, getCurrentChannelId, getCurrentTeamId, getCurrentUserId, getLicense, setCurrentChannelId, setCurrentTeamAndChannelId} from '@queries/servers/system';
|
||||
import {getNthLastChannelFromTeam, getMyTeamById, getTeamByName, queryMyTeams, removeChannelFromTeamHistory} from '@queries/servers/team';
|
||||
import {getCurrentUser} from '@queries/servers/user';
|
||||
@@ -36,10 +35,9 @@ import {setDirectChannelVisible} from './preference';
|
||||
import {fetchRolesIfNeeded} from './role';
|
||||
import {forceLogoutIfNecessary} from './session';
|
||||
import {addCurrentUserToTeam, fetchTeamByName, removeCurrentUserFromTeam} from './team';
|
||||
import {fetchProfilesInChannel, fetchProfilesInGroupChannels, fetchProfilesPerChannels, fetchUsersByIds, updateUsersNoLongerVisible} from './user';
|
||||
import {fetchProfilesInGroupChannels, fetchProfilesPerChannels, fetchUsersByIds, updateUsersNoLongerVisible} from './user';
|
||||
|
||||
import type {Client} from '@client/rest';
|
||||
import type ClientError from '@client/rest/error';
|
||||
import type {Model} from '@nozbe/watermelondb';
|
||||
import type ChannelModel from '@typings/database/models/servers/channel';
|
||||
import type {IntlShape} from 'react-intl';
|
||||
@@ -51,122 +49,37 @@ export type MyChannelsRequest = {
|
||||
error?: unknown;
|
||||
}
|
||||
|
||||
export type ChannelMembersRequest = {
|
||||
members?: ChannelMembership[];
|
||||
error?: unknown;
|
||||
}
|
||||
|
||||
export async function removeMemberFromChannel(serverUrl: string, channelId: string, userId: string) {
|
||||
try {
|
||||
const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
||||
const client = NetworkManager.getClient(serverUrl);
|
||||
|
||||
await client.removeFromChannel(userId, channelId);
|
||||
await deleteChannelMembership(operator, userId, channelId);
|
||||
|
||||
return {error: undefined};
|
||||
} catch (error) {
|
||||
logError('removeMemberFromChannel', error);
|
||||
forceLogoutIfNecessary(serverUrl, error as ClientErrorProps);
|
||||
return {error};
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchChannelMembersByIds(serverUrl: string, channelId: string, userIds: string[], fetchOnly = false): Promise<ChannelMembersRequest> {
|
||||
try {
|
||||
const client = NetworkManager.getClient(serverUrl);
|
||||
const members = await client.getChannelMembersByIds(channelId, userIds);
|
||||
|
||||
if (!fetchOnly) {
|
||||
const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
||||
if (operator && members.length) {
|
||||
const memberships = members.map((u) => ({
|
||||
channel_id: channelId,
|
||||
user_id: u.user_id,
|
||||
scheme_admin: u.scheme_admin,
|
||||
}));
|
||||
await operator.handleChannelMembership({
|
||||
channelMemberships: memberships,
|
||||
prepareRecordsOnly: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {members};
|
||||
} catch (error) {
|
||||
logError('fetchChannelMembersByIds', error);
|
||||
forceLogoutIfNecessary(serverUrl, error as ClientError);
|
||||
return {error};
|
||||
}
|
||||
}
|
||||
export async function updateChannelMemberSchemeRoles(serverUrl: string, channelId: string, userId: string, isSchemeUser: boolean, isSchemeAdmin: boolean, fetchOnly = false) {
|
||||
try {
|
||||
const client = NetworkManager.getClient(serverUrl);
|
||||
await client.updateChannelMemberSchemeRoles(channelId, userId, isSchemeUser, isSchemeAdmin);
|
||||
|
||||
if (!fetchOnly) {
|
||||
return getMemberInChannel(serverUrl, channelId, userId);
|
||||
}
|
||||
|
||||
return {error: undefined};
|
||||
} catch (error) {
|
||||
logError('updateChannelMemberSchemeRoles', error);
|
||||
forceLogoutIfNecessary(serverUrl, error as ClientErrorProps);
|
||||
return {error};
|
||||
}
|
||||
}
|
||||
|
||||
export async function getMemberInChannel(serverUrl: string, channelId: string, userId: string, fetchOnly = false) {
|
||||
try {
|
||||
const client = NetworkManager.getClient(serverUrl);
|
||||
const member = await client.getMemberInChannel(channelId, userId);
|
||||
|
||||
if (!fetchOnly) {
|
||||
updateLocalUser(serverUrl, member, userId);
|
||||
}
|
||||
return {member, error: undefined};
|
||||
} catch (error) {
|
||||
logError('getMemberInChannel', error);
|
||||
forceLogoutIfNecessary(serverUrl, error as ClientErrorProps);
|
||||
return {error};
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchChannelMemberships(serverUrl: string, channelId: string, options: GetUsersOptions, fetchOnly = false) {
|
||||
const {users = []} = await fetchProfilesInChannel(serverUrl, channelId, undefined, options, fetchOnly);
|
||||
const userIds = users.map((u) => u.id);
|
||||
|
||||
// MM-49896 https://mattermost.atlassian.net/browse/MM-49896
|
||||
// We are not sure the getChannelMembers API returns the same members
|
||||
// from getProfilesInChannel. This guarantees a 1:1 match of the
|
||||
// user IDs
|
||||
const {members = []} = await fetchChannelMembersByIds(serverUrl, channelId, userIds, true);
|
||||
return {users, members};
|
||||
}
|
||||
|
||||
export async function addMembersToChannel(serverUrl: string, channelId: string, userIds: string[], postRootId = '', fetchOnly = false) {
|
||||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||
if (!operator) {
|
||||
return {error: `${serverUrl} database not found`};
|
||||
}
|
||||
|
||||
let client: Client;
|
||||
try {
|
||||
client = NetworkManager.getClient(serverUrl);
|
||||
} catch (error) {
|
||||
return {error};
|
||||
}
|
||||
|
||||
try {
|
||||
const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
||||
const client = NetworkManager.getClient(serverUrl);
|
||||
const promises = userIds.map((id) => client.addToChannel(id, channelId, postRootId));
|
||||
const channelMemberships: ChannelMembership[] = await Promise.all(promises);
|
||||
const {users} = await fetchUsersByIds(serverUrl, userIds, true);
|
||||
|
||||
if (!fetchOnly) {
|
||||
const modelPromises: Array<Promise<Model[]>> = [];
|
||||
if (users?.length) {
|
||||
modelPromises.push(operator.handleUsers({
|
||||
users,
|
||||
prepareRecordsOnly: true,
|
||||
}));
|
||||
}
|
||||
modelPromises.push(operator.handleUsers({
|
||||
users,
|
||||
prepareRecordsOnly: true,
|
||||
}));
|
||||
modelPromises.push(operator.handleChannelMembership({
|
||||
channelMemberships,
|
||||
prepareRecordsOnly: true,
|
||||
}));
|
||||
|
||||
const models = await Promise.all(modelPromises);
|
||||
await operator.batchRecords(models.flat(), 'addMembersToChannel');
|
||||
await operator.batchRecords(models.flat());
|
||||
}
|
||||
return {channelMemberships};
|
||||
} catch (error) {
|
||||
@@ -243,7 +156,7 @@ export async function createChannel(serverUrl: string, displayName: string, purp
|
||||
models.push(...categoriesModels.models);
|
||||
}
|
||||
if (models.length) {
|
||||
await operator.batchRecords(models, 'createChannel');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
fetchChannelStats(serverUrl, channelData.id, false);
|
||||
EphemeralStore.creatingChannel = false;
|
||||
@@ -288,7 +201,7 @@ export async function patchChannel(serverUrl: string, channelPatch: Partial<Chan
|
||||
models.push(channel);
|
||||
}
|
||||
if (models?.length) {
|
||||
await operator.batchRecords(models.flat(), 'patchChannel');
|
||||
await operator.batchRecords(models.flat());
|
||||
}
|
||||
return {channel: channelData};
|
||||
} catch (error) {
|
||||
@@ -335,7 +248,7 @@ export async function leaveChannel(serverUrl: string, channelId: string) {
|
||||
models.push(...removeUserModels);
|
||||
}
|
||||
|
||||
await operator.batchRecords(models, 'leaveChannel');
|
||||
await operator.batchRecords(models);
|
||||
|
||||
if (isTabletDevice) {
|
||||
switchToLastChannel(serverUrl);
|
||||
@@ -385,7 +298,7 @@ export async function fetchChannelCreator(serverUrl: string, channelId: string,
|
||||
}));
|
||||
|
||||
const models = await Promise.all(modelPromises);
|
||||
await operator.batchRecords(models.flat(), 'fetchChannelCreator');
|
||||
await operator.batchRecords(models.flat());
|
||||
}
|
||||
|
||||
return {user};
|
||||
@@ -476,7 +389,7 @@ export async function fetchMyChannelsForTeam(serverUrl: string, teamId: string,
|
||||
const {models: catModels} = await storeCategories(serverUrl, categories, true, true); // Re-sync
|
||||
const models = (chModels || []).concat(catModels || []);
|
||||
if (models.length) {
|
||||
await operator.batchRecords(models, 'fetchMyChannelsForTeam');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
setTeamLoading(serverUrl, false);
|
||||
}
|
||||
@@ -525,37 +438,38 @@ export async function fetchMyChannel(serverUrl: string, teamId: string, channelI
|
||||
}
|
||||
|
||||
export async function fetchMissingDirectChannelsInfo(serverUrl: string, directChannels: Channel[], locale?: string, teammateDisplayNameSetting?: string, currentUserId?: string, fetchOnly = false) {
|
||||
try {
|
||||
const {database, operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
||||
const displayNameByChannel: Record<string, string> = {};
|
||||
const users: UserProfile[] = [];
|
||||
const updatedChannels = new Set<Channel>();
|
||||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||
if (!operator) {
|
||||
return {error: `${serverUrl} database not found`};
|
||||
}
|
||||
|
||||
const dms: Channel[] = [];
|
||||
const dmIds: string[] = [];
|
||||
const dmWithoutDisplayName = new Set<string>();
|
||||
const gms: Channel[] = [];
|
||||
const channelIds = new Set(directChannels.map((c) => c.id));
|
||||
const storedChannels = await queryChannelsById(database, Array.from(channelIds)).fetch();
|
||||
const storedChannelsMap = new Map(storedChannels.map((c) => [c.id, c]));
|
||||
const {database} = operator;
|
||||
const displayNameByChannel: Record<string, string> = {};
|
||||
const users: UserProfile[] = [];
|
||||
const updatedChannels = new Set<Channel>();
|
||||
|
||||
for (const c of directChannels) {
|
||||
if (c.type === General.DM_CHANNEL) {
|
||||
dms.push(c);
|
||||
dmIds.push(c.id);
|
||||
if (!c.display_name && !storedChannelsMap.get(c.id)?.displayName) {
|
||||
dmWithoutDisplayName.add(c.id);
|
||||
}
|
||||
continue;
|
||||
const dms: Channel[] = [];
|
||||
const dmIds: string[] = [];
|
||||
const dmWithoutDisplayName = new Set<string>();
|
||||
const gms: Channel[] = [];
|
||||
for (const c of directChannels) {
|
||||
if (c.type === General.DM_CHANNEL) {
|
||||
dms.push(c);
|
||||
dmIds.push(c.id);
|
||||
if (!c.display_name) {
|
||||
dmWithoutDisplayName.add(c.id);
|
||||
}
|
||||
gms.push(c);
|
||||
continue;
|
||||
}
|
||||
gms.push(c);
|
||||
}
|
||||
|
||||
try {
|
||||
const currentUser = await getCurrentUser(database);
|
||||
|
||||
// let's filter those channels that we already have the users
|
||||
const membersCount = await getMembersCountByChannelsId(database, dmIds);
|
||||
const profileChannelsToFetch = dmIds.filter((id) => membersCount[id] <= 1 && dmWithoutDisplayName.has(id));
|
||||
const profileChannelsToFetch = dmIds.filter((id) => membersCount[id] <= 1 || dmWithoutDisplayName.has(id));
|
||||
const results = await Promise.all([
|
||||
profileChannelsToFetch.length ? fetchProfilesPerChannels(serverUrl, profileChannelsToFetch, currentUserId, false) : Promise.resolve({data: undefined}),
|
||||
fetchProfilesInGroupChannels(serverUrl, gms.map((c) => c.id), false),
|
||||
@@ -601,7 +515,7 @@ export async function fetchMissingDirectChannelsInfo(serverUrl: string, directCh
|
||||
}
|
||||
|
||||
const models = await Promise.all(modelPromises);
|
||||
await operator.batchRecords(models.flat(), 'fetchMissingDirectChannelInfo');
|
||||
await operator.batchRecords(models.flat());
|
||||
}
|
||||
|
||||
return {directChannels: updatedChannelsArray, users};
|
||||
@@ -616,7 +530,7 @@ export async function fetchDirectChannelsInfo(serverUrl: string, directChannels:
|
||||
return {error: `${serverUrl} database not found`};
|
||||
}
|
||||
|
||||
const preferences = await queryDisplayNamePreferences(database).fetch();
|
||||
const preferences = await queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS).fetch();
|
||||
const config = await getConfig(database);
|
||||
const license = await getLicense(database);
|
||||
const teammateDisplayNameSetting = getTeammateNameDisplaySetting(preferences, config?.LockTeammateNameDisplay, config?.TeammateNameDisplay, license);
|
||||
@@ -679,7 +593,7 @@ export async function joinChannel(serverUrl: string, teamId: string, channelId?:
|
||||
}
|
||||
if (flattenedModels?.length > 0) {
|
||||
try {
|
||||
await operator.batchRecords(flattenedModels, 'joinChannel');
|
||||
await operator.batchRecords(flattenedModels);
|
||||
} catch {
|
||||
logError('FAILED TO BATCH CHANNELS');
|
||||
}
|
||||
@@ -862,7 +776,7 @@ export async function createDirectChannel(serverUrl: string, userId: string, dis
|
||||
if (displayName) {
|
||||
created.display_name = displayName;
|
||||
} else {
|
||||
const preferences = await queryDisplayNamePreferences(database, Preferences.NAME_NAME_FORMAT).fetch();
|
||||
const preferences = await queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.NAME_NAME_FORMAT).fetch();
|
||||
const license = await getLicense(database);
|
||||
const config = await getConfig(database);
|
||||
const teammateDisplayNameSetting = getTeammateNameDisplaySetting(preferences || [], config.LockTeammateNameDisplay, config.TeammateNameDisplay, license);
|
||||
@@ -905,7 +819,7 @@ export async function createDirectChannel(serverUrl: string, userId: string, dis
|
||||
models.push(...userModels);
|
||||
}
|
||||
|
||||
await operator.batchRecords(models, 'createDirectChannel');
|
||||
await operator.batchRecords(models);
|
||||
EphemeralStore.creatingDMorGMTeammates = [];
|
||||
fetchRolesIfNeeded(serverUrl, member.roles.split(' '));
|
||||
return {data: created};
|
||||
@@ -1004,7 +918,7 @@ export async function createGroupChannel(serverUrl: string, userIds: string[]) {
|
||||
return {data: created};
|
||||
}
|
||||
|
||||
const preferences = await queryDisplayNamePreferences(database, Preferences.NAME_NAME_FORMAT).fetch();
|
||||
const preferences = await queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.NAME_NAME_FORMAT).fetch();
|
||||
const license = await getLicense(database);
|
||||
const config = await getConfig(database);
|
||||
const teammateDisplayNameSetting = getTeammateNameDisplaySetting(preferences || [], config.LockTeammateNameDisplay, config.TeammateNameDisplay, license);
|
||||
@@ -1039,7 +953,7 @@ export async function createGroupChannel(serverUrl: string, userIds: string[]) {
|
||||
}
|
||||
|
||||
models.push(...userModels);
|
||||
operator.batchRecords(models, 'createGroupChannel');
|
||||
operator.batchRecords(models);
|
||||
}
|
||||
}
|
||||
EphemeralStore.creatingDMorGMTeammates = [];
|
||||
@@ -1240,7 +1154,7 @@ export async function searchAllChannels(serverUrl: string, term: string, archive
|
||||
}
|
||||
}
|
||||
|
||||
export const updateChannelNotifyProps = async (serverUrl: string, channelId: string, props: Partial<ChannelNotifyProps>) => {
|
||||
export const updateChannelNotifyProps = async (serverUrl: string, channelId: string, props: Partial<ChannelNotifyProps>, updateLocally = false) => {
|
||||
let client: Client;
|
||||
try {
|
||||
client = NetworkManager.getClient(serverUrl);
|
||||
@@ -1258,7 +1172,20 @@ export const updateChannelNotifyProps = async (serverUrl: string, channelId: str
|
||||
const notifyProps = {...props, channel_id: channelId, user_id: userId} as ChannelNotifyProps & {channel_id: string; user_id: string};
|
||||
|
||||
await client.updateChannelNotifyProps(notifyProps);
|
||||
if (updateLocally) {
|
||||
const channelSettings = await queryMyChannelSettingsByIds(database, [channelId]).fetch();
|
||||
const myChannelSetting = channelSettings?.[0];
|
||||
|
||||
if (myChannelSetting) {
|
||||
const updatedProps: Partial<ChannelNotifyProps> = {...myChannelSetting.notifyProps, ...notifyProps};
|
||||
|
||||
await database.write(async () => {
|
||||
await myChannelSetting.update((c) => {
|
||||
c.notifyProps = updatedProps;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
return {
|
||||
notifyProps,
|
||||
};
|
||||
@@ -1277,6 +1204,10 @@ export const toggleMuteChannel = async (serverUrl: string, channelId: string, sh
|
||||
try {
|
||||
const channelSettings = await queryMyChannelSettingsByIds(database, [channelId]).fetch();
|
||||
const myChannelSetting = channelSettings?.[0];
|
||||
if (!myChannelSetting) {
|
||||
return {error: 'Channel setting not found'};
|
||||
}
|
||||
|
||||
const mark_unread = myChannelSetting.notifyProps?.mark_unread === 'mention' ? 'all' : 'mention';
|
||||
|
||||
const notifyProps: Partial<ChannelNotifyProps> = {...myChannelSetting.notifyProps, mark_unread};
|
||||
@@ -1384,7 +1315,7 @@ export const convertChannelToPrivate = async (serverUrl: string, channelId: stri
|
||||
channel.prepareUpdate((c) => {
|
||||
c.type = General.PRIVATE_CHANNEL;
|
||||
});
|
||||
await operator.batchRecords([channel], 'convertChannelToPrivate');
|
||||
await operator.batchRecords([channel]);
|
||||
}
|
||||
return {error: undefined};
|
||||
} catch (error) {
|
||||
|
||||
@@ -33,7 +33,7 @@ export async function appEntry(serverUrl: string, since = 0, isUpgrade = false)
|
||||
// clear lastUnreadChannelId
|
||||
const removeLastUnreadChannelId = await prepareCommonSystemValues(operator, {lastUnreadChannelId: ''});
|
||||
if (removeLastUnreadChannelId) {
|
||||
await operator.batchRecords(removeLastUnreadChannelId, 'appEntry - removeLastUnreadChannelId');
|
||||
await operator.batchRecords(removeLastUnreadChannelId);
|
||||
}
|
||||
|
||||
const {database} = operator;
|
||||
@@ -58,14 +58,14 @@ export async function appEntry(serverUrl: string, since = 0, isUpgrade = false)
|
||||
currentChannelId: isTabletDevice ? initialChannelId : undefined,
|
||||
});
|
||||
if (me?.length) {
|
||||
await operator.batchRecords(me, 'appEntry - upgrade store me');
|
||||
await operator.batchRecords(me);
|
||||
}
|
||||
}
|
||||
|
||||
await handleEntryAfterLoadNavigation(serverUrl, teamData.memberships || [], chData?.memberships || [], currentTeamId, currentChannelId, initialTeamId, initialChannelId);
|
||||
|
||||
const dt = Date.now();
|
||||
await operator.batchRecords(models, 'appEntry');
|
||||
await operator.batchRecords(models);
|
||||
logInfo('ENTRY MODELS BATCHING TOOK', `${Date.now() - dt}ms`);
|
||||
setTeamLoading(serverUrl, false);
|
||||
|
||||
|
||||
@@ -178,7 +178,7 @@ export const fetchAppEntryData = async (serverUrl: string, sinceArg: number, ini
|
||||
if (!initialTeamId && teamData.teams?.length && teamData.memberships?.length) {
|
||||
// If no initial team was set in the database but got teams in the response
|
||||
const config = await getConfig(database);
|
||||
const teamOrderPreference = getPreferenceValue<string>(prefData.preferences || [], Preferences.CATEGORIES.TEAMS_ORDER, '', '');
|
||||
const teamOrderPreference = getPreferenceValue(prefData.preferences || [], Preferences.TEAMS_ORDER, '', '') as string;
|
||||
const teamMembers = new Set(teamData.memberships.filter((m) => m.delete_at === 0).map((m) => m.team_id));
|
||||
const myTeams = teamData.teams!.filter((t) => teamMembers.has(t.id));
|
||||
const defaultTeam = selectDefaultTeam(myTeams, meData.user?.locale || DEFAULT_LOCALE, teamOrderPreference, config?.ExperimentalPrimaryTeam);
|
||||
@@ -431,7 +431,7 @@ const graphQLSyncAllChannelMembers = async (serverUrl: string) => {
|
||||
const modelPromises = await prepareMyChannelsForTeam(operator, '', channels, memberships, undefined, true);
|
||||
const models = (await Promise.all(modelPromises)).flat();
|
||||
if (models.length) {
|
||||
await operator.batchRecords(models, 'graphQLSyncAllChannelMembers');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ export async function deferredAppEntryGraphQLActions(
|
||||
modelPromises.push(operator.handleRole({roles, prepareRecordsOnly: true}));
|
||||
}
|
||||
const models = (await Promise.all(modelPromises)).flat();
|
||||
operator.batchRecords(models, 'deferredAppEntryActions');
|
||||
operator.batchRecords(models);
|
||||
|
||||
setTimeout(() => {
|
||||
if (result.chData?.channels?.length && result.chData.memberships?.length) {
|
||||
@@ -217,7 +217,7 @@ export const entryGQL = async (serverUrl: string, currentTeamId?: string, curren
|
||||
if (!teamData.teams.length) {
|
||||
initialTeamId = '';
|
||||
} else if (!initialTeamId || !teamData.teams.find((t) => t.id === currentTeamId && t.delete_at === 0)) {
|
||||
const teamOrderPreference = getPreferenceValue<string>(prefData.preferences || [], Preferences.CATEGORIES.TEAMS_ORDER, '', '');
|
||||
const teamOrderPreference = getPreferenceValue(prefData.preferences || [], Preferences.TEAMS_ORDER, '', '') as string;
|
||||
initialTeamId = selectDefaultTeam(teamData.teams, meData.user.locale, teamOrderPreference, config.ExperimentalPrimaryTeam)?.id || '';
|
||||
}
|
||||
const gqlRoles = [
|
||||
|
||||
@@ -68,7 +68,7 @@ export async function loginEntry({serverUrl, user, deviceToken}: AfterLoginArgs)
|
||||
setCurrentTeamAndChannelId(operator, initialTeamId, '');
|
||||
}
|
||||
|
||||
await operator.batchRecords(models, 'loginEntry');
|
||||
await operator.batchRecords(models);
|
||||
setTeamLoading(serverUrl, false);
|
||||
|
||||
const config = clData.config || {} as ClientConfig;
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
|
||||
import {switchToChannelById} from '@actions/remote/channel';
|
||||
import {fetchAndSwitchToThread} from '@actions/remote/thread';
|
||||
import {Screens} from '@constants';
|
||||
import {Preferences, Screens} from '@constants';
|
||||
import {getDefaultThemeByAppearance} from '@context/theme';
|
||||
import DatabaseManager from '@database/manager';
|
||||
import {getMyChannel} from '@queries/servers/channel';
|
||||
import {queryThemePreferences} from '@queries/servers/preference';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
import {getConfig, getCurrentTeamId, getLicense, getWebSocketLastDisconnected, setCurrentTeamAndChannelId} from '@queries/servers/system';
|
||||
import {getMyTeamById} from '@queries/servers/team';
|
||||
import {getIsCRTEnabled} from '@queries/servers/thread';
|
||||
@@ -53,7 +53,7 @@ export async function pushNotificationEntry(serverUrl: string, notification: Not
|
||||
// When opening the app from a push notification the theme may not be set in the EphemeralStore
|
||||
// causing the goToScreen to use the Appearance theme instead and that causes the screen background color to potentially
|
||||
// not match the theme
|
||||
const themes = await queryThemePreferences(database, teamId).fetch();
|
||||
const themes = await queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_THEME, teamId).fetch();
|
||||
let theme = getDefaultThemeByAppearance();
|
||||
if (themes.length) {
|
||||
theme = setThemeDefaults(JSON.parse(themes[0].value) as Theme);
|
||||
@@ -136,7 +136,7 @@ export async function pushNotificationEntry(serverUrl: string, notification: Not
|
||||
await NavigationStore.waitUntilScreenHasLoaded(Screens.THREAD);
|
||||
}
|
||||
|
||||
await operator.batchRecords(models, 'pushNotificationEntry');
|
||||
await operator.batchRecords(models);
|
||||
setTeamLoading(serverUrl, false);
|
||||
|
||||
const {id: currentUserId, locale: currentUserLocale} = (await getCurrentUser(operator.database))!;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
import {DOWNLOAD_TIMEOUT} from '@constants/network';
|
||||
import NetworkManager from '@managers/network_manager';
|
||||
import {logDebug} from '@utils/log';
|
||||
|
||||
import {forceLogoutIfNecessary} from './session';
|
||||
|
||||
@@ -33,11 +32,10 @@ export const uploadFile = (
|
||||
let client: Client;
|
||||
try {
|
||||
client = NetworkManager.getClient(serverUrl);
|
||||
return {cancel: client.uploadPostAttachment(file, channelId, onProgress, onComplete, onError, skipBytes)};
|
||||
} catch (error) {
|
||||
logDebug('uploadFile', error);
|
||||
return {error: error as ClientError};
|
||||
}
|
||||
return {cancel: client.uploadPostAttachment(file, channelId, onProgress, onComplete, onError, skipBytes)};
|
||||
};
|
||||
|
||||
export const fetchPublicLink = async (serverUrl: string, fileId: string) => {
|
||||
|
||||
@@ -83,7 +83,7 @@ export const fetchGroupsForChannel = async (serverUrl: string, channelId: string
|
||||
]);
|
||||
|
||||
if (!fetchOnly) {
|
||||
await operator.batchRecords([...groups, ...groupChannels], 'fetchGroupsForChannel');
|
||||
await operator.batchRecords([...groups, ...groupChannels]);
|
||||
}
|
||||
|
||||
return {groups, groupChannels};
|
||||
@@ -110,7 +110,7 @@ export const fetchGroupsForTeam = async (serverUrl: string, teamId: string, fetc
|
||||
]);
|
||||
|
||||
if (!fetchOnly) {
|
||||
await operator.batchRecords([...groups, ...groupTeams], 'fetchGroupsForTeam');
|
||||
await operator.batchRecords([...groups, ...groupTeams]);
|
||||
}
|
||||
|
||||
return {groups, groupTeams};
|
||||
@@ -136,7 +136,7 @@ export const fetchGroupsForMember = async (serverUrl: string, userId: string, fe
|
||||
]);
|
||||
|
||||
if (!fetchOnly) {
|
||||
await operator.batchRecords([...groups, ...groupMemberships], 'fetchGroupsForMember');
|
||||
await operator.batchRecords([...groups, ...groupMemberships]);
|
||||
}
|
||||
|
||||
return {groups, groupMemberships};
|
||||
|
||||
@@ -128,7 +128,7 @@ export async function createPost(serverUrl: string, post: Partial<Post>, files:
|
||||
initialPostModels.push(...reactionModels);
|
||||
}
|
||||
|
||||
await operator.batchRecords(initialPostModels, 'createPost - initial');
|
||||
await operator.batchRecords(initialPostModels);
|
||||
|
||||
const isCRTEnabled = await getIsCRTEnabled(database);
|
||||
|
||||
@@ -167,7 +167,7 @@ export async function createPost(serverUrl: string, post: Partial<Post>, files:
|
||||
models.push(...threadModels);
|
||||
}
|
||||
}
|
||||
await operator.batchRecords(models, 'createPost - failure');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
|
||||
return {data: true};
|
||||
@@ -192,7 +192,7 @@ export async function createPost(serverUrl: string, post: Partial<Post>, files:
|
||||
models.push(...threadModels);
|
||||
}
|
||||
}
|
||||
await operator.batchRecords(models, 'createPost - success');
|
||||
await operator.batchRecords(models);
|
||||
|
||||
newPost = created;
|
||||
|
||||
@@ -237,7 +237,7 @@ export const retryFailedPost = async (serverUrl: string, post: PostModel) => {
|
||||
p.props = newPost.props;
|
||||
p.updateAt = timestamp;
|
||||
});
|
||||
await operator.batchRecords([post], 'retryFailedPost - first update');
|
||||
await operator.batchRecords([post]);
|
||||
|
||||
const created = await client.createPost(newPost);
|
||||
const models = await operator.handlePosts({
|
||||
@@ -253,7 +253,7 @@ export const retryFailedPost = async (serverUrl: string, post: PostModel) => {
|
||||
models.push(member);
|
||||
}
|
||||
}
|
||||
await operator.batchRecords(models, 'retryFailedPost - success update');
|
||||
await operator.batchRecords(models);
|
||||
} catch (error) {
|
||||
if (isServerError(error) && (
|
||||
error.server_error_id === ServerErrors.DELETED_ROOT_POST_ERROR ||
|
||||
@@ -268,7 +268,7 @@ export const retryFailedPost = async (serverUrl: string, post: PostModel) => {
|
||||
failed: true,
|
||||
};
|
||||
});
|
||||
await operator.batchRecords([post], 'retryFailedPost - error update');
|
||||
await operator.batchRecords([post]);
|
||||
}
|
||||
|
||||
return {error};
|
||||
@@ -375,7 +375,7 @@ export async function fetchPosts(serverUrl: string, channelId: string, page = 0,
|
||||
models.push(...threadModels);
|
||||
}
|
||||
}
|
||||
await operator.batchRecords(models, 'fetchPosts');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
return result;
|
||||
} catch (error) {
|
||||
@@ -434,7 +434,7 @@ export async function fetchPostsBefore(serverUrl: string, channelId: string, pos
|
||||
}
|
||||
}
|
||||
|
||||
await operator.batchRecords(models, 'fetchPostsBefore');
|
||||
await operator.batchRecords(models);
|
||||
} catch (error) {
|
||||
logError('FETCH POSTS BEFORE ERROR', error);
|
||||
}
|
||||
@@ -489,7 +489,7 @@ export async function fetchPostsSince(serverUrl: string, channelId: string, sinc
|
||||
models.push(...threadModels);
|
||||
}
|
||||
}
|
||||
await operator.batchRecords(models, 'fetchPostsSince');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
return result;
|
||||
} catch (error) {
|
||||
@@ -621,7 +621,7 @@ export async function fetchPostThread(serverUrl: string, postId: string, options
|
||||
models.push(...threadModels);
|
||||
}
|
||||
}
|
||||
await operator.batchRecords(models, 'fetchPostThread');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
setFetchingThreadState(postId, false);
|
||||
return {posts: extractRecordsForTable<PostModel>(posts, MM_TABLES.SERVER.POST)};
|
||||
@@ -697,7 +697,7 @@ export async function fetchPostsAround(serverUrl: string, channelId: string, pos
|
||||
models.push(...threadModels);
|
||||
}
|
||||
}
|
||||
await operator.batchRecords(models, 'fetchPostsAround');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
|
||||
return {posts: extractRecordsForTable<PostModel>(posts, MM_TABLES.SERVER.POST)};
|
||||
@@ -751,7 +751,7 @@ export async function fetchMissingChannelsFromPosts(serverUrl: string, posts: Po
|
||||
return mdls;
|
||||
});
|
||||
if (models.length) {
|
||||
await operator.batchRecords(models, 'fetchMissingChannelsFromPosts');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -809,7 +809,7 @@ export async function fetchPostById(serverUrl: string, postId: string, fetchOnly
|
||||
}
|
||||
}
|
||||
|
||||
await operator.batchRecords(models, 'fetchPostById');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
|
||||
return {post};
|
||||
@@ -1036,7 +1036,7 @@ export async function fetchSavedPosts(serverUrl: string, teamId?: string, channe
|
||||
return mdls;
|
||||
});
|
||||
|
||||
await operator.batchRecords(models, 'fetchSavedPosts');
|
||||
await operator.batchRecords(models);
|
||||
|
||||
return {
|
||||
order,
|
||||
@@ -1118,7 +1118,7 @@ export async function fetchPinnedPosts(serverUrl: string, channelId: string) {
|
||||
return mdls;
|
||||
});
|
||||
|
||||
await operator.batchRecords(models, 'fetchPinnedPosts');
|
||||
await operator.batchRecords(models);
|
||||
|
||||
return {
|
||||
order,
|
||||
|
||||
@@ -5,12 +5,14 @@ import {General, Preferences} from '@constants';
|
||||
import DatabaseManager from '@database/manager';
|
||||
import NetworkManager from '@managers/network_manager';
|
||||
import {getChannelById} from '@queries/servers/channel';
|
||||
import {querySavedPostsPreferences} from '@queries/servers/preference';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
import {getCurrentUserId} from '@queries/servers/system';
|
||||
import {getUserIdFromChannelName} from '@utils/user';
|
||||
|
||||
import {forceLogoutIfNecessary} from './session';
|
||||
|
||||
const {CATEGORY_DIRECT_CHANNEL_SHOW, CATEGORY_GROUP_CHANNEL_SHOW, CATEGORY_FAVORITE_CHANNEL, CATEGORY_SAVED_POST} = Preferences;
|
||||
|
||||
export type MyPreferencesRequest = {
|
||||
preferences?: PreferenceType[];
|
||||
error?: unknown;
|
||||
@@ -54,7 +56,7 @@ export const saveFavoriteChannel = async (serverUrl: string, channelId: string,
|
||||
try {
|
||||
const userId = await getCurrentUserId(operator.database);
|
||||
const favPref: PreferenceType = {
|
||||
category: Preferences.CATEGORIES.FAVORITE_CHANNEL,
|
||||
category: CATEGORY_FAVORITE_CHANNEL,
|
||||
name: channelId,
|
||||
user_id: userId,
|
||||
value: String(isFavorite),
|
||||
@@ -75,7 +77,7 @@ export const savePostPreference = async (serverUrl: string, postId: string) => {
|
||||
const userId = await getCurrentUserId(operator.database);
|
||||
const pref: PreferenceType = {
|
||||
user_id: userId,
|
||||
category: Preferences.CATEGORIES.SAVED_POST,
|
||||
category: CATEGORY_SAVED_POST,
|
||||
name: postId,
|
||||
value: 'true',
|
||||
};
|
||||
@@ -114,15 +116,23 @@ export const savePreference = async (serverUrl: string, preferences: PreferenceT
|
||||
};
|
||||
|
||||
export const deleteSavedPost = async (serverUrl: string, postId: string) => {
|
||||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||
if (!operator) {
|
||||
return {error: `${serverUrl} database not found`};
|
||||
}
|
||||
let client;
|
||||
try {
|
||||
const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
||||
const client = NetworkManager.getClient(serverUrl);
|
||||
const userId = await getCurrentUserId(database);
|
||||
const records = await querySavedPostsPreferences(database, postId).fetch();
|
||||
client = NetworkManager.getClient(serverUrl);
|
||||
} catch (error) {
|
||||
return {error};
|
||||
}
|
||||
try {
|
||||
const userId = await getCurrentUserId(operator.database);
|
||||
const records = await queryPreferencesByCategoryAndName(operator.database, CATEGORY_SAVED_POST, postId).fetch();
|
||||
const postPreferenceRecord = records.find((r) => postId === r.name);
|
||||
const pref = {
|
||||
user_id: userId,
|
||||
category: Preferences.CATEGORIES.SAVED_POST,
|
||||
category: CATEGORY_SAVED_POST,
|
||||
name: postId,
|
||||
value: 'true',
|
||||
};
|
||||
@@ -151,8 +161,7 @@ export const setDirectChannelVisible = async (serverUrl: string, channelId: stri
|
||||
const channel = await getChannelById(database, channelId);
|
||||
if (channel?.type === General.DM_CHANNEL || channel?.type === General.GM_CHANNEL) {
|
||||
const userId = await getCurrentUserId(database);
|
||||
const {DIRECT_CHANNEL_SHOW, GROUP_CHANNEL_SHOW} = Preferences.CATEGORIES;
|
||||
const category = channel.type === General.DM_CHANNEL ? DIRECT_CHANNEL_SHOW : GROUP_CHANNEL_SHOW;
|
||||
const category = channel.type === General.DM_CHANNEL ? CATEGORY_DIRECT_CHANNEL_SHOW : CATEGORY_GROUP_CHANNEL_SHOW;
|
||||
const name = channel.type === General.DM_CHANNEL ? getUserIdFromChannelName(userId, channel.name) : channelId;
|
||||
const pref: PreferenceType = {
|
||||
user_id: userId,
|
||||
@@ -176,7 +185,7 @@ export const savePreferredSkinTone = async (serverUrl: string, skinCode: string)
|
||||
const userId = await getCurrentUserId(database);
|
||||
const pref: PreferenceType = {
|
||||
user_id: userId,
|
||||
category: Preferences.CATEGORIES.EMOJI,
|
||||
category: Preferences.CATEGORY_EMOJI,
|
||||
name: Preferences.EMOJI_SKINTONE,
|
||||
value: skinCode,
|
||||
};
|
||||
|
||||
@@ -51,7 +51,7 @@ export async function addReaction(serverUrl: string, postId: string, emojiName:
|
||||
models.push(...recent);
|
||||
}
|
||||
|
||||
await operator.batchRecords(models, 'addReaction');
|
||||
await operator.batchRecords(models);
|
||||
|
||||
return {reaction};
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import {selectDefaultTeam} from '@helpers/api/team';
|
||||
import NetworkManager from '@managers/network_manager';
|
||||
import {prepareCategoriesAndCategoriesChannels} from '@queries/servers/categories';
|
||||
import {prepareMyChannelsForTeam} from '@queries/servers/channel';
|
||||
import {prepareMyPreferences, queryDisplayNamePreferences} from '@queries/servers/preference';
|
||||
import {prepareMyPreferences, queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
import {prepareCommonSystemValues, getConfig, getLicense} from '@queries/servers/system';
|
||||
import {prepareMyTeams} from '@queries/servers/team';
|
||||
import {getCurrentUser} from '@queries/servers/user';
|
||||
@@ -60,7 +60,7 @@ export async function retryInitialTeamAndChannel(serverUrl: string) {
|
||||
|
||||
// select initial team
|
||||
if (!clData.error && !prefData.error && !teamData.error) {
|
||||
const teamOrderPreference = getPreferenceValue<string>(prefData.preferences!, Preferences.CATEGORIES.TEAMS_ORDER, '', '');
|
||||
const teamOrderPreference = getPreferenceValue(prefData.preferences!, Preferences.TEAMS_ORDER, '', '') as string;
|
||||
const teamRoles: string[] = [];
|
||||
const teamMembers = new Set<string>();
|
||||
|
||||
@@ -117,7 +117,7 @@ export async function retryInitialTeamAndChannel(serverUrl: string) {
|
||||
),
|
||||
])).flat();
|
||||
|
||||
await operator.batchRecords(models, 'retryInitialTeamAndChannel');
|
||||
await operator.batchRecords(models);
|
||||
|
||||
const directChannels = chData!.channels!.filter(isDMorGM);
|
||||
const channelsToFetchProfiles = new Set<Channel>(directChannels);
|
||||
@@ -157,7 +157,7 @@ export async function retryInitialChannel(serverUrl: string, teamId: string) {
|
||||
return {error: true};
|
||||
}
|
||||
|
||||
const prefs = await queryDisplayNamePreferences(database, Preferences.NAME_NAME_FORMAT).fetch();
|
||||
const prefs = await queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.NAME_NAME_FORMAT).fetch();
|
||||
const preferences: PreferenceType[] = prefs.map((p) => ({
|
||||
category: p.category,
|
||||
name: p.name,
|
||||
@@ -196,7 +196,7 @@ export async function retryInitialChannel(serverUrl: string, teamId: string) {
|
||||
prepareCommonSystemValues(operator, {currentChannelId: initialChannel?.id}),
|
||||
])).flat();
|
||||
|
||||
await operator.batchRecords(models, 'retryInitialChannel');
|
||||
await operator.batchRecords(models);
|
||||
|
||||
const directChannels = chData!.channels!.filter(isDMorGM);
|
||||
const channelsToFetchProfiles = new Set<Channel>(directChannels);
|
||||
|
||||
@@ -103,7 +103,7 @@ export const searchPosts = async (serverUrl: string, teamId: string, params: Pos
|
||||
return mdls;
|
||||
});
|
||||
|
||||
await operator.batchRecords(models, 'searchPosts');
|
||||
await operator.batchRecords(models);
|
||||
return {
|
||||
order,
|
||||
posts: postsArray,
|
||||
|
||||
@@ -86,7 +86,7 @@ export async function addUserToTeam(serverUrl: string, teamId: string, userId: s
|
||||
prepareCategoriesAndCategoriesChannels(operator, categories || [], true),
|
||||
])).flat();
|
||||
|
||||
await operator.batchRecords(models, 'addUserToTeam');
|
||||
await operator.batchRecords(models);
|
||||
setTeamLoading(serverUrl, false);
|
||||
loadEventSent = false;
|
||||
|
||||
@@ -199,7 +199,7 @@ export async function fetchMyTeams(serverUrl: string, fetchOnly = false): Promis
|
||||
const models = await Promise.all(modelPromises);
|
||||
const flattenedModels = models.flat();
|
||||
if (flattenedModels.length > 0) {
|
||||
await operator.batchRecords(flattenedModels, 'fetchMyTeams');
|
||||
await operator.batchRecords(flattenedModels);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -233,7 +233,7 @@ export async function fetchMyTeam(serverUrl: string, teamId: string, fetchOnly =
|
||||
const models = await Promise.all(modelPromises);
|
||||
const flattenedModels = models.flat();
|
||||
if (flattenedModels?.length > 0) {
|
||||
await operator.batchRecords(flattenedModels, 'fetchMyTeam');
|
||||
await operator.batchRecords(flattenedModels);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -362,7 +362,7 @@ export async function fetchTeamByName(serverUrl: string, teamName: string, fetch
|
||||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||
if (operator) {
|
||||
const models = await operator.handleTeam({teams: [team], prepareRecordsOnly: true});
|
||||
await operator.batchRecords(models, 'fetchTeamByName');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -441,7 +441,7 @@ export async function handleTeamChange(serverUrl: string, teamId: string) {
|
||||
}
|
||||
|
||||
if (models.length) {
|
||||
await operator.batchRecords(models, 'handleTeamChange');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
DeviceEventEmitter.emit(Events.TEAM_SWITCH, false);
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ export async function updateTermsOfServiceStatus(serverUrl: string, id: string,
|
||||
u.termsOfServiceId = '';
|
||||
}
|
||||
});
|
||||
operator.batchRecords([currentUser], 'updateTermsOfServiceStatus');
|
||||
operator.batchRecords([currentUser]);
|
||||
}
|
||||
return {resp};
|
||||
} catch (error) {
|
||||
|
||||
@@ -360,7 +360,7 @@ export const syncTeamThreads = async (serverUrl: string, teamId: string, prepare
|
||||
const allNewThreads = await fetchThreads(
|
||||
serverUrl,
|
||||
teamId,
|
||||
{deleted: true, since: syncData.latest + 1},
|
||||
{deleted: true, since: syncData.latest},
|
||||
);
|
||||
if (allNewThreads.error) {
|
||||
return {error: allNewThreads.error};
|
||||
@@ -395,7 +395,7 @@ export const syncTeamThreads = async (serverUrl: string, teamId: string, prepare
|
||||
|
||||
if (!prepareRecordsOnly && models?.length) {
|
||||
try {
|
||||
await operator.batchRecords(models, 'syncTeamThreads');
|
||||
await operator.batchRecords(models);
|
||||
} catch (err) {
|
||||
if (__DEV__) {
|
||||
throw err;
|
||||
@@ -460,7 +460,7 @@ export const loadEarlierThreads = async (serverUrl: string, teamId: string, last
|
||||
|
||||
if (!prepareRecordsOnly && models?.length) {
|
||||
try {
|
||||
await operator.batchRecords(models, 'loadEarlierThreads');
|
||||
await operator.batchRecords(models);
|
||||
} catch (err) {
|
||||
if (__DEV__) {
|
||||
throw err;
|
||||
|
||||
@@ -74,7 +74,7 @@ export const fetchMe = async (serverUrl: string, fetchOnly = false): Promise<MyU
|
||||
}
|
||||
};
|
||||
|
||||
export async function fetchProfilesInChannel(serverUrl: string, channelId: string, excludeUserId?: string, options?: GetUsersOptions, fetchOnly = false): Promise<ProfilesInChannelRequest> {
|
||||
export async function fetchProfilesInChannel(serverUrl: string, channelId: string, excludeUserId?: string, fetchOnly = false): Promise<ProfilesInChannelRequest> {
|
||||
let client: Client;
|
||||
try {
|
||||
client = NetworkManager.getClient(serverUrl);
|
||||
@@ -83,7 +83,7 @@ export async function fetchProfilesInChannel(serverUrl: string, channelId: strin
|
||||
}
|
||||
|
||||
try {
|
||||
const users = await client.getProfilesInChannel(channelId, options);
|
||||
const users = await client.getProfilesInChannel(channelId);
|
||||
const uniqueUsers = Array.from(new Set(users));
|
||||
const filteredUsers = uniqueUsers.filter((u) => u.id !== excludeUserId);
|
||||
if (!fetchOnly) {
|
||||
@@ -102,13 +102,12 @@ export async function fetchProfilesInChannel(serverUrl: string, channelId: strin
|
||||
modelPromises.push(prepare);
|
||||
|
||||
const models = await Promise.all(modelPromises);
|
||||
await operator.batchRecords(models.flat(), 'fetchProfilesInChannel');
|
||||
await operator.batchRecords(models.flat());
|
||||
}
|
||||
}
|
||||
|
||||
return {channelId, users: filteredUsers};
|
||||
} catch (error) {
|
||||
logError('fetchProfilesInChannel', error);
|
||||
forceLogoutIfNecessary(serverUrl, error as ClientError);
|
||||
return {channelId, error};
|
||||
}
|
||||
@@ -178,7 +177,7 @@ export async function fetchProfilesInGroupChannels(serverUrl: string, groupChann
|
||||
}
|
||||
|
||||
const models = await Promise.all(modelPromises);
|
||||
await operator.batchRecords(models.flat(), 'fetchProfilesInGroupChannels');
|
||||
await operator.batchRecords(models.flat());
|
||||
}
|
||||
|
||||
return {data};
|
||||
@@ -199,7 +198,7 @@ export async function fetchProfilesPerChannels(serverUrl: string, channelIds: st
|
||||
const data: ProfilesInChannelRequest[] = [];
|
||||
|
||||
for await (const cIds of channels) {
|
||||
const requests = cIds.map((id) => fetchProfilesInChannel(serverUrl, id, excludeUserId, undefined, true));
|
||||
const requests = cIds.map((id) => fetchProfilesInChannel(serverUrl, id, excludeUserId, true));
|
||||
const response = await Promise.all(requests);
|
||||
data.push(...response);
|
||||
}
|
||||
@@ -230,7 +229,7 @@ export async function fetchProfilesPerChannels(serverUrl: string, channelIds: st
|
||||
}
|
||||
|
||||
const models = await Promise.all(modelPromises);
|
||||
await operator.batchRecords(models.flat(), 'fetchProfilesPerChannels');
|
||||
await operator.batchRecords(models.flat());
|
||||
}
|
||||
|
||||
return {data};
|
||||
@@ -366,7 +365,7 @@ export async function fetchStatusByIds(serverUrl: string, userIds: string[], fet
|
||||
user.prepareStatus(status?.status || General.OFFLINE);
|
||||
}
|
||||
|
||||
await operator.batchRecords(users, 'fetchStatusByIds');
|
||||
await operator.batchRecords(users);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -531,37 +530,7 @@ export const fetchProfilesInTeam = async (serverUrl: string, teamId: string, pag
|
||||
}
|
||||
};
|
||||
|
||||
export const fetchProfilesNotInChannel = async (
|
||||
serverUrl: string,
|
||||
teamId: string,
|
||||
channelId: string,
|
||||
groupConstrained: boolean,
|
||||
page = 0,
|
||||
perPage: number = General.PROFILE_CHUNK_SIZE,
|
||||
fetchOnly = false,
|
||||
) => {
|
||||
try {
|
||||
const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
||||
const client = NetworkManager.getClient(serverUrl);
|
||||
const users = await client.getProfilesNotInChannel(teamId, channelId, groupConstrained, page, perPage);
|
||||
|
||||
if (!fetchOnly && users.length) {
|
||||
const currentUserId = await getCurrentUserId(operator.database);
|
||||
const toStore = removeUserFromList(currentUserId, users);
|
||||
await operator.handleUsers({
|
||||
users: toStore,
|
||||
prepareRecordsOnly: false,
|
||||
});
|
||||
}
|
||||
|
||||
return {users};
|
||||
} catch (error) {
|
||||
forceLogoutIfNecessary(serverUrl, error as ClientError);
|
||||
return {error};
|
||||
}
|
||||
};
|
||||
|
||||
export const searchProfiles = async (serverUrl: string, term: string, options: SearchUserOptions, fetchOnly = false) => {
|
||||
export const searchProfiles = async (serverUrl: string, term: string, options: any = {}, fetchOnly = false) => {
|
||||
let client: Client;
|
||||
try {
|
||||
client = NetworkManager.getClient(serverUrl);
|
||||
@@ -594,7 +563,6 @@ export const searchProfiles = async (serverUrl: string, term: string, options: S
|
||||
|
||||
return {data: users};
|
||||
} catch (error) {
|
||||
logError('searchProfiles', error);
|
||||
forceLogoutIfNecessary(serverUrl, error as ClientError);
|
||||
return {error};
|
||||
}
|
||||
@@ -671,7 +639,7 @@ export async function updateAllUsersSince(serverUrl: string, since: number, fetc
|
||||
modelsToBatch.push(...models);
|
||||
}
|
||||
|
||||
await operator.batchRecords(modelsToBatch, 'updateAllUsersSince');
|
||||
await operator.batchRecords(modelsToBatch);
|
||||
}
|
||||
} catch {
|
||||
// Do nothing
|
||||
@@ -707,7 +675,7 @@ export async function updateUsersNoLongerVisible(serverUrl: string, prepareRecor
|
||||
}
|
||||
}
|
||||
if (models.length && !prepareRecordsOnly) {
|
||||
serverDatabase.operator.batchRecords(models, 'updateUsersNoLongerVisible');
|
||||
serverDatabase.operator.batchRecords(models);
|
||||
}
|
||||
} catch (error) {
|
||||
forceLogoutIfNecessary(serverUrl, error as ClientError);
|
||||
@@ -947,7 +915,7 @@ export const fetchTeamAndChannelMembership = async (serverUrl: string, userId: s
|
||||
}
|
||||
|
||||
const models = await Promise.all(modelPromises);
|
||||
await operator.batchRecords(models.flat(), 'fetchTeamAndChannelMembership');
|
||||
await operator.batchRecords(models.flat());
|
||||
return {error: undefined};
|
||||
} catch (error) {
|
||||
return {error};
|
||||
|
||||
@@ -96,7 +96,7 @@ export async function handleCategoryOrderUpdatedEvent(serverUrl: string, msg: We
|
||||
c.sortOrder = order.findIndex(findOrder);
|
||||
});
|
||||
});
|
||||
await operator.batchRecords(categories, 'handleCategoryOrderUpdatedEvent');
|
||||
await operator.batchRecords(categories);
|
||||
}
|
||||
} catch (e) {
|
||||
logError('Category WS: handleCategoryOrderUpdatedEvent', e, msg);
|
||||
|
||||
@@ -57,7 +57,7 @@ export async function handleChannelCreatedEvent(serverUrl: string, msg: any) {
|
||||
}
|
||||
}
|
||||
}
|
||||
operator.batchRecords(models, 'handleChannelCreatedEvent');
|
||||
operator.batchRecords(models);
|
||||
} catch {
|
||||
// do nothing
|
||||
}
|
||||
@@ -109,7 +109,7 @@ export async function handleChannelUpdatedEvent(serverUrl: string, msg: any) {
|
||||
if (infoModel.model) {
|
||||
models.push(...infoModel.model);
|
||||
}
|
||||
operator.batchRecords(models, 'handleChannelUpdatedEvent');
|
||||
operator.batchRecords(models);
|
||||
} catch {
|
||||
// Do nothing
|
||||
}
|
||||
@@ -165,7 +165,7 @@ export async function handleChannelMemberUpdatedEvent(serverUrl: string, msg: an
|
||||
if (rolesRequest.roles?.length) {
|
||||
models.push(...await operator.handleRole({roles: rolesRequest.roles, prepareRecordsOnly: true}));
|
||||
}
|
||||
operator.batchRecords(models, 'handleChannelMemberUpdatedEvent');
|
||||
operator.batchRecords(models);
|
||||
} catch {
|
||||
// do nothing
|
||||
}
|
||||
@@ -235,7 +235,7 @@ export async function handleDirectAddedEvent(serverUrl: string, msg: WebSocketMe
|
||||
models.push(...userModels);
|
||||
}
|
||||
|
||||
operator.batchRecords(models, 'handleDirectAddedEvent');
|
||||
operator.batchRecords(models);
|
||||
} catch {
|
||||
// do nothing
|
||||
}
|
||||
@@ -267,7 +267,7 @@ export async function handleUserAddedToChannelEvent(serverUrl: string, msg: any)
|
||||
const prepareModels = await Promise.all(prepare);
|
||||
const flattenedModels = prepareModels.flat();
|
||||
if (flattenedModels?.length > 0) {
|
||||
await operator.batchRecords(flattenedModels, 'handleUserAddedToChannelEvent - prepareMyChannelsForTeam');
|
||||
await operator.batchRecords(flattenedModels);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,7 +308,7 @@ export async function handleUserAddedToChannelEvent(serverUrl: string, msg: any)
|
||||
}
|
||||
|
||||
if (models.length) {
|
||||
await operator.batchRecords(models, 'handleUserAddedToChannelEvent');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
|
||||
await fetchChannelStats(serverUrl, channelId, false);
|
||||
@@ -356,7 +356,7 @@ export async function handleUserRemovedFromChannelEvent(serverUrl: string, msg:
|
||||
}
|
||||
}
|
||||
|
||||
operator.batchRecords(models, 'handleUserRemovedFromChannelEvent');
|
||||
operator.batchRecords(models);
|
||||
} catch (error) {
|
||||
logDebug('cannot handle user removed from channel websocket event', error);
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ async function doReconnect(serverUrl: string) {
|
||||
await handleEntryAfterLoadNavigation(serverUrl, teamData.memberships || [], chData?.memberships || [], currentTeam?.id || '', currentChannel?.id || '', initialTeamId, initialChannelId);
|
||||
|
||||
const dt = Date.now();
|
||||
await operator.batchRecords(models, 'doReconnect');
|
||||
await operator.batchRecords(models);
|
||||
logInfo('WEBSOCKET RECONNECT MODELS BATCHING TOOK', `${Date.now() - dt}ms`);
|
||||
setTeamLoading(serverUrl, false);
|
||||
|
||||
|
||||
@@ -176,7 +176,7 @@ export async function handleNewPostEvent(serverUrl: string, msg: WebSocketMessag
|
||||
|
||||
models.push(...postModels);
|
||||
|
||||
operator.batchRecords(models, 'handleNewPostEvent');
|
||||
operator.batchRecords(models);
|
||||
}
|
||||
|
||||
export async function handlePostEdited(serverUrl: string, msg: WebSocketMessage) {
|
||||
@@ -220,7 +220,7 @@ export async function handlePostEdited(serverUrl: string, msg: WebSocketMessage)
|
||||
});
|
||||
models.push(...postModels);
|
||||
|
||||
operator.batchRecords(models, 'handlePostEdited');
|
||||
operator.batchRecords(models);
|
||||
}
|
||||
|
||||
export async function handlePostDeleted(serverUrl: string, msg: WebSocketMessage) {
|
||||
@@ -263,7 +263,7 @@ export async function handlePostDeleted(serverUrl: string, msg: WebSocketMessage
|
||||
}
|
||||
|
||||
if (models.length) {
|
||||
await operator.batchRecords(models, 'handlePostDeleted');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
} catch {
|
||||
// Do nothing
|
||||
|
||||
@@ -107,7 +107,7 @@ async function handleSavePostAdded(serverUrl: string, preferences: PreferenceTyp
|
||||
return;
|
||||
}
|
||||
|
||||
const savedPosts = preferences.filter((p) => p.category === Preferences.CATEGORIES.SAVED_POST);
|
||||
const savedPosts = preferences.filter((p) => p.category === Preferences.CATEGORY_SAVED_POST);
|
||||
for await (const saved of savedPosts) {
|
||||
const post = await getPostById(database, saved.name);
|
||||
if (!post) {
|
||||
|
||||
@@ -66,7 +66,7 @@ export async function handleUserRoleUpdatedEvent(serverUrl: string, msg: WebSock
|
||||
models.push(user);
|
||||
}
|
||||
|
||||
await operator.batchRecords(models, 'handleUserRoleUpdatedEvent');
|
||||
await operator.batchRecords(models);
|
||||
}
|
||||
|
||||
export async function handleTeamMemberRoleUpdatedEvent(serverUrl: string, msg: WebSocketMessage): Promise<void> {
|
||||
@@ -118,7 +118,7 @@ export async function handleTeamMemberRoleUpdatedEvent(serverUrl: string, msg: W
|
||||
});
|
||||
models.push(...teamMembership);
|
||||
|
||||
await operator.batchRecords(models, 'handleTeamMemberRoleUpdatedEvent');
|
||||
await operator.batchRecords(models);
|
||||
} catch {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@@ -162,5 +162,5 @@ const fetchAndStoreJoinedTeamInfo = async (serverUrl: string, operator: ServerDa
|
||||
}
|
||||
|
||||
const models = await Promise.all(modelPromises);
|
||||
await operator.batchRecords(models.flat(), 'fetchAndStoreJoinedTeamInfo');
|
||||
await operator.batchRecords(models.flat());
|
||||
};
|
||||
|
||||
@@ -10,7 +10,7 @@ import DatabaseManager from '@database/manager';
|
||||
import {getTeammateNameDisplaySetting} from '@helpers/api/preference';
|
||||
import WebsocketManager from '@managers/websocket_manager';
|
||||
import {queryChannelsByTypes, queryUserChannelsByTypes} from '@queries/servers/channel';
|
||||
import {queryDisplayNamePreferences} from '@queries/servers/preference';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
import {getConfig, getLicense} from '@queries/servers/system';
|
||||
import {getCurrentUser} from '@queries/servers/user';
|
||||
import {displayUsername} from '@utils/user';
|
||||
@@ -71,7 +71,7 @@ export async function handleUserUpdatedEvent(serverUrl: string, msg: WebSocketMe
|
||||
modelsToBatch.push(...userModel);
|
||||
|
||||
try {
|
||||
await operator.batchRecords(modelsToBatch, 'handleUserUpdatedEvent');
|
||||
await operator.batchRecords(modelsToBatch);
|
||||
} catch {
|
||||
// do nothing
|
||||
}
|
||||
@@ -91,7 +91,7 @@ export async function handleUserTypingEvent(serverUrl: string, msg: WebSocketMes
|
||||
const {users, existingUsers} = await fetchUsersByIds(serverUrl, [msg.data.user_id]);
|
||||
const user = users?.[0] || existingUsers?.[0];
|
||||
|
||||
const namePreference = await queryDisplayNamePreferences(database, Preferences.NAME_NAME_FORMAT).fetch();
|
||||
const namePreference = await queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.NAME_NAME_FORMAT).fetch();
|
||||
const teammateDisplayNameSetting = getTeammateNameDisplaySetting(namePreference, config.LockTeammateNameDisplay, config.TeammateNameDisplay, license);
|
||||
const currentUser = await getCurrentUser(database);
|
||||
const username = displayUsername(user, currentUser?.locale, teammateDisplayNameSetting);
|
||||
|
||||
@@ -3,15 +3,13 @@
|
||||
|
||||
import {buildQueryString} from '@utils/helpers';
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientAppsMix {
|
||||
executeAppCall: <Res = unknown>(call: AppCallRequest, trackAsSubmit: boolean) => Promise<AppCallResponse<Res>>;
|
||||
getAppsBindings: (userID: string, channelID: string, teamID: string) => Promise<AppBinding[]>;
|
||||
}
|
||||
|
||||
const ClientApps = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
executeAppCall = async (call: AppCallRequest, trackAsSubmit: boolean) => {
|
||||
const ClientApps = (superclass: any) => class extends superclass {
|
||||
executeAppCall = async <Res = unknown>(call: AppCallRequest, trackAsSubmit: boolean): Promise<AppCallResponse<Res>> => {
|
||||
const callCopy = {
|
||||
...call,
|
||||
context: {
|
||||
|
||||
@@ -26,7 +26,6 @@ export default class ClientBase {
|
||||
requestHeaders: {[x: string]: string} = {};
|
||||
serverVersion = '';
|
||||
urlVersion = '/api/v4';
|
||||
enableLogging = false;
|
||||
|
||||
constructor(apiClient: APIClientInterface, serverUrl: string, bearerToken?: string, csrfToken?: string) {
|
||||
this.apiClient = apiClient;
|
||||
@@ -46,10 +45,6 @@ export default class ClientBase {
|
||||
}
|
||||
}
|
||||
|
||||
getBaseRoute() {
|
||||
return this.apiClient.baseUrl || '';
|
||||
}
|
||||
|
||||
getAbsoluteUrl(baseUrl?: string) {
|
||||
if (typeof baseUrl !== 'string' || !baseUrl.startsWith('/')) {
|
||||
return baseUrl;
|
||||
@@ -308,7 +303,7 @@ export default class ClientBase {
|
||||
}
|
||||
|
||||
if (response.ok) {
|
||||
return returnDataOnly ? (response.data || {}) : response;
|
||||
return returnDataOnly ? response.data : response;
|
||||
}
|
||||
|
||||
throw new ClientError(this.apiClient.baseUrl, {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientCategoriesMix {
|
||||
getCategories: (userId: string, teamId: string) => Promise<CategoriesWithOrder>;
|
||||
getCategoriesOrder: (userId: string, teamId: string) => Promise<string[]>;
|
||||
@@ -10,7 +8,7 @@ export interface ClientCategoriesMix {
|
||||
updateChannelCategories: (userId: string, teamId: string, categories: CategoryWithChannels[]) => Promise<CategoriesWithOrder>;
|
||||
}
|
||||
|
||||
const ClientCategories = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
const ClientCategories = (superclass: any) => class extends superclass {
|
||||
getCategories = async (userId: string, teamId: string) => {
|
||||
return this.doFetch(
|
||||
`${this.getCategoriesRoute(userId, teamId)}`,
|
||||
|
||||
@@ -5,8 +5,6 @@ import {buildQueryString} from '@utils/helpers';
|
||||
|
||||
import {PER_PAGE_DEFAULT} from './constants';
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientChannelsMix {
|
||||
getAllChannels: (page?: number, perPage?: number, notAssociatedToGroup?: string, excludeDefaultChannels?: boolean, includeTotalCount?: boolean) => Promise<any>;
|
||||
createChannel: (channel: Channel) => Promise<Channel>;
|
||||
@@ -42,11 +40,9 @@ export interface ClientChannelsMix {
|
||||
searchChannels: (teamId: string, term: string) => Promise<Channel[]>;
|
||||
searchArchivedChannels: (teamId: string, term: string) => Promise<Channel[]>;
|
||||
searchAllChannels: (term: string, teamIds: string[], archivedOnly?: boolean) => Promise<Channel[]>;
|
||||
updateChannelMemberSchemeRoles: (channelId: string, userId: string, isSchemeUser: boolean, isSchemeAdmin: boolean) => Promise<any>;
|
||||
getMemberInChannel: (channelId: string, userId: string) => Promise<any>;
|
||||
}
|
||||
|
||||
const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
const ClientChannels = (superclass: any) => class extends superclass {
|
||||
getAllChannels = async (page = 0, perPage = PER_PAGE_DEFAULT, notAssociatedToGroup = '', excludeDefaultChannels = false, includeTotalCount = false) => {
|
||||
const queryData = {
|
||||
page,
|
||||
@@ -62,7 +58,7 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
||||
};
|
||||
|
||||
createChannel = async (channel: Channel) => {
|
||||
this.analytics?.trackAPI('api_channels_create', {team_id: channel.team_id});
|
||||
this.analytics.trackAPI('api_channels_create', {team_id: channel.team_id});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelsRoute()}`,
|
||||
@@ -71,7 +67,7 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
||||
};
|
||||
|
||||
createDirectChannel = async (userIds: string[]) => {
|
||||
this.analytics?.trackAPI('api_channels_create_direct');
|
||||
this.analytics.trackAPI('api_channels_create_direct');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelsRoute()}/direct`,
|
||||
@@ -80,7 +76,7 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
||||
};
|
||||
|
||||
createGroupChannel = async (userIds: string[]) => {
|
||||
this.analytics?.trackAPI('api_channels_create_group');
|
||||
this.analytics.trackAPI('api_channels_create_group');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelsRoute()}/group`,
|
||||
@@ -89,7 +85,7 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
||||
};
|
||||
|
||||
deleteChannel = async (channelId: string) => {
|
||||
this.analytics?.trackAPI('api_channels_delete', {channel_id: channelId});
|
||||
this.analytics.trackAPI('api_channels_delete', {channel_id: channelId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channelId)}`,
|
||||
@@ -98,7 +94,7 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
||||
};
|
||||
|
||||
unarchiveChannel = async (channelId: string) => {
|
||||
this.analytics?.trackAPI('api_channels_unarchive', {channel_id: channelId});
|
||||
this.analytics.trackAPI('api_channels_unarchive', {channel_id: channelId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channelId)}/restore`,
|
||||
@@ -107,7 +103,7 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
||||
};
|
||||
|
||||
updateChannel = async (channel: Channel) => {
|
||||
this.analytics?.trackAPI('api_channels_update', {channel_id: channel.id});
|
||||
this.analytics.trackAPI('api_channels_update', {channel_id: channel.id});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channel.id)}`,
|
||||
@@ -120,7 +116,7 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
||||
};
|
||||
|
||||
updateChannelPrivacy = async (channelId: string, privacy: any) => {
|
||||
this.analytics?.trackAPI('api_channels_update_privacy', {channel_id: channelId, privacy});
|
||||
this.analytics.trackAPI('api_channels_update_privacy', {channel_id: channelId, privacy});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channelId)}/privacy`,
|
||||
@@ -129,7 +125,7 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
||||
};
|
||||
|
||||
patchChannel = async (channelId: string, channelPatch: Partial<Channel>) => {
|
||||
this.analytics?.trackAPI('api_channels_patch', {channel_id: channelId});
|
||||
this.analytics.trackAPI('api_channels_patch', {channel_id: channelId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channelId)}/patch`,
|
||||
@@ -138,7 +134,7 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
||||
};
|
||||
|
||||
updateChannelNotifyProps = async (props: ChannelNotifyProps & {channel_id: string; user_id: string}) => {
|
||||
this.analytics?.trackAPI('api_users_update_channel_notifications', {channel_id: props.channel_id});
|
||||
this.analytics.trackAPI('api_users_update_channel_notifications', {channel_id: props.channel_id});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelMemberRoute(props.channel_id, props.user_id)}/notify_props`,
|
||||
@@ -147,7 +143,7 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
||||
};
|
||||
|
||||
getChannel = async (channelId: string) => {
|
||||
this.analytics?.trackAPI('api_channel_get', {channel_id: channelId});
|
||||
this.analytics.trackAPI('api_channel_get', {channel_id: channelId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channelId)}`,
|
||||
@@ -163,7 +159,7 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
||||
};
|
||||
|
||||
getChannelByNameAndTeamName = async (teamName: string, channelName: string, includeDeleted = false) => {
|
||||
this.analytics?.trackAPI('api_channel_get_by_name_and_teamName', {channel_name: channelName, team_name: teamName, include_deleted: includeDeleted});
|
||||
this.analytics.trackAPI('api_channel_get_by_name_and_teamName', {channel_name: channelName, team_name: teamName, include_deleted: includeDeleted});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getTeamNameRoute(teamName)}/channels/name/${channelName}?include_deleted=${includeDeleted}`,
|
||||
@@ -245,7 +241,7 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
||||
};
|
||||
|
||||
addToChannel = async (userId: string, channelId: string, postRootId = '') => {
|
||||
this.analytics?.trackAPI('api_channels_add_member', {channel_id: channelId});
|
||||
this.analytics.trackAPI('api_channels_add_member', {channel_id: channelId});
|
||||
|
||||
const member = {user_id: userId, channel_id: channelId, post_root_id: postRootId};
|
||||
return this.doFetch(
|
||||
@@ -255,7 +251,7 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
||||
};
|
||||
|
||||
removeFromChannel = async (userId: string, channelId: string) => {
|
||||
this.analytics?.trackAPI('api_channels_remove_member', {channel_id: channelId});
|
||||
this.analytics.trackAPI('api_channels_remove_member', {channel_id: channelId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelMemberRoute(channelId, userId)}`,
|
||||
@@ -331,25 +327,6 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
||||
{method: 'post', body},
|
||||
);
|
||||
};
|
||||
|
||||
// Update a channel member's scheme_admin/scheme_user properties. Typically
|
||||
// this should either be scheme_admin=false, scheme_user=true for ordinary
|
||||
// channel member, or scheme_admin=true, scheme_user=true for a channel
|
||||
// admin.
|
||||
updateChannelMemberSchemeRoles = (channelId: string, userId: string, isSchemeUser: boolean, isSchemeAdmin: boolean) => {
|
||||
const body = {scheme_user: isSchemeUser, scheme_admin: isSchemeAdmin};
|
||||
return this.doFetch(
|
||||
`${this.getChannelMembersRoute(channelId)}/${userId}/schemeRoles`,
|
||||
{method: 'put', body},
|
||||
);
|
||||
};
|
||||
|
||||
getMemberInChannel = (channelId: string, userId: string) => {
|
||||
return this.doFetch(
|
||||
`${this.getChannelMembersRoute(channelId)}/${userId}`,
|
||||
{method: 'get'},
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
export default ClientChannels;
|
||||
|
||||
@@ -5,8 +5,6 @@ import {buildQueryString} from '@utils/helpers';
|
||||
|
||||
import {PER_PAGE_DEFAULT} from './constants';
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientEmojisMix {
|
||||
getCustomEmoji: (id: string) => Promise<CustomEmoji>;
|
||||
getCustomEmojiByName: (name: string) => Promise<CustomEmoji>;
|
||||
@@ -17,7 +15,7 @@ export interface ClientEmojisMix {
|
||||
autocompleteCustomEmoji: (name: string) => Promise<CustomEmoji[]>;
|
||||
}
|
||||
|
||||
const ClientEmojis = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
const ClientEmojis = (superclass: any) => class extends superclass {
|
||||
getCustomEmoji = async (id: string) => {
|
||||
return this.doFetch(
|
||||
`${this.getEmojisRoute()}/${id}`,
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
import {toMilliseconds} from '@utils/datetime';
|
||||
|
||||
import type ClientBase from './base';
|
||||
import type {ClientResponse, ClientResponseError, ProgressPromise, UploadRequestOptions} from '@mattermost/react-native-network-client';
|
||||
|
||||
export interface ClientFilesMix {
|
||||
@@ -23,7 +22,7 @@ export interface ClientFilesMix {
|
||||
searchFilesWithParams: (teamId: string, FileSearchParams: string) => Promise<FileSearchRequest>;
|
||||
}
|
||||
|
||||
const ClientFiles = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
const ClientFiles = (superclass: any) => class extends superclass {
|
||||
getFileUrl(fileId: string, timestamp: number) {
|
||||
let url = `${this.apiClient.baseUrl}${this.getFileRoute(fileId)}`;
|
||||
if (timestamp) {
|
||||
@@ -77,17 +76,13 @@ const ClientFiles = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
},
|
||||
timeoutInterval: toMilliseconds({minutes: 3}),
|
||||
};
|
||||
if (!file.localPath) {
|
||||
throw new Error('file does not have local path defined');
|
||||
}
|
||||
|
||||
const promise = this.apiClient.upload(url, file.localPath, options) as ProgressPromise<ClientResponse>;
|
||||
promise.progress!(onProgress).then(onComplete).catch(onError);
|
||||
return promise.cancel!;
|
||||
};
|
||||
|
||||
searchFilesWithParams = async (teamId: string, params: FileSearchParams) => {
|
||||
this.analytics?.trackAPI('api_files_search');
|
||||
this.analytics.trackAPI('api_files_search');
|
||||
const endpoint = teamId ? `${this.getTeamRoute(teamId)}/files/search` : `${this.getFilesRoute()}/search`;
|
||||
return this.doFetch(endpoint, {method: 'post', body: params});
|
||||
};
|
||||
|
||||
@@ -6,8 +6,6 @@ import {buildQueryString} from '@utils/helpers';
|
||||
import {PER_PAGE_DEFAULT} from './constants';
|
||||
import ClientError from './error';
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
type PoliciesResponse<T> = {
|
||||
policies: T[];
|
||||
total_count: number;
|
||||
@@ -27,7 +25,7 @@ export interface ClientGeneralMix {
|
||||
getRedirectLocation: (urlParam: string) => Promise<Record<string, string>>;
|
||||
}
|
||||
|
||||
const ClientGeneral = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
const ClientGeneral = (superclass: any) => class extends superclass {
|
||||
getOpenGraphMetadata = async (url: string) => {
|
||||
return this.doFetch(
|
||||
`${this.urlVersion}/opengraph`,
|
||||
@@ -51,7 +49,7 @@ const ClientGeneral = <TBase extends Constructor<ClientBase>>(superclass: TBase)
|
||||
const url = `${this.urlVersion}/logs`;
|
||||
|
||||
if (!this.enableLogging) {
|
||||
throw new ClientError(this.apiClient.baseUrl, {
|
||||
throw new ClientError(this.client.baseUrl, {
|
||||
message: 'Logging disabled.',
|
||||
url,
|
||||
});
|
||||
|
||||
@@ -5,8 +5,6 @@ import {buildQueryString} from '@utils/helpers';
|
||||
|
||||
import {PER_PAGE_DEFAULT} from './constants';
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientGroupsMix {
|
||||
getGroup: (id: string) => Promise<Group>;
|
||||
getGroups: (params: {query?: string; filterAllowReference?: boolean; page?: number; perPage?: number; since?: number; includeMemberCount?: boolean}) => Promise<Group[]>;
|
||||
@@ -18,7 +16,7 @@ export interface ClientGroupsMix {
|
||||
getAllTeamsAssociatedToGroup: (groupId: string, filterAllowReference?: boolean) => Promise<{groupTeams: GroupTeam[]}>;
|
||||
}
|
||||
|
||||
const ClientGroups = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
const ClientGroups = (superclass: any) => class extends superclass {
|
||||
getGroup = async (id: string) => {
|
||||
return this.doFetch(
|
||||
`${this.urlVersion}/groups/${id}`,
|
||||
|
||||
@@ -5,8 +5,6 @@ import {buildQueryString} from '@utils/helpers';
|
||||
|
||||
import {PER_PAGE_DEFAULT} from './constants';
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientIntegrationsMix {
|
||||
getCommandsList: (teamId: string) => Promise<Command[]>;
|
||||
getCommandAutocompleteSuggestionsList: (userInput: string, teamId: string, channelId: string, rootId?: string) => Promise<AutocompleteSuggestion[]>;
|
||||
@@ -16,7 +14,7 @@ export interface ClientIntegrationsMix {
|
||||
submitInteractiveDialog: (data: DialogSubmission) => Promise<any>;
|
||||
}
|
||||
|
||||
const ClientIntegrations = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
const ClientIntegrations = (superclass: any) => class extends superclass {
|
||||
getCommandsList = async (teamId: string) => {
|
||||
return this.doFetch(
|
||||
`${this.getCommandsRoute()}?team_id=${teamId}`,
|
||||
@@ -39,7 +37,7 @@ const ClientIntegrations = <TBase extends Constructor<ClientBase>>(superclass: T
|
||||
};
|
||||
|
||||
executeCommand = async (command: string, commandArgs = {}) => {
|
||||
this.analytics?.trackAPI('api_integrations_used');
|
||||
this.analytics.trackAPI('api_integrations_used');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getCommandsRoute()}/execute`,
|
||||
@@ -48,7 +46,7 @@ const ClientIntegrations = <TBase extends Constructor<ClientBase>>(superclass: T
|
||||
};
|
||||
|
||||
addCommand = async (command: Command) => {
|
||||
this.analytics?.trackAPI('api_integrations_created');
|
||||
this.analytics.trackAPI('api_integrations_created');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getCommandsRoute()}`,
|
||||
@@ -57,7 +55,7 @@ const ClientIntegrations = <TBase extends Constructor<ClientBase>>(superclass: T
|
||||
};
|
||||
|
||||
submitInteractiveDialog = async (data: DialogSubmission) => {
|
||||
this.analytics?.trackAPI('api_interactive_messages_dialog_submitted');
|
||||
this.analytics.trackAPI('api_interactive_messages_dialog_submitted');
|
||||
return this.doFetch(
|
||||
`${this.urlVersion}/actions/dialogs/submit`,
|
||||
{method: 'post', body: data},
|
||||
|
||||
@@ -3,13 +3,11 @@
|
||||
|
||||
import {General} from '@constants';
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientNPSMix {
|
||||
npsGiveFeedbackAction: () => Promise<Post>;
|
||||
}
|
||||
|
||||
const ClientNPS = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
const ClientNPS = (superclass: any) => class extends superclass {
|
||||
npsGiveFeedbackAction = async () => {
|
||||
return this.doFetch(
|
||||
`${this.getPluginRoute(General.NPS_PLUGIN_ID)}/api/v1/give_feedback`,
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientPluginsMix {
|
||||
getPluginsManifests: () => Promise<ClientPluginManifest[]>;
|
||||
}
|
||||
|
||||
const ClientPlugins = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
const ClientPlugins = (superclass: any) => class extends superclass {
|
||||
getPluginsManifests = async () => {
|
||||
return this.doFetch(
|
||||
`${this.getPluginsRoute()}/webapp`,
|
||||
|
||||
@@ -5,8 +5,6 @@ import {buildQueryString} from '@utils/helpers';
|
||||
|
||||
import {PER_PAGE_DEFAULT} from './constants';
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientPostsMix {
|
||||
createPost: (post: Post) => Promise<Post>;
|
||||
updatePost: (post: Post) => Promise<Post>;
|
||||
@@ -33,12 +31,12 @@ export interface ClientPostsMix {
|
||||
doPostActionWithCookie: (postId: string, actionId: string, actionCookie: string, selectedOption?: string) => Promise<any>;
|
||||
}
|
||||
|
||||
const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
const ClientPosts = (superclass: any) => class extends superclass {
|
||||
createPost = async (post: Post) => {
|
||||
this.analytics?.trackAPI('api_posts_create', {channel_id: post.channel_id});
|
||||
this.analytics.trackAPI('api_posts_create', {channel_id: post.channel_id});
|
||||
|
||||
if (post.root_id != null && post.root_id !== '') {
|
||||
this.analytics?.trackAPI('api_posts_replied', {channel_id: post.channel_id});
|
||||
this.analytics.trackAPI('api_posts_replied', {channel_id: post.channel_id});
|
||||
}
|
||||
|
||||
return this.doFetch(
|
||||
@@ -48,7 +46,7 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
updatePost = async (post: Post) => {
|
||||
this.analytics?.trackAPI('api_posts_update', {channel_id: post.channel_id});
|
||||
this.analytics.trackAPI('api_posts_update', {channel_id: post.channel_id});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getPostRoute(post.id)}`,
|
||||
@@ -64,7 +62,7 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
patchPost = async (postPatch: Partial<Post> & {id: string}) => {
|
||||
this.analytics?.trackAPI('api_posts_patch', {channel_id: postPatch.channel_id});
|
||||
this.analytics.trackAPI('api_posts_patch', {channel_id: postPatch.channel_id});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getPostRoute(postPatch.id)}/patch`,
|
||||
@@ -73,7 +71,7 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
deletePost = async (postId: string) => {
|
||||
this.analytics?.trackAPI('api_posts_delete');
|
||||
this.analytics.trackAPI('api_posts_delete');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getPostRoute(postId)}`,
|
||||
@@ -81,7 +79,7 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
);
|
||||
};
|
||||
|
||||
getPostThread = (postId: string, options: FetchPaginatedThreadOptions) => {
|
||||
getPostThread = (postId: string, options: FetchPaginatedThreadOptions): Promise<PostResponse> => {
|
||||
const {
|
||||
fetchThreads = true,
|
||||
collapsedThreads = false,
|
||||
@@ -112,7 +110,7 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
getPostsBefore = async (channelId: string, postId: string, page = 0, perPage = PER_PAGE_DEFAULT, collapsedThreads = false, collapsedThreadsExtended = false) => {
|
||||
this.analytics?.trackAPI('api_posts_get_before', {channel_id: channelId});
|
||||
this.analytics.trackAPI('api_posts_get_before', {channel_id: channelId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channelId)}/posts${buildQueryString({before: postId, page, per_page: perPage, collapsedThreads, collapsedThreadsExtended})}`,
|
||||
@@ -121,7 +119,7 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
getPostsAfter = async (channelId: string, postId: string, page = 0, perPage = PER_PAGE_DEFAULT, collapsedThreads = false, collapsedThreadsExtended = false) => {
|
||||
this.analytics?.trackAPI('api_posts_get_after', {channel_id: channelId});
|
||||
this.analytics.trackAPI('api_posts_get_after', {channel_id: channelId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channelId)}/posts${buildQueryString({after: postId, page, per_page: perPage, collapsedThreads, collapsedThreadsExtended})}`,
|
||||
@@ -137,7 +135,7 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
getSavedPosts = async (userId: string, channelId = '', teamId = '', page = 0, perPage = PER_PAGE_DEFAULT) => {
|
||||
this.analytics?.trackAPI('api_posts_get_flagged', {team_id: teamId});
|
||||
this.analytics.trackAPI('api_posts_get_flagged', {team_id: teamId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUserRoute(userId)}/posts/flagged${buildQueryString({channel_id: channelId, team_id: teamId, page, per_page: perPage})}`,
|
||||
@@ -146,7 +144,7 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
getPinnedPosts = async (channelId: string) => {
|
||||
this.analytics?.trackAPI('api_posts_get_pinned', {channel_id: channelId});
|
||||
this.analytics.trackAPI('api_posts_get_pinned', {channel_id: channelId});
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channelId)}/pinned`,
|
||||
{method: 'get'},
|
||||
@@ -154,7 +152,7 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
markPostAsUnread = async (userId: string, postId: string) => {
|
||||
this.analytics?.trackAPI('api_post_set_unread_post');
|
||||
this.analytics.trackAPI('api_post_set_unread_post');
|
||||
|
||||
// collapsed_threads_supported is not based on user preferences but to know if "CLIENT" supports CRT
|
||||
const body = JSON.stringify({collapsed_threads_supported: true});
|
||||
@@ -166,7 +164,7 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
pinPost = async (postId: string) => {
|
||||
this.analytics?.trackAPI('api_posts_pin');
|
||||
this.analytics.trackAPI('api_posts_pin');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getPostRoute(postId)}/pin`,
|
||||
@@ -175,7 +173,7 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
unpinPost = async (postId: string) => {
|
||||
this.analytics?.trackAPI('api_posts_unpin');
|
||||
this.analytics.trackAPI('api_posts_unpin');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getPostRoute(postId)}/unpin`,
|
||||
@@ -184,7 +182,7 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
addReaction = async (userId: string, postId: string, emojiName: string) => {
|
||||
this.analytics?.trackAPI('api_reactions_save', {post_id: postId});
|
||||
this.analytics.trackAPI('api_reactions_save', {post_id: postId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getReactionsRoute()}`,
|
||||
@@ -193,7 +191,7 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
removeReaction = async (userId: string, postId: string, emojiName: string) => {
|
||||
this.analytics?.trackAPI('api_reactions_delete', {post_id: postId});
|
||||
this.analytics.trackAPI('api_reactions_delete', {post_id: postId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUserRoute(userId)}/posts/${postId}/reactions/${emojiName}`,
|
||||
@@ -209,7 +207,7 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
searchPostsWithParams = async (teamId: string, params: PostSearchParams) => {
|
||||
this.analytics?.trackAPI('api_posts_search');
|
||||
this.analytics.trackAPI('api_posts_search');
|
||||
const endpoint = teamId ? `${this.getTeamRoute(teamId)}/posts/search` : `${this.getPostsRoute()}/search`;
|
||||
return this.doFetch(endpoint, {method: 'post', body: params});
|
||||
};
|
||||
@@ -224,9 +222,9 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
|
||||
doPostActionWithCookie = async (postId: string, actionId: string, actionCookie: string, selectedOption = '') => {
|
||||
if (selectedOption) {
|
||||
this.analytics?.trackAPI('api_interactive_messages_menu_selected');
|
||||
this.analytics.trackAPI('api_interactive_messages_menu_selected');
|
||||
} else {
|
||||
this.analytics?.trackAPI('api_interactive_messages_button_clicked');
|
||||
this.analytics.trackAPI('api_interactive_messages_button_clicked');
|
||||
}
|
||||
|
||||
const msg: any = {
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientPreferencesMix {
|
||||
savePreferences: (userId: string, preferences: PreferenceType[]) => Promise<any>;
|
||||
deletePreferences: (userId: string, preferences: PreferenceType[]) => Promise<any>;
|
||||
getMyPreferences: () => Promise<PreferenceType[]>;
|
||||
}
|
||||
|
||||
const ClientPreferences = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
const ClientPreferences = (superclass: any) => class extends superclass {
|
||||
savePreferences = async (userId: string, preferences: PreferenceType[]) => {
|
||||
this.analytics?.trackAPI('action_posts_flag');
|
||||
this.analytics.trackAPI('action_posts_flag');
|
||||
return this.doFetch(
|
||||
`${this.getPreferencesRoute(userId)}`,
|
||||
{method: 'put', body: preferences},
|
||||
@@ -26,7 +24,7 @@ const ClientPreferences = <TBase extends Constructor<ClientBase>>(superclass: TB
|
||||
};
|
||||
|
||||
deletePreferences = async (userId: string, preferences: PreferenceType[]) => {
|
||||
this.analytics?.trackAPI('action_posts_unflag');
|
||||
this.analytics.trackAPI('action_posts_unflag');
|
||||
return this.doFetch(
|
||||
`${this.getPreferencesRoute(userId)}/delete`,
|
||||
{method: 'post', body: preferences},
|
||||
|
||||
@@ -5,8 +5,6 @@ import {buildQueryString} from '@utils/helpers';
|
||||
|
||||
import {PER_PAGE_DEFAULT} from './constants';
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientTeamsMix {
|
||||
createTeam: (team: Team) => Promise<Team>;
|
||||
deleteTeam: (teamId: string) => Promise<any>;
|
||||
@@ -30,9 +28,9 @@ export interface ClientTeamsMix {
|
||||
getTeamIconUrl: (teamId: string, lastTeamIconUpdate: number) => string;
|
||||
}
|
||||
|
||||
const ClientTeams = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
const ClientTeams = (superclass: any) => class extends superclass {
|
||||
createTeam = async (team: Team) => {
|
||||
this.analytics?.trackAPI('api_teams_create');
|
||||
this.analytics.trackAPI('api_teams_create');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getTeamsRoute()}`,
|
||||
@@ -41,7 +39,7 @@ const ClientTeams = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
deleteTeam = async (teamId: string) => {
|
||||
this.analytics?.trackAPI('api_teams_delete');
|
||||
this.analytics.trackAPI('api_teams_delete');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getTeamRoute(teamId)}`,
|
||||
@@ -50,7 +48,7 @@ const ClientTeams = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
updateTeam = async (team: Team) => {
|
||||
this.analytics?.trackAPI('api_teams_update_name', {team_id: team.id});
|
||||
this.analytics.trackAPI('api_teams_update_name', {team_id: team.id});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getTeamRoute(team.id)}`,
|
||||
@@ -59,7 +57,7 @@ const ClientTeams = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
patchTeam = async (team: Partial<Team> & {id: string}) => {
|
||||
this.analytics?.trackAPI('api_teams_patch_name', {team_id: team.id});
|
||||
this.analytics.trackAPI('api_teams_patch_name', {team_id: team.id});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getTeamRoute(team.id)}/patch`,
|
||||
@@ -82,7 +80,7 @@ const ClientTeams = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
getTeamByName = async (teamName: string) => {
|
||||
this.analytics?.trackAPI('api_teams_get_team_by_name');
|
||||
this.analytics.trackAPI('api_teams_get_team_by_name');
|
||||
|
||||
return this.doFetch(
|
||||
this.getTeamNameRoute(teamName),
|
||||
@@ -133,7 +131,7 @@ const ClientTeams = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
addToTeam = async (teamId: string, userId: string) => {
|
||||
this.analytics?.trackAPI('api_teams_invite_members', {team_id: teamId});
|
||||
this.analytics.trackAPI('api_teams_invite_members', {team_id: teamId});
|
||||
|
||||
const member = {user_id: userId, team_id: teamId};
|
||||
return this.doFetch(
|
||||
@@ -143,7 +141,7 @@ const ClientTeams = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
addUsersToTeamGracefully = (teamId: string, userIds: string[]) => {
|
||||
this.analytics?.trackAPI('api_teams_batch_add_members', {team_id: teamId, count: userIds.length});
|
||||
this.analytics.trackAPI('api_teams_batch_add_members', {team_id: teamId, count: userIds.length});
|
||||
|
||||
const members: Array<{team_id: string; user_id: string}> = [];
|
||||
userIds.forEach((id) => members.push({team_id: teamId, user_id: id}));
|
||||
@@ -155,7 +153,7 @@ const ClientTeams = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
sendEmailInvitesToTeamGracefully = (teamId: string, emails: string[]) => {
|
||||
this.analytics?.trackAPI('api_teams_invite_members', {team_id: teamId});
|
||||
this.analytics.trackAPI('api_teams_invite_members', {team_id: teamId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getTeamRoute(teamId)}/invite/email?graceful=true`,
|
||||
@@ -172,7 +170,7 @@ const ClientTeams = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
removeFromTeam = async (teamId: string, userId: string) => {
|
||||
this.analytics?.trackAPI('api_teams_remove_members', {team_id: teamId});
|
||||
this.analytics.trackAPI('api_teams_remove_members', {team_id: teamId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getTeamMemberRoute(teamId, userId)}`,
|
||||
|
||||
@@ -5,8 +5,6 @@ import {buildQueryString, isMinimumServerVersion} from '@utils/helpers';
|
||||
|
||||
import {PER_PAGE_DEFAULT} from './constants';
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientThreadsMix {
|
||||
getThreads: (userId: string, teamId: string, before?: string, after?: string, pageSize?: number, deleted?: boolean, unread?: boolean, since?: number, totalsOnly?: boolean, serverVersion?: string) => Promise<GetUserThreadsResponse>;
|
||||
getThread: (userId: string, teamId: string, threadId: string, extended?: boolean) => Promise<any>;
|
||||
@@ -16,7 +14,7 @@ export interface ClientThreadsMix {
|
||||
updateThreadFollow: (userId: string, teamId: string, threadId: string, state: boolean) => Promise<any>;
|
||||
}
|
||||
|
||||
const ClientThreads = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
const ClientThreads = (superclass: any) => class extends superclass {
|
||||
getThreads = async (userId: string, teamId: string, before = '', after = '', pageSize = PER_PAGE_DEFAULT, deleted = false, unread = false, since = 0, totalsOnly = false, serverVersion = '') => {
|
||||
const queryStringObj: Record<string, any> = {
|
||||
extended: 'true',
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientTosMix {
|
||||
updateMyTermsOfServiceStatus: (termsOfServiceId: string, accepted: boolean) => Promise<{status: string}>;
|
||||
getTermsOfService: () => Promise<TermsOfService>;
|
||||
}
|
||||
|
||||
const ClientTos = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
const ClientTos = (superclass: any) => class extends superclass {
|
||||
updateMyTermsOfServiceStatus = async (termsOfServiceId: string, accepted: boolean) => {
|
||||
return this.doFetch(
|
||||
`${this.getUserRoute('me')}/terms_of_service`,
|
||||
|
||||
@@ -6,8 +6,6 @@ import {buildQueryString} from '@utils/helpers';
|
||||
|
||||
import {PER_PAGE_DEFAULT} from './constants';
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientUsersMix {
|
||||
createUser: (user: UserProfile, token: string, inviteId: string) => Promise<UserProfile>;
|
||||
patchMe: (userPatch: Partial<UserProfile>) => Promise<UserProfile>;
|
||||
@@ -26,7 +24,7 @@ export interface ClientUsersMix {
|
||||
getProfilesInTeam: (teamId: string, page?: number, perPage?: number, sort?: string, options?: Record<string, any>) => Promise<UserProfile[]>;
|
||||
getProfilesNotInTeam: (teamId: string, groupConstrained: boolean, page?: number, perPage?: number) => Promise<UserProfile[]>;
|
||||
getProfilesWithoutTeam: (page?: number, perPage?: number, options?: Record<string, any>) => Promise<UserProfile[]>;
|
||||
getProfilesInChannel: (channelId: string, options?: GetUsersOptions) => Promise<UserProfile[]>;
|
||||
getProfilesInChannel: (channelId: string, page?: number, perPage?: number, sort?: string) => Promise<UserProfile[]>;
|
||||
getProfilesInGroupChannels: (channelsIds: string[]) => Promise<{[x: string]: UserProfile[]}>;
|
||||
getProfilesNotInChannel: (teamId: string, channelId: string, groupConstrained: boolean, page?: number, perPage?: number) => Promise<UserProfile[]>;
|
||||
getMe: () => Promise<UserProfile>;
|
||||
@@ -39,7 +37,7 @@ export interface ClientUsersMix {
|
||||
getSessions: (userId: string) => Promise<Session[]>;
|
||||
checkUserMfa: (loginId: string) => Promise<{mfa_required: boolean}>;
|
||||
attachDevice: (deviceId: string) => Promise<any>;
|
||||
searchUsers: (term: string, options: SearchUserOptions) => Promise<UserProfile[]>;
|
||||
searchUsers: (term: string, options: any) => Promise<UserProfile[]>;
|
||||
getStatusesByIds: (userIds: string[]) => Promise<UserStatus[]>;
|
||||
getStatus: (userId: string) => Promise<UserStatus>;
|
||||
updateStatus: (status: UserStatus) => Promise<UserStatus>;
|
||||
@@ -48,9 +46,9 @@ export interface ClientUsersMix {
|
||||
removeRecentCustomStatus: (customStatus: UserCustomStatus) => Promise<{status: string}>;
|
||||
}
|
||||
|
||||
const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
const ClientUsers = (superclass: any) => class extends superclass {
|
||||
createUser = async (user: UserProfile, token: string, inviteId: string) => {
|
||||
this.analytics?.trackAPI('api_users_create');
|
||||
this.analytics.trackAPI('api_users_create');
|
||||
|
||||
const queryParams: any = {};
|
||||
|
||||
@@ -76,7 +74,7 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
patchUser = async (userPatch: Partial<UserProfile> & {id: string}) => {
|
||||
this.analytics?.trackAPI('api_users_patch');
|
||||
this.analytics.trackAPI('api_users_patch');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUserRoute(userPatch.id)}/patch`,
|
||||
@@ -85,7 +83,7 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
updateUser = async (user: UserProfile) => {
|
||||
this.analytics?.trackAPI('api_users_update');
|
||||
this.analytics.trackAPI('api_users_update');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUserRoute(user.id)}`,
|
||||
@@ -94,7 +92,7 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
demoteUserToGuest = async (userId: string) => {
|
||||
this.analytics?.trackAPI('api_users_demote_user_to_guest');
|
||||
this.analytics.trackAPI('api_users_demote_user_to_guest');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUserRoute(userId)}/demote`,
|
||||
@@ -103,7 +101,7 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
getKnownUsers = async () => {
|
||||
this.analytics?.trackAPI('api_get_known_users');
|
||||
this.analytics.trackAPI('api_get_known_users');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}/known`,
|
||||
@@ -112,7 +110,7 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
sendPasswordResetEmail = async (email: string) => {
|
||||
this.analytics?.trackAPI('api_users_send_password_reset');
|
||||
this.analytics.trackAPI('api_users_send_password_reset');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}/password/reset/send`,
|
||||
@@ -121,7 +119,7 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
setDefaultProfileImage = async (userId: string) => {
|
||||
this.analytics?.trackAPI('api_users_set_default_profile_picture');
|
||||
this.analytics.trackAPI('api_users_set_default_profile_picture');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUserRoute(userId)}/image`,
|
||||
@@ -130,10 +128,10 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
login = async (loginId: string, password: string, token = '', deviceId = '', ldapOnly = false) => {
|
||||
this.analytics?.trackAPI('api_users_login');
|
||||
this.analytics.trackAPI('api_users_login');
|
||||
|
||||
if (ldapOnly) {
|
||||
this.analytics?.trackAPI('api_users_login_ldap');
|
||||
this.analytics.trackAPI('api_users_login_ldap');
|
||||
}
|
||||
|
||||
const body: any = {
|
||||
@@ -161,7 +159,7 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
loginById = async (id: string, password: string, token = '', deviceId = '') => {
|
||||
this.analytics?.trackAPI('api_users_login');
|
||||
this.analytics.trackAPI('api_users_login');
|
||||
const body: any = {
|
||||
device_id: deviceId,
|
||||
id,
|
||||
@@ -183,7 +181,7 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
logout = async () => {
|
||||
this.analytics?.trackAPI('api_users_logout');
|
||||
this.analytics.trackAPI('api_users_logout');
|
||||
|
||||
const response = await this.doFetch(
|
||||
`${this.getUsersRoute()}/logout`,
|
||||
@@ -194,7 +192,7 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
getProfiles = async (page = 0, perPage = PER_PAGE_DEFAULT, options = {}) => {
|
||||
this.analytics?.trackAPI('api_profiles_get');
|
||||
this.analytics.trackAPI('api_profiles_get');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}${buildQueryString({page, per_page: perPage, ...options})}`,
|
||||
@@ -203,7 +201,7 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
getProfilesByIds = async (userIds: string[], options = {}) => {
|
||||
this.analytics?.trackAPI('api_profiles_get_by_ids');
|
||||
this.analytics.trackAPI('api_profiles_get_by_ids');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}/ids${buildQueryString(options)}`,
|
||||
@@ -212,7 +210,7 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
getProfilesByUsernames = async (usernames: string[]) => {
|
||||
this.analytics?.trackAPI('api_profiles_get_by_usernames');
|
||||
this.analytics.trackAPI('api_profiles_get_by_usernames');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}/usernames`,
|
||||
@@ -221,7 +219,7 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
getProfilesInTeam = async (teamId: string, page = 0, perPage = PER_PAGE_DEFAULT, sort = '', options = {}) => {
|
||||
this.analytics?.trackAPI('api_profiles_get_in_team', {team_id: teamId, sort});
|
||||
this.analytics.trackAPI('api_profiles_get_in_team', {team_id: teamId, sort});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}${buildQueryString({...options, in_team: teamId, page, per_page: perPage, sort})}`,
|
||||
@@ -230,7 +228,7 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
getProfilesNotInTeam = async (teamId: string, groupConstrained: boolean, page = 0, perPage = PER_PAGE_DEFAULT) => {
|
||||
this.analytics?.trackAPI('api_profiles_get_not_in_team', {team_id: teamId, group_constrained: groupConstrained});
|
||||
this.analytics.trackAPI('api_profiles_get_not_in_team', {team_id: teamId, group_constrained: groupConstrained});
|
||||
|
||||
const queryStringObj: any = {not_in_team: teamId, page, per_page: perPage};
|
||||
if (groupConstrained) {
|
||||
@@ -244,7 +242,7 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
getProfilesWithoutTeam = async (page = 0, perPage = PER_PAGE_DEFAULT, options = {}) => {
|
||||
this.analytics?.trackAPI('api_profiles_get_without_team');
|
||||
this.analytics.trackAPI('api_profiles_get_without_team');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}${buildQueryString({...options, without_team: 1, page, per_page: perPage})}`,
|
||||
@@ -252,10 +250,10 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
);
|
||||
};
|
||||
|
||||
getProfilesInChannel = async (channelId: string, options: GetUsersOptions) => {
|
||||
this.analytics?.trackAPI('api_profiles_get_in_channel', {channel_id: channelId});
|
||||
getProfilesInChannel = async (channelId: string, page = 0, perPage = PER_PAGE_DEFAULT, sort = '') => {
|
||||
this.analytics.trackAPI('api_profiles_get_in_channel', {channel_id: channelId});
|
||||
|
||||
const queryStringObj = {in_channel: channelId, ...options};
|
||||
const queryStringObj = {in_channel: channelId, page, per_page: perPage, sort};
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}${buildQueryString(queryStringObj)}`,
|
||||
{method: 'get'},
|
||||
@@ -263,7 +261,7 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
getProfilesInGroupChannels = async (channelsIds: string[]) => {
|
||||
this.analytics?.trackAPI('api_profiles_get_in_group_channels', {channelsIds});
|
||||
this.analytics.trackAPI('api_profiles_get_in_group_channels', {channelsIds});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}/group_channels`,
|
||||
@@ -272,7 +270,7 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
getProfilesNotInChannel = async (teamId: string, channelId: string, groupConstrained: boolean, page = 0, perPage = PER_PAGE_DEFAULT) => {
|
||||
this.analytics?.trackAPI('api_profiles_get_not_in_channel', {team_id: teamId, channel_id: channelId, group_constrained: groupConstrained});
|
||||
this.analytics.trackAPI('api_profiles_get_not_in_channel', {team_id: teamId, channel_id: channelId, group_constrained: groupConstrained});
|
||||
|
||||
const queryStringObj: any = {in_team: teamId, not_in_channel: channelId, page, per_page: perPage};
|
||||
if (groupConstrained) {
|
||||
@@ -374,7 +372,7 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
||||
};
|
||||
|
||||
searchUsers = async (term: string, options: any) => {
|
||||
this.analytics?.trackAPI('api_search_users');
|
||||
this.analytics.trackAPI('api_search_users');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}/search`,
|
||||
|
||||
@@ -8,7 +8,7 @@ import {switchMap} from 'rxjs/operators';
|
||||
|
||||
import {Preferences} from '@constants';
|
||||
import {queryAllCustomEmojis} from '@queries/servers/custom_emoji';
|
||||
import {queryEmojiPreferences} from '@queries/servers/preference';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
import {observeConfigBooleanValue} from '@queries/servers/system';
|
||||
|
||||
import EmojiSuggestion from './emoji_suggestion';
|
||||
@@ -21,9 +21,12 @@ const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
|
||||
const isCustomEmojisEnabled = observeConfigBooleanValue(database, 'EnableCustomEmoji');
|
||||
return {
|
||||
customEmojis: isCustomEmojisEnabled.pipe(
|
||||
switchMap((enabled) => (enabled ? queryAllCustomEmojis(database).observe() : of$(emptyEmojiList))),
|
||||
switchMap((enabled) => (enabled ?
|
||||
queryAllCustomEmojis(database).observe() :
|
||||
of$(emptyEmojiList)),
|
||||
),
|
||||
),
|
||||
skinTone: queryEmojiPreferences(database, Preferences.EMOJI_SKINTONE).
|
||||
skinTone: queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_EMOJI, Preferences.EMOJI_SKINTONE).
|
||||
observeWithColumns(['value']).pipe(
|
||||
switchMap((prefs) => of$(prefs?.[0]?.value ?? 'default')),
|
||||
),
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {StyleProp, TextStyle, View, ViewStyle} from 'react-native';
|
||||
import {StyleProp, TextStyle, View, ViewProps, ViewStyle} from 'react-native';
|
||||
|
||||
import FormattedText from '@components/formatted_text';
|
||||
import {useTheme} from '@context/theme';
|
||||
@@ -36,7 +36,7 @@ export type SectionText = {
|
||||
values?: MessageDescriptor;
|
||||
}
|
||||
|
||||
export type BlockProps = {
|
||||
export type BlockProps = ViewProps & {
|
||||
children: React.ReactNode;
|
||||
disableFooter?: boolean;
|
||||
disableHeader?: boolean;
|
||||
@@ -56,12 +56,16 @@ const Block = ({
|
||||
headerStyles,
|
||||
headerText,
|
||||
footerStyles,
|
||||
...props
|
||||
}: BlockProps) => {
|
||||
const theme = useTheme();
|
||||
const styles = getStyleSheet(theme);
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View
|
||||
style={styles.container}
|
||||
{...props}
|
||||
>
|
||||
{(headerText && !disableHeader) &&
|
||||
<FormattedText
|
||||
defaultMessage={headerText.defaultMessage}
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useCallback} from 'react';
|
||||
import {defineMessages, useIntl} from 'react-intl';
|
||||
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
import OptionBox from '@components/option_box';
|
||||
import {Screens} from '@constants';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {t} from '@i18n';
|
||||
import {goToScreen, showModal} from '@screens/navigation';
|
||||
import {changeOpacity} from '@utils/theme';
|
||||
|
||||
import type {StyleProp, ViewStyle} from 'react-native';
|
||||
|
||||
type Props = {
|
||||
channelId: string;
|
||||
containerStyle?: StyleProp<ViewStyle>;
|
||||
displayName: string;
|
||||
inModal?: boolean;
|
||||
testID?: string;
|
||||
}
|
||||
|
||||
const messages = defineMessages({
|
||||
title: {
|
||||
id: t('mobile.channel_add_people.title'),
|
||||
defaultMessage: 'Add Members',
|
||||
},
|
||||
boxText: {
|
||||
id: t('intro.add_people'),
|
||||
defaultMessage: 'Add People',
|
||||
},
|
||||
});
|
||||
|
||||
const {CHANNEL_ADD_PEOPLE} = Screens;
|
||||
const closeButtonId = 'close-add-people';
|
||||
|
||||
const AddPeopleBox = ({channelId, containerStyle, displayName, inModal, testID}: Props) => {
|
||||
const {formatMessage} = useIntl();
|
||||
const theme = useTheme();
|
||||
|
||||
const onAddPeople = useCallback(async () => {
|
||||
const closeButton = await CompassIcon.getImageSourceSync('close', 24, theme.sidebarHeaderTextColor);
|
||||
const title = formatMessage(messages.title);
|
||||
|
||||
const options = {
|
||||
topBar: {
|
||||
subtitle: {
|
||||
color: changeOpacity(theme.sidebarHeaderTextColor, 0.72),
|
||||
text: displayName,
|
||||
},
|
||||
leftButtons: inModal ? [] : [{
|
||||
id: closeButtonId,
|
||||
icon: closeButton,
|
||||
testID: 'close.channel_info.button',
|
||||
}],
|
||||
},
|
||||
};
|
||||
|
||||
if (inModal) {
|
||||
goToScreen(CHANNEL_ADD_PEOPLE, title, {channelId}, options);
|
||||
return;
|
||||
}
|
||||
|
||||
showModal(CHANNEL_ADD_PEOPLE, title, {channelId, closeButtonId}, options);
|
||||
}, [formatMessage, channelId, inModal]);
|
||||
|
||||
return (
|
||||
<OptionBox
|
||||
containerStyle={containerStyle}
|
||||
iconName='account-plus-outline'
|
||||
onPress={onAddPeople}
|
||||
testID={testID}
|
||||
text={formatMessage(messages.boxText)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddPeopleBox;
|
||||
@@ -1,28 +1,44 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
|
||||
import withObservables from '@nozbe/with-observables';
|
||||
import {of as of$} from 'rxjs';
|
||||
import {switchMap} from 'rxjs/operators';
|
||||
import React, {useCallback} from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
|
||||
import {observeChannel} from '@queries/servers/channel';
|
||||
import OptionBox from '@components/option_box';
|
||||
import {Screens} from '@constants';
|
||||
import {dismissBottomSheet, goToScreen, showModal} from '@screens/navigation';
|
||||
|
||||
import AddPeopleBox from './add_people_box';
|
||||
import type {StyleProp, ViewStyle} from 'react-native';
|
||||
|
||||
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||
|
||||
type Props = WithDatabaseArgs & {
|
||||
type Props = {
|
||||
channelId: string;
|
||||
containerStyle?: StyleProp<ViewStyle>;
|
||||
inModal?: boolean;
|
||||
testID?: string;
|
||||
}
|
||||
|
||||
const enhanced = withObservables(['channelId'], ({channelId, database}: Props) => {
|
||||
const channel = observeChannel(database, channelId);
|
||||
const displayName = channel.pipe(switchMap((c) => of$(c?.displayName)));
|
||||
const AddPeopleBox = ({channelId, containerStyle, inModal, testID}: Props) => {
|
||||
const intl = useIntl();
|
||||
|
||||
return {
|
||||
displayName,
|
||||
};
|
||||
});
|
||||
const onAddPeople = useCallback(async () => {
|
||||
const title = intl.formatMessage({id: 'intro.add_people', defaultMessage: 'Add People'});
|
||||
if (inModal) {
|
||||
goToScreen(Screens.CHANNEL_ADD_PEOPLE, title, {channelId});
|
||||
return;
|
||||
}
|
||||
await dismissBottomSheet();
|
||||
showModal(Screens.CHANNEL_ADD_PEOPLE, title, {channelId});
|
||||
}, [intl, channelId, inModal]);
|
||||
|
||||
export default withDatabase(enhanced(AddPeopleBox));
|
||||
return (
|
||||
<OptionBox
|
||||
containerStyle={containerStyle}
|
||||
iconName='account-plus-outline'
|
||||
onPress={onAddPeople}
|
||||
testID={testID}
|
||||
text={intl.formatMessage({id: 'intro.add_people', defaultMessage: 'Add People'})}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddPeopleBox;
|
||||
|
||||
@@ -5,7 +5,6 @@ import React, {useCallback} from 'react';
|
||||
import {StyleSheet, View} from 'react-native';
|
||||
|
||||
import ChannelInfoStartButton from '@calls/components/channel_info_start';
|
||||
import AddPeopleBox from '@components/channel_actions/add_people_box';
|
||||
import CopyChannelLinkBox from '@components/channel_actions/copy_channel_link_box';
|
||||
import FavoriteBox from '@components/channel_actions/favorite_box';
|
||||
import MutedBox from '@components/channel_actions/mute_box';
|
||||
@@ -14,6 +13,8 @@ import {useServerUrl} from '@context/server';
|
||||
import {dismissBottomSheet} from '@screens/navigation';
|
||||
import {isTypeDMorGM} from '@utils/channel';
|
||||
|
||||
// import AddPeopleBox from '@components/channel_actions/add_people_box';
|
||||
|
||||
type Props = {
|
||||
channelId: string;
|
||||
channelType?: ChannelType;
|
||||
@@ -69,6 +70,7 @@ const ChannelActions = ({channelId, channelType, inModal = false, dismissChannel
|
||||
testID={`${testID}.set_header.action`}
|
||||
/>
|
||||
}
|
||||
{/* Add back in after MM-47655 is resolved. https://mattermost.atlassian.net/browse/MM-47655
|
||||
{!isDM &&
|
||||
<AddPeopleBox
|
||||
channelId={channelId}
|
||||
@@ -76,6 +78,7 @@ const ChannelActions = ({channelId, channelType, inModal = false, dismissChannel
|
||||
testID={`${testID}.add_people.action`}
|
||||
/>
|
||||
}
|
||||
*/}
|
||||
{!isDM && !callsEnabled &&
|
||||
<>
|
||||
<View style={styles.separator}/>
|
||||
|
||||
@@ -23,7 +23,7 @@ type Props = {
|
||||
testID?: string;
|
||||
}
|
||||
|
||||
const LeaveChannelLabel = ({canLeave, channelId, displayName, isOptionItem, type, testID}: Props) => {
|
||||
const LeaveChanelLabel = ({canLeave, channelId, displayName, isOptionItem, type, testID}: Props) => {
|
||||
const intl = useIntl();
|
||||
const serverUrl = useServerUrl();
|
||||
const isTablet = useIsTablet();
|
||||
@@ -183,4 +183,4 @@ const LeaveChannelLabel = ({canLeave, channelId, displayName, isOptionItem, type
|
||||
);
|
||||
};
|
||||
|
||||
export default LeaveChannelLabel;
|
||||
export default LeaveChanelLabel;
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
|
||||
import withObservables from '@nozbe/with-observables';
|
||||
import {of as of$} from 'rxjs';
|
||||
import {switchMap} from 'rxjs/operators';
|
||||
|
||||
import {queryUsersById} from '@queries/servers/user';
|
||||
|
||||
import ManageMembersLabel from './manage_members_label';
|
||||
|
||||
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||
|
||||
type OwnProps = WithDatabaseArgs & {
|
||||
isDefaultChannel: boolean;
|
||||
userId: string;
|
||||
}
|
||||
|
||||
const enhanced = withObservables(['isDefaultChannel', 'userId'], ({isDefaultChannel, userId, database}: OwnProps) => {
|
||||
const users = queryUsersById(database, [userId]).observe();
|
||||
const canRemoveUser = users.pipe(
|
||||
switchMap((u) => {
|
||||
return of$(!isDefaultChannel || (isDefaultChannel && u[0].isGuest));
|
||||
}),
|
||||
);
|
||||
|
||||
return {
|
||||
canRemoveUser,
|
||||
};
|
||||
});
|
||||
|
||||
export default withDatabase(enhanced(ManageMembersLabel));
|
||||
@@ -1,152 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useCallback} from 'react';
|
||||
import {defineMessages, useIntl} from 'react-intl';
|
||||
import {Alert, DeviceEventEmitter} from 'react-native';
|
||||
|
||||
import {fetchChannelStats, removeMemberFromChannel, updateChannelMemberSchemeRoles} from '@actions/remote/channel';
|
||||
import OptionItem from '@components/option_item';
|
||||
import {Events, Members} from '@constants';
|
||||
import {useServerUrl} from '@context/server';
|
||||
import {t} from '@i18n';
|
||||
import {dismissBottomSheet} from '@screens/navigation';
|
||||
import {alertErrorWithFallback} from '@utils/draft';
|
||||
|
||||
import type {ManageOptionsTypes} from '@constants/members';
|
||||
|
||||
const {MAKE_CHANNEL_ADMIN, MAKE_CHANNEL_MEMBER, REMOVE_USER} = Members.ManageOptions;
|
||||
|
||||
const messages = defineMessages({
|
||||
role_change_error: {
|
||||
id: t('mobile.manage_members.change_role.error'),
|
||||
defaultMessage: 'An error occurred while trying to update the role. Please check your connection and try again.',
|
||||
},
|
||||
make_channel_admin: {
|
||||
id: t('mobile.manage_members.make_channel_admin'),
|
||||
defaultMessage: 'Make Channel Admin',
|
||||
},
|
||||
make_channel_member: {
|
||||
id: t('mobile.manage_members.make_channel_member'),
|
||||
defaultMessage: 'Make Channel Member',
|
||||
},
|
||||
remove_title: {
|
||||
id: t('mobile.manage_members.remove_member'),
|
||||
defaultMessage: 'Remove From Channel',
|
||||
},
|
||||
remove_message: {
|
||||
id: t('mobile.manage_members.message'),
|
||||
defaultMessage: 'Are you sure you want to remove the selected member from the channel?',
|
||||
},
|
||||
remove_cancel: {
|
||||
id: t('mobile.manage_members.cancel'),
|
||||
defaultMessage: 'Cancel',
|
||||
},
|
||||
remove_confirm: {
|
||||
id: t('mobile.manage_members.remove'),
|
||||
defaultMessage: 'Remove',
|
||||
},
|
||||
});
|
||||
|
||||
type Props = {
|
||||
canRemoveUser: boolean;
|
||||
channelId: string;
|
||||
manageOption: ManageOptionsTypes;
|
||||
testID?: string;
|
||||
userId: string;
|
||||
}
|
||||
|
||||
const ManageMembersLabel = ({canRemoveUser, channelId, manageOption, testID, userId}: Props) => {
|
||||
const intl = useIntl();
|
||||
const {formatMessage} = intl;
|
||||
const serverUrl = useServerUrl();
|
||||
|
||||
const handleRemoveUser = useCallback(async () => {
|
||||
removeMemberFromChannel(serverUrl, channelId, userId);
|
||||
fetchChannelStats(serverUrl, channelId, false);
|
||||
await dismissBottomSheet();
|
||||
DeviceEventEmitter.emit(Events.REMOVE_USER_FROM_CHANNEL, userId);
|
||||
}, [channelId, serverUrl, userId]);
|
||||
|
||||
const removeFromChannel = useCallback(() => {
|
||||
Alert.alert(
|
||||
formatMessage(messages.remove_title),
|
||||
formatMessage(messages.remove_message),
|
||||
[{
|
||||
text: formatMessage(messages.remove_cancel),
|
||||
style: 'cancel',
|
||||
}, {
|
||||
text: formatMessage(messages.remove_confirm),
|
||||
style: 'destructive',
|
||||
onPress: handleRemoveUser,
|
||||
}], {cancelable: false},
|
||||
);
|
||||
}, [formatMessage, handleRemoveUser]);
|
||||
|
||||
const updateChannelMemberSchemeRole = useCallback(async (schemeAdmin: boolean) => {
|
||||
const result = await updateChannelMemberSchemeRoles(serverUrl, channelId, userId, true, schemeAdmin);
|
||||
if (result.error) {
|
||||
alertErrorWithFallback(intl, result.error, messages.role_change_error);
|
||||
}
|
||||
await dismissBottomSheet();
|
||||
DeviceEventEmitter.emit(Events.MANAGE_USER_CHANGE_ROLE, {userId, schemeAdmin});
|
||||
}, [channelId, userId, intl, serverUrl]);
|
||||
|
||||
const onAction = useCallback(() => {
|
||||
switch (manageOption) {
|
||||
case REMOVE_USER:
|
||||
removeFromChannel();
|
||||
break;
|
||||
case MAKE_CHANNEL_ADMIN:
|
||||
updateChannelMemberSchemeRole(true);
|
||||
break;
|
||||
case MAKE_CHANNEL_MEMBER:
|
||||
updateChannelMemberSchemeRole(false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}, [manageOption, removeFromChannel, updateChannelMemberSchemeRole]);
|
||||
|
||||
let actionText;
|
||||
let icon;
|
||||
let isDestructive = false;
|
||||
switch (manageOption) {
|
||||
case REMOVE_USER:
|
||||
actionText = (formatMessage(messages.remove_title));
|
||||
icon = 'trash-can-outline';
|
||||
isDestructive = true;
|
||||
break;
|
||||
case MAKE_CHANNEL_ADMIN:
|
||||
actionText = formatMessage(messages.make_channel_admin);
|
||||
icon = 'account-outline';
|
||||
break;
|
||||
case MAKE_CHANNEL_MEMBER:
|
||||
actionText = formatMessage(messages.make_channel_member);
|
||||
icon = 'account-outline';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (manageOption === REMOVE_USER && !canRemoveUser) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!actionText) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<OptionItem
|
||||
action={onAction}
|
||||
destructive={isDestructive}
|
||||
icon={icon}
|
||||
label={actionText}
|
||||
testID={testID}
|
||||
type='default'
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ManageMembersLabel;
|
||||
@@ -10,7 +10,7 @@ import {switchMap, distinctUntilChanged} from 'rxjs/operators';
|
||||
import {observeChannelsWithCalls} from '@calls/state';
|
||||
import {General} from '@constants';
|
||||
import {withServerUrl} from '@context/server';
|
||||
import {observeChannelSettings, observeMyChannel, queryChannelMembers} from '@queries/servers/channel';
|
||||
import {observeIsMutedSetting, observeMyChannel, queryChannelMembers} from '@queries/servers/channel';
|
||||
import {queryDraft} from '@queries/servers/drafts';
|
||||
import {observeCurrentChannelId, observeCurrentUserId} from '@queries/servers/system';
|
||||
import {observeTeam} from '@queries/servers/team';
|
||||
@@ -19,7 +19,6 @@ import ChannelItem from './channel_item';
|
||||
|
||||
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||
import type ChannelModel from '@typings/database/models/servers/channel';
|
||||
import type MyChannelModel from '@typings/database/models/servers/my_channel';
|
||||
|
||||
type EnhanceProps = WithDatabaseArgs & {
|
||||
channel: ChannelModel;
|
||||
@@ -27,8 +26,6 @@ type EnhanceProps = WithDatabaseArgs & {
|
||||
serverUrl?: string;
|
||||
}
|
||||
|
||||
const observeIsMutedSetting = (mc: MyChannelModel) => observeChannelSettings(mc.database, mc.id).pipe(switchMap((s) => of$(s?.notifyProps?.mark_unread === General.MENTION)));
|
||||
|
||||
const enhance = withObservables(['channel', 'showTeamName'], ({
|
||||
channel,
|
||||
database,
|
||||
@@ -53,7 +50,7 @@ const enhance = withObservables(['channel', 'showTeamName'], ({
|
||||
if (!mc) {
|
||||
return of$(false);
|
||||
}
|
||||
return observeIsMutedSetting(mc);
|
||||
return observeIsMutedSetting(database, mc.id);
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@@ -12,8 +12,9 @@ import {switchMap} from 'rxjs/operators';
|
||||
import FormattedDate from '@components/formatted_date';
|
||||
import FormattedText from '@components/formatted_text';
|
||||
import FormattedTime from '@components/formatted_time';
|
||||
import {getDisplayNamePreferenceAsBool} from '@helpers/api/preference';
|
||||
import {queryDisplayNamePreferences} from '@queries/servers/preference';
|
||||
import {Preferences} from '@constants';
|
||||
import {getPreferenceAsBool} from '@helpers/api/preference';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
import {observeCurrentUser} from '@queries/servers/user';
|
||||
import {getCurrentMomentForTimezone} from '@utils/helpers';
|
||||
import {makeStyleSheetFromTheme} from '@utils/theme';
|
||||
@@ -133,10 +134,10 @@ const CustomStatusExpiry = ({currentUser, isMilitaryTime, showPrefix, showTimeCo
|
||||
|
||||
const enhanced = withObservables([], ({database}: WithDatabaseArgs) => ({
|
||||
currentUser: observeCurrentUser(database),
|
||||
isMilitaryTime: queryDisplayNamePreferences(database).
|
||||
isMilitaryTime: queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS).
|
||||
observeWithColumns(['value']).pipe(
|
||||
switchMap(
|
||||
(preferences) => of$(getDisplayNamePreferenceAsBool(preferences, 'use_military_time')),
|
||||
(preferences) => of$(getPreferenceAsBool(preferences, Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time', false)),
|
||||
),
|
||||
),
|
||||
}));
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {defineMessages, useIntl} from 'react-intl';
|
||||
import {Text, View} from 'react-native';
|
||||
|
||||
import {popToRoot} from '@app/screens/navigation';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {t} from '@i18n';
|
||||
import Button from '@screens/bottom_sheet/button';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {typography} from '@utils/typography';
|
||||
|
||||
const messages = defineMessages({
|
||||
no_more_members_title: {
|
||||
id: t('mobile.no_more_members.title'),
|
||||
defaultMessage: 'No other members to add',
|
||||
},
|
||||
no_more_members_subtext: {
|
||||
id: t('mobile.no_more_members.subtext'),
|
||||
defaultMessage: 'All team members are already in this channel.',
|
||||
},
|
||||
go_back: {
|
||||
id: t('mobile.no_more_members.go_back'),
|
||||
defaultMessage: 'Go back',
|
||||
},
|
||||
});
|
||||
|
||||
const getStyleFromTheme = makeStyleSheetFromTheme((theme: Theme) => {
|
||||
return {
|
||||
container: {
|
||||
alignItems: 'center' as const,
|
||||
flexGrow: 1,
|
||||
height: '100%',
|
||||
justifyContent: 'center' as const,
|
||||
},
|
||||
buttonContainer: {
|
||||
marginTop: 24,
|
||||
},
|
||||
title: {
|
||||
color: theme.centerChannelColor,
|
||||
...typography('Heading', 400, 'SemiBold'),
|
||||
},
|
||||
subText: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.72),
|
||||
marginTop: 8,
|
||||
...typography('Body', 200),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const NoResultsWithButton = () => {
|
||||
const theme = useTheme();
|
||||
const styles = getStyleFromTheme(theme);
|
||||
|
||||
const {formatMessage} = useIntl();
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.title}>{formatMessage(messages.no_more_members_title)}</Text>
|
||||
<Text style={styles.subText}>{formatMessage(messages.no_more_members_subtext)}</Text>
|
||||
<View style={styles.buttonContainer}>
|
||||
<Button
|
||||
onPress={popToRoot}
|
||||
icon={'arrow-left'}
|
||||
text={formatMessage(messages.go_back)}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default NoResultsWithButton;
|
||||
@@ -6,8 +6,8 @@ import withObservables from '@nozbe/with-observables';
|
||||
import {of as of$, combineLatest} from 'rxjs';
|
||||
|
||||
import {Preferences} from '@constants';
|
||||
import {getDisplayNamePreferenceAsBool} from '@helpers/api/preference';
|
||||
import {queryDisplayNamePreferences} from '@queries/servers/preference';
|
||||
import {getPreferenceAsBool} from '@helpers/api/preference';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
import {observeConfigBooleanValue} from '@queries/servers/system';
|
||||
|
||||
import Opengraph from './opengraph';
|
||||
@@ -22,10 +22,10 @@ const enhance = withObservables(
|
||||
}
|
||||
|
||||
const linkPreviewsConfig = observeConfigBooleanValue(database, 'EnableLinkPreviews');
|
||||
const linkPreviewPreference = queryDisplayNamePreferences(database, Preferences.LINK_PREVIEW_DISPLAY).
|
||||
const linkPreviewPreference = queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.LINK_PREVIEW_DISPLAY).
|
||||
observeWithColumns(['value']);
|
||||
const showLinkPreviews = combineLatest([linkPreviewsConfig, linkPreviewPreference], (cfg, pref) => {
|
||||
const previewsEnabled = getDisplayNamePreferenceAsBool(pref, Preferences.LINK_PREVIEW_DISPLAY, true);
|
||||
const previewsEnabled = getPreferenceAsBool(pref, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.LINK_PREVIEW_DISPLAY, true);
|
||||
return of$(previewsEnabled && cfg);
|
||||
});
|
||||
|
||||
|
||||
@@ -6,9 +6,10 @@ import withObservables from '@nozbe/with-observables';
|
||||
import {of as of$} from 'rxjs';
|
||||
import {map, switchMap} from 'rxjs/operators';
|
||||
|
||||
import {getDisplayNamePreferenceAsBool} from '@helpers/api/preference';
|
||||
import {Preferences} from '@constants';
|
||||
import {getPreferenceAsBool} from '@helpers/api/preference';
|
||||
import {observePost, observePostAuthor, queryPostReplies} from '@queries/servers/post';
|
||||
import {queryDisplayNamePreferences} from '@queries/servers/preference';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
import {observeConfigBooleanValue} from '@queries/servers/system';
|
||||
import {observeTeammateNameDisplay, observeUser} from '@queries/servers/user';
|
||||
|
||||
@@ -25,12 +26,12 @@ type HeaderInputProps = {
|
||||
const withHeaderProps = withObservables(
|
||||
['post', 'differentThreadSequence'],
|
||||
({post, database, differentThreadSequence}: WithDatabaseArgs & HeaderInputProps) => {
|
||||
const preferences = queryDisplayNamePreferences(database).
|
||||
const preferences = queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS).
|
||||
observeWithColumns(['value']);
|
||||
const author = observePostAuthor(database, post);
|
||||
const enablePostUsernameOverride = observeConfigBooleanValue(database, 'EnablePostUsernameOverride');
|
||||
const isTimezoneEnabled = observeConfigBooleanValue(database, 'ExperimentalTimezone');
|
||||
const isMilitaryTime = preferences.pipe(map((prefs) => getDisplayNamePreferenceAsBool(prefs, 'use_military_time')));
|
||||
const isMilitaryTime = preferences.pipe(map((prefs) => getPreferenceAsBool(prefs, Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time', false)));
|
||||
const teammateNameDisplay = observeTeammateNameDisplay(database);
|
||||
const commentCount = queryPostReplies(database, post.rootId || post.id).observeCount();
|
||||
const isCustomStatusEnabled = observeConfigBooleanValue(database, 'EnableCustomUserStatuses');
|
||||
|
||||
@@ -102,7 +102,7 @@ const withPost = withObservables(
|
||||
const isEphemeral = of$(isPostEphemeral(post));
|
||||
|
||||
if (post.props?.add_channel_member && isPostEphemeral(post)) {
|
||||
isPostAddChannelMember = observeCanManageChannelMembers(database, post.channelId, currentUser);
|
||||
isPostAddChannelMember = observeCanManageChannelMembers(database, post, currentUser);
|
||||
}
|
||||
|
||||
let highlightReplyBar = of$(false);
|
||||
|
||||
@@ -6,8 +6,9 @@ import withObservables from '@nozbe/with-observables';
|
||||
import {of as of$} from 'rxjs';
|
||||
import {switchMap} from 'rxjs/operators';
|
||||
|
||||
import {Preferences} from '@constants';
|
||||
import {observePost, queryPostReplies} from '@queries/servers/post';
|
||||
import {querySavedPostsPreferences} from '@queries/servers/preference';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
|
||||
import ThreadOverview from './thread_overview';
|
||||
|
||||
@@ -18,7 +19,7 @@ const enhanced = withObservables(
|
||||
({database, rootId}: WithDatabaseArgs & {rootId: string}) => {
|
||||
return {
|
||||
rootPost: observePost(database, rootId),
|
||||
isSaved: querySavedPostsPreferences(database, rootId).
|
||||
isSaved: queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_SAVED_POST, rootId).
|
||||
observeWithColumns(['value']).
|
||||
pipe(
|
||||
switchMap((pref) => of$(Boolean(pref[0]?.value === 'true'))),
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {GestureResponderEvent, StyleSheet, Text, View} from 'react-native';
|
||||
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
import TouchableWithFeedback from '@components/touchable_with_feedback';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {buttonBackgroundStyle, buttonTextStyle} from '@utils/buttonStyles';
|
||||
import {changeOpacity} from '@utils/theme';
|
||||
|
||||
type Props = {
|
||||
disabled?: boolean;
|
||||
onPress?: (e: GestureResponderEvent) => void;
|
||||
icon?: string;
|
||||
testID?: string;
|
||||
text?: string;
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
button: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
},
|
||||
icon_container: {
|
||||
width: 24,
|
||||
height: 24,
|
||||
top: -1,
|
||||
marginRight: 4,
|
||||
},
|
||||
});
|
||||
|
||||
export default function Button({disabled = false, onPress, icon, testID, text}: Props) {
|
||||
const theme = useTheme();
|
||||
|
||||
const buttonType = disabled ? 'disabled' : 'default';
|
||||
const styleButtonText = buttonTextStyle(theme, 'lg', 'primary', buttonType);
|
||||
const styleButtonBackground = buttonBackgroundStyle(theme, 'lg', 'primary', buttonType);
|
||||
|
||||
const iconColor = disabled ? changeOpacity(theme.centerChannelColor, 0.32) : theme.buttonColor;
|
||||
|
||||
return (
|
||||
<TouchableWithFeedback
|
||||
onPress={onPress}
|
||||
type='opacity'
|
||||
style={[styles.button, styleButtonBackground]}
|
||||
testID={testID}
|
||||
>
|
||||
{icon && (
|
||||
<View style={styles.icon_container}>
|
||||
<CompassIcon
|
||||
size={24}
|
||||
name={icon}
|
||||
color={iconColor}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
{text && (
|
||||
<Text
|
||||
style={styleButtonText}
|
||||
>{text}</Text>
|
||||
)}
|
||||
|
||||
</TouchableWithFeedback>
|
||||
);
|
||||
}
|
||||
@@ -11,9 +11,9 @@ import Toast from '@components/toast';
|
||||
import {General} from '@constants';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {useIsTablet, useKeyboardHeightWithDuration} from '@hooks/device';
|
||||
import Button from '@screens/bottom_sheet/button';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
||||
import Button from './button';
|
||||
import SelectedUser from './selected_user';
|
||||
|
||||
type Props = {
|
||||
|
||||
@@ -9,8 +9,9 @@ import {map} from 'rxjs/operators';
|
||||
|
||||
import FormattedText from '@components/formatted_text';
|
||||
import FormattedTime from '@components/formatted_time';
|
||||
import {getDisplayNamePreferenceAsBool} from '@helpers/api/preference';
|
||||
import {queryDisplayNamePreferences} from '@queries/servers/preference';
|
||||
import {Preferences} from '@constants';
|
||||
import {getPreferenceAsBool} from '@helpers/api/preference';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
import {observeConfigBooleanValue} from '@queries/servers/system';
|
||||
import {observeCurrentUser} from '@queries/servers/user';
|
||||
import {makeStyleSheetFromTheme} from '@utils/theme';
|
||||
@@ -79,11 +80,11 @@ const SystemHeader = ({isMilitaryTime, isTimezoneEnabled, createAt, theme, user}
|
||||
};
|
||||
|
||||
const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
|
||||
const preferences = queryDisplayNamePreferences(database, 'use_military_time').
|
||||
const preferences = queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time').
|
||||
observeWithColumns(['value']);
|
||||
const isTimezoneEnabled = observeConfigBooleanValue(database, 'ExperimentalTimezone');
|
||||
const isMilitaryTime = preferences.pipe(
|
||||
map((prefs) => getDisplayNamePreferenceAsBool(prefs, 'use_military_time')),
|
||||
map((prefs) => getPreferenceAsBool(prefs, Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time', false)),
|
||||
);
|
||||
const user = observeCurrentUser(database);
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ const withTeams = withObservables([], ({database}: WithDatabaseArgs) => {
|
||||
const teamIds = queryJoinedTeams(database).observe().pipe(
|
||||
map((ts) => ts.map((t) => ({id: t.id, displayName: t.displayName}))),
|
||||
);
|
||||
const order = queryPreferencesByCategoryAndName(database, Preferences.CATEGORIES.TEAMS_ORDER).
|
||||
const order = queryPreferencesByCategoryAndName(database, Preferences.TEAMS_ORDER).
|
||||
observeWithColumns(['value']).pipe(
|
||||
switchMap((p) => (p.length ? of$(p[0].value.split(',')) : of$([]))),
|
||||
);
|
||||
|
||||
@@ -7,8 +7,8 @@ import {of as of$} from 'rxjs';
|
||||
import {switchMap} from 'rxjs/operators';
|
||||
|
||||
import Preferences from '@constants/preferences';
|
||||
import {getSidebarPreferenceAsBool} from '@helpers/api/preference';
|
||||
import {querySidebarPreferences} from '@queries/servers/preference';
|
||||
import {getPreferenceAsBool} from '@helpers/api/preference';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
import {observeCurrentChannelId, observeCurrentTeamId, observeOnlyUnreads} from '@queries/servers/system';
|
||||
import {observeUnreadsAndMentionsInTeam} from '@queries/servers/thread';
|
||||
|
||||
@@ -22,10 +22,10 @@ const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
|
||||
|
||||
return {
|
||||
currentChannelId: observeCurrentChannelId(database),
|
||||
groupUnreadsSeparately: querySidebarPreferences(database, Preferences.CHANNEL_SIDEBAR_GROUP_UNREADS).
|
||||
groupUnreadsSeparately: queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_SIDEBAR_SETTINGS, Preferences.CHANNEL_SIDEBAR_GROUP_UNREADS).
|
||||
observeWithColumns(['value']).
|
||||
pipe(
|
||||
switchMap((prefs: PreferenceModel[]) => of$(getSidebarPreferenceAsBool(prefs, Preferences.CHANNEL_SIDEBAR_GROUP_UNREADS))),
|
||||
switchMap((prefs: PreferenceModel[]) => of$(getPreferenceAsBool(prefs, Preferences.CATEGORY_SIDEBAR_SETTINGS, Preferences.CHANNEL_SIDEBAR_GROUP_UNREADS, false))),
|
||||
),
|
||||
onlyUnreads: observeOnlyUnreads(database),
|
||||
unreadsAndMentions: currentTeamId.pipe(
|
||||
|
||||
@@ -928,506 +928,3 @@ exports[`components/channel_list_row should show results no tutorial 1`] = `
|
||||
</View>
|
||||
</RCTScrollView>
|
||||
`;
|
||||
|
||||
exports[`components/channel_list_row should show results no tutorial 2 users 1`] = `
|
||||
<RCTScrollView
|
||||
ListEmptyComponent={[Function]}
|
||||
ListFooterComponent={[Function]}
|
||||
contentContainerStyle={
|
||||
{
|
||||
"flexGrow": 1,
|
||||
}
|
||||
}
|
||||
data={
|
||||
[
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"auth_service": "",
|
||||
"create_at": 1111,
|
||||
"delete_at": 0,
|
||||
"email": "john@doe.com",
|
||||
"first_name": "",
|
||||
"id": "1",
|
||||
"last_name": "",
|
||||
"locale": "",
|
||||
"nickname": "",
|
||||
"notify_props": {
|
||||
"channel": "true",
|
||||
"comments": "never",
|
||||
"desktop": "mention",
|
||||
"desktop_sound": "true",
|
||||
"email": "true",
|
||||
"first_name": "true",
|
||||
"mention_keys": "",
|
||||
"push": "mention",
|
||||
"push_status": "away",
|
||||
},
|
||||
"position": "",
|
||||
"roles": "",
|
||||
"update_at": 1111,
|
||||
"username": "johndoe",
|
||||
},
|
||||
],
|
||||
"first": true,
|
||||
"id": "J",
|
||||
},
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"auth_service": "",
|
||||
"create_at": 1111,
|
||||
"delete_at": 0,
|
||||
"email": "rocky@doe.com",
|
||||
"first_name": "",
|
||||
"id": "2",
|
||||
"last_name": "",
|
||||
"locale": "",
|
||||
"nickname": "",
|
||||
"notify_props": {
|
||||
"channel": "true",
|
||||
"comments": "never",
|
||||
"desktop": "mention",
|
||||
"desktop_sound": "true",
|
||||
"email": "true",
|
||||
"first_name": "true",
|
||||
"mention_keys": "",
|
||||
"push": "mention",
|
||||
"push_status": "away",
|
||||
},
|
||||
"position": "",
|
||||
"roles": "",
|
||||
"update_at": 1111,
|
||||
"username": "rocky",
|
||||
},
|
||||
],
|
||||
"first": false,
|
||||
"id": "R",
|
||||
},
|
||||
]
|
||||
}
|
||||
getItem={[Function]}
|
||||
getItemCount={[Function]}
|
||||
initialNumToRender={15}
|
||||
keyExtractor={[Function]}
|
||||
keyboardDismissMode="on-drag"
|
||||
keyboardShouldPersistTaps="always"
|
||||
maxToRenderPerBatch={16}
|
||||
onContentSizeChange={[Function]}
|
||||
onEndReached={[Function]}
|
||||
onLayout={[Function]}
|
||||
onMomentumScrollBegin={[Function]}
|
||||
onMomentumScrollEnd={[Function]}
|
||||
onScroll={[Function]}
|
||||
onScrollBeginDrag={[Function]}
|
||||
onScrollEndDrag={[Function]}
|
||||
removeClippedSubviews={true}
|
||||
renderItem={[Function]}
|
||||
scrollEventThrottle={60}
|
||||
stickyHeaderIndices={[]}
|
||||
style={
|
||||
{
|
||||
"backgroundColor": "#ffffff",
|
||||
"flex": 1,
|
||||
}
|
||||
}
|
||||
testID="UserListRow.section_list"
|
||||
>
|
||||
<View>
|
||||
<View
|
||||
onFocusCapture={[Function]}
|
||||
onLayout={[Function]}
|
||||
style={null}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
{
|
||||
"backgroundColor": "#ffffff",
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
{
|
||||
"backgroundColor": "rgba(63,67,80,0.08)",
|
||||
"height": 24,
|
||||
"justifyContent": "center",
|
||||
"paddingLeft": 16,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
{
|
||||
"color": "#3f4350",
|
||||
"fontFamily": "OpenSans-SemiBold",
|
||||
"fontSize": 12,
|
||||
"fontWeight": "600",
|
||||
"lineHeight": 16,
|
||||
}
|
||||
}
|
||||
>
|
||||
J
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
onFocusCapture={[Function]}
|
||||
onLayout={[Function]}
|
||||
style={null}
|
||||
>
|
||||
<View
|
||||
onMoveShouldSetResponder={[Function]}
|
||||
onMoveShouldSetResponderCapture={[Function]}
|
||||
onResponderEnd={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderReject={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderStart={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
onStartShouldSetResponderCapture={[Function]}
|
||||
>
|
||||
<View
|
||||
accessibilityValue={
|
||||
{
|
||||
"max": undefined,
|
||||
"min": undefined,
|
||||
"now": undefined,
|
||||
"text": undefined,
|
||||
}
|
||||
}
|
||||
accessible={true}
|
||||
focusable={true}
|
||||
onClick={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
{
|
||||
"flex": 1,
|
||||
"flexDirection": "row",
|
||||
"height": 58,
|
||||
"overflow": "hidden",
|
||||
"paddingHorizontal": 20,
|
||||
}
|
||||
}
|
||||
testID="create_direct_message.user_list.user_item.1"
|
||||
>
|
||||
<View
|
||||
style={
|
||||
[
|
||||
{
|
||||
"alignItems": "center",
|
||||
"color": "#3f4350",
|
||||
"flexDirection": "row",
|
||||
},
|
||||
{
|
||||
"opacity": 1,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
{
|
||||
"borderRadius": 21.5,
|
||||
"height": 42,
|
||||
"width": 42,
|
||||
}
|
||||
}
|
||||
testID="create_direct_message.user_list.user_item.1.profile_picture"
|
||||
>
|
||||
<Icon
|
||||
name="account-outline"
|
||||
size={24}
|
||||
style={
|
||||
{
|
||||
"color": "rgba(63,67,80,0.48)",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
style={
|
||||
[
|
||||
{
|
||||
"flex": 1,
|
||||
"flexDirection": "column",
|
||||
"justifyContent": "center",
|
||||
"paddingHorizontal": 10,
|
||||
},
|
||||
{
|
||||
"opacity": 1,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
{
|
||||
"flexDirection": "row",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
ellipsizeMode="tail"
|
||||
numberOfLines={1}
|
||||
style={
|
||||
{
|
||||
"color": "#3f4350",
|
||||
"fontFamily": "OpenSans",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
"height": 24,
|
||||
"lineHeight": 24,
|
||||
"maxWidth": "80%",
|
||||
}
|
||||
}
|
||||
testID="create_direct_message.user_list.user_item.1.display_name"
|
||||
>
|
||||
johndoe
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
style={
|
||||
{
|
||||
"alignItems": "center",
|
||||
"justifyContent": "center",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Icon
|
||||
color="rgba(63,67,80,0.32)"
|
||||
name="circle-outline"
|
||||
size={28}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
onFocusCapture={[Function]}
|
||||
onLayout={[Function]}
|
||||
style={null}
|
||||
/>
|
||||
<View
|
||||
onFocusCapture={[Function]}
|
||||
onLayout={[Function]}
|
||||
style={null}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
{
|
||||
"backgroundColor": "#ffffff",
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
{
|
||||
"backgroundColor": "rgba(63,67,80,0.08)",
|
||||
"height": 24,
|
||||
"justifyContent": "center",
|
||||
"paddingLeft": 16,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
{
|
||||
"color": "#3f4350",
|
||||
"fontFamily": "OpenSans-SemiBold",
|
||||
"fontSize": 12,
|
||||
"fontWeight": "600",
|
||||
"lineHeight": 16,
|
||||
}
|
||||
}
|
||||
>
|
||||
R
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
onFocusCapture={[Function]}
|
||||
onLayout={[Function]}
|
||||
style={null}
|
||||
>
|
||||
<View
|
||||
onMoveShouldSetResponder={[Function]}
|
||||
onMoveShouldSetResponderCapture={[Function]}
|
||||
onResponderEnd={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderReject={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderStart={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
onStartShouldSetResponderCapture={[Function]}
|
||||
>
|
||||
<View
|
||||
accessibilityValue={
|
||||
{
|
||||
"max": undefined,
|
||||
"min": undefined,
|
||||
"now": undefined,
|
||||
"text": undefined,
|
||||
}
|
||||
}
|
||||
accessible={true}
|
||||
focusable={true}
|
||||
onClick={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
{
|
||||
"flex": 1,
|
||||
"flexDirection": "row",
|
||||
"height": 58,
|
||||
"overflow": "hidden",
|
||||
"paddingHorizontal": 20,
|
||||
}
|
||||
}
|
||||
testID="create_direct_message.user_list.user_item.2"
|
||||
>
|
||||
<View
|
||||
style={
|
||||
[
|
||||
{
|
||||
"alignItems": "center",
|
||||
"color": "#3f4350",
|
||||
"flexDirection": "row",
|
||||
},
|
||||
{
|
||||
"opacity": 1,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
{
|
||||
"borderRadius": 21.5,
|
||||
"height": 42,
|
||||
"width": 42,
|
||||
}
|
||||
}
|
||||
testID="create_direct_message.user_list.user_item.2.profile_picture"
|
||||
>
|
||||
<Icon
|
||||
name="account-outline"
|
||||
size={24}
|
||||
style={
|
||||
{
|
||||
"color": "rgba(63,67,80,0.48)",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
style={
|
||||
[
|
||||
{
|
||||
"flex": 1,
|
||||
"flexDirection": "column",
|
||||
"justifyContent": "center",
|
||||
"paddingHorizontal": 10,
|
||||
},
|
||||
{
|
||||
"opacity": 1,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
{
|
||||
"flexDirection": "row",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
ellipsizeMode="tail"
|
||||
numberOfLines={1}
|
||||
style={
|
||||
{
|
||||
"color": "#3f4350",
|
||||
"fontFamily": "OpenSans",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
"height": 24,
|
||||
"lineHeight": 24,
|
||||
"maxWidth": "80%",
|
||||
}
|
||||
}
|
||||
testID="create_direct_message.user_list.user_item.2.display_name"
|
||||
>
|
||||
rocky
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
style={
|
||||
{
|
||||
"alignItems": "center",
|
||||
"justifyContent": "center",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Icon
|
||||
color="rgba(63,67,80,0.32)"
|
||||
name="circle-outline"
|
||||
size={28}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
onFocusCapture={[Function]}
|
||||
onLayout={[Function]}
|
||||
style={null}
|
||||
/>
|
||||
<View
|
||||
onLayout={[Function]}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
{
|
||||
"alignItems": "center",
|
||||
"flex": 1,
|
||||
"justifyContent": "center",
|
||||
}
|
||||
}
|
||||
>
|
||||
<ActivityIndicator
|
||||
color="#1c58d9"
|
||||
size="large"
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</RCTScrollView>
|
||||
`;
|
||||
|
||||
@@ -38,34 +38,6 @@ describe('components/channel_list_row', () => {
|
||||
push_status: 'away',
|
||||
},
|
||||
};
|
||||
|
||||
const user2: UserProfile = {
|
||||
id: '2',
|
||||
create_at: 1111,
|
||||
update_at: 1111,
|
||||
delete_at: 0,
|
||||
username: 'rocky',
|
||||
auth_service: '',
|
||||
email: 'rocky@doe.com',
|
||||
nickname: '',
|
||||
first_name: '',
|
||||
last_name: '',
|
||||
position: '',
|
||||
roles: '',
|
||||
locale: '',
|
||||
notify_props: {
|
||||
channel: 'true',
|
||||
comments: 'never',
|
||||
desktop: 'mention',
|
||||
desktop_sound: 'true',
|
||||
email: 'true',
|
||||
first_name: 'true',
|
||||
mention_keys: '',
|
||||
push: 'mention',
|
||||
push_status: 'away',
|
||||
},
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
const server = await TestHelper.setupServerDatabase();
|
||||
database = server.database;
|
||||
@@ -119,30 +91,6 @@ describe('components/channel_list_row', () => {
|
||||
expect(wrapper.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should show results no tutorial 2 users', () => {
|
||||
const wrapper = renderWithEverything(
|
||||
<UserList
|
||||
profiles={[user, user2]}
|
||||
testID='UserListRow'
|
||||
currentUserId={'1'}
|
||||
teammateNameDisplay={'johndoe'}
|
||||
handleSelectProfile={() => {
|
||||
// noop
|
||||
}}
|
||||
fetchMore={() => {
|
||||
// noop
|
||||
}}
|
||||
loading={true}
|
||||
selectedIds={{}}
|
||||
showNoResults={true}
|
||||
tutorialWatched={true}
|
||||
/>,
|
||||
{database},
|
||||
);
|
||||
|
||||
expect(wrapper.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should show results and tutorial', () => {
|
||||
const wrapper = renderWithEverything(
|
||||
<UserList
|
||||
|
||||
@@ -2,19 +2,17 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useCallback, useMemo} from 'react';
|
||||
import {defineMessages, IntlShape, useIntl} from 'react-intl';
|
||||
import {useIntl} from 'react-intl';
|
||||
import {FlatList, Keyboard, ListRenderItemInfo, Platform, SectionList, SectionListData, Text, View} from 'react-native';
|
||||
|
||||
import {storeProfile} from '@actions/local/user';
|
||||
import Loading from '@components/loading';
|
||||
import NoResultsWithButton from '@components/no_results_with_button';
|
||||
import NoResultsWithTerm from '@components/no_results_with_term';
|
||||
import UserListRow from '@components/user_list_row';
|
||||
import {General, Screens} from '@constants';
|
||||
import {useServerUrl} from '@context/server';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {useKeyboardHeight} from '@hooks/device';
|
||||
import {t} from '@i18n';
|
||||
import {openAsBottomSheet} from '@screens/navigation';
|
||||
import {
|
||||
changeOpacity,
|
||||
@@ -22,23 +20,9 @@ import {
|
||||
} from '@utils/theme';
|
||||
import {typography} from '@utils/typography';
|
||||
|
||||
type UserProfileWithChannelAdmin = UserProfile & {scheme_admin?: boolean}
|
||||
type RenderItemType = ListRenderItemInfo<UserProfileWithChannelAdmin> & {section?: SectionListData<UserProfileWithChannelAdmin>}
|
||||
|
||||
const INITIAL_BATCH_TO_RENDER = 15;
|
||||
const SCROLL_EVENT_THROTTLE = 60;
|
||||
|
||||
const messages = defineMessages({
|
||||
admins: {
|
||||
id: t('mobile.manage_members.section_title_admins'),
|
||||
defaultMessage: 'CHANNEL ADMINS',
|
||||
},
|
||||
members: {
|
||||
id: t('mobile.manage_members.section_title_members'),
|
||||
defaultMessage: 'MEMBERS',
|
||||
},
|
||||
});
|
||||
|
||||
const keyboardDismissProp = Platform.select({
|
||||
android: {
|
||||
onScrollBeginDrag: Keyboard.dismiss,
|
||||
@@ -57,58 +41,29 @@ const sectionKeyExtractor = (profile: UserProfile) => {
|
||||
return profile.username[0].toUpperCase();
|
||||
};
|
||||
|
||||
const sectionRoleKeyExtractor = (cAdmin: boolean) => {
|
||||
// Group items by channel admin or channel member
|
||||
return cAdmin ? messages.admins : messages.members;
|
||||
};
|
||||
export function createProfilesSections(profiles: UserProfile[]) {
|
||||
const sections: {[key: string]: UserProfile[]} = {};
|
||||
const sectionKeys: string[] = [];
|
||||
for (const profile of profiles) {
|
||||
const sectionKey = sectionKeyExtractor(profile);
|
||||
|
||||
export function createProfilesSections(intl: IntlShape, profiles: UserProfile[], members?: ChannelMember[]) {
|
||||
if (!profiles.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const sections = new Map();
|
||||
|
||||
if (members?.length) {
|
||||
// when channel members are provided, build the sections by admins and members
|
||||
const membersDictionary = new Map();
|
||||
const membersSections = new Map();
|
||||
const {formatMessage} = intl;
|
||||
members.forEach((m) => membersDictionary.set(m.user_id, m));
|
||||
profiles.forEach((p) => {
|
||||
const member = membersDictionary.get(p.id);
|
||||
const sectionKey = sectionRoleKeyExtractor(member.scheme_admin!);
|
||||
const sectionValue = membersSections.get(sectionKey) || [];
|
||||
|
||||
// combine UserProfile and ChannelMember objects so can get channel member scheme_admin permission
|
||||
const section = [...sectionValue, {...p, ...member}];
|
||||
membersSections.set(sectionKey, section);
|
||||
});
|
||||
sections.set(formatMessage(messages.admins), membersSections.get(messages.admins));
|
||||
sections.set(formatMessage(messages.members), membersSections.get(messages.members));
|
||||
} else {
|
||||
// when channel members are not provided, build the sections alphabetically
|
||||
profiles.forEach((p) => {
|
||||
const sectionKey = sectionKeyExtractor(p);
|
||||
const sectionValue = sections.get(sectionKey) || [];
|
||||
const section = [...sectionValue, p];
|
||||
sections.set(sectionKey, section);
|
||||
});
|
||||
}
|
||||
|
||||
const results = [];
|
||||
let index = 0;
|
||||
for (const [k, v] of sections) {
|
||||
if (v) {
|
||||
results.push({
|
||||
first: index === 0,
|
||||
id: k,
|
||||
data: v,
|
||||
});
|
||||
if (!sections[sectionKey]) {
|
||||
sections[sectionKey] = [];
|
||||
sectionKeys.push(sectionKey);
|
||||
}
|
||||
index++;
|
||||
|
||||
sections[sectionKey].push(profile);
|
||||
}
|
||||
return results;
|
||||
|
||||
sectionKeys.sort();
|
||||
|
||||
return sectionKeys.map((sectionKey, index) => {
|
||||
return {
|
||||
id: sectionKey,
|
||||
first: index === 0,
|
||||
data: sections[sectionKey],
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
@@ -148,14 +103,11 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
|
||||
type Props = {
|
||||
profiles: UserProfile[];
|
||||
channelMembers?: ChannelMember[];
|
||||
currentUserId: string;
|
||||
teammateNameDisplay: string;
|
||||
handleSelectProfile: (user: UserProfile) => void;
|
||||
fetchMore: () => void;
|
||||
loading: boolean;
|
||||
manageMode?: boolean;
|
||||
showManageMode?: boolean;
|
||||
showNoResults: boolean;
|
||||
selectedIds: {[id: string]: UserProfile};
|
||||
testID?: string;
|
||||
@@ -165,15 +117,12 @@ type Props = {
|
||||
|
||||
export default function UserList({
|
||||
profiles,
|
||||
channelMembers,
|
||||
selectedIds,
|
||||
currentUserId,
|
||||
teammateNameDisplay,
|
||||
handleSelectProfile,
|
||||
fetchMore,
|
||||
loading,
|
||||
manageMode = false,
|
||||
showManageMode = false,
|
||||
showNoResults,
|
||||
term,
|
||||
testID,
|
||||
@@ -184,22 +133,17 @@ export default function UserList({
|
||||
const serverUrl = useServerUrl();
|
||||
const style = getStyleFromTheme(theme);
|
||||
const keyboardHeight = useKeyboardHeight();
|
||||
const noResultsStyle = useMemo(() => [
|
||||
const noResutsStyle = useMemo(() => [
|
||||
style.noResultContainer,
|
||||
{paddingBottom: keyboardHeight},
|
||||
], [style, keyboardHeight]);
|
||||
|
||||
const data = useMemo(() => {
|
||||
if (profiles.length === 0 && !loading) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (term) {
|
||||
return profiles;
|
||||
}
|
||||
|
||||
return createProfilesSections(intl, profiles, channelMembers);
|
||||
}, [channelMembers, loading, profiles, term]);
|
||||
return createProfilesSections(profiles);
|
||||
}, [term, profiles]);
|
||||
|
||||
const openUserProfile = useCallback(async (profile: UserProfile) => {
|
||||
const {user} = await storeProfile(serverUrl, profile);
|
||||
@@ -218,34 +162,29 @@ export default function UserList({
|
||||
}
|
||||
}, []);
|
||||
|
||||
const renderItem = useCallback(({item, index, section}: RenderItemType) => {
|
||||
const renderItem = useCallback(({item, index, section}: ListRenderItemInfo<UserProfile> & {section?: SectionListData<UserProfile>}) => {
|
||||
// The list will re-render when the selection changes because it's passed into the list as extraData
|
||||
const selected = Boolean(selectedIds[item.id]);
|
||||
const canAdd = Object.keys(selectedIds).length < General.MAX_USERS_IN_GM;
|
||||
|
||||
const isChAdmin = item.scheme_admin || false;
|
||||
|
||||
return (
|
||||
<UserListRow
|
||||
key={item.id}
|
||||
highlight={section?.first && index === 0}
|
||||
id={item.id}
|
||||
isChannelAdmin={isChAdmin}
|
||||
isMyUser={currentUserId === item.id}
|
||||
manageMode={manageMode}
|
||||
onPress={handleSelectProfile}
|
||||
onLongPress={openUserProfile}
|
||||
selectable={manageMode || canAdd}
|
||||
disabled={!canAdd}
|
||||
selectable={true}
|
||||
selected={selected}
|
||||
showManageMode={showManageMode}
|
||||
testID='create_direct_message.user_list.user_item'
|
||||
teammateNameDisplay={teammateNameDisplay}
|
||||
tutorialWatched={tutorialWatched}
|
||||
user={item}
|
||||
/>
|
||||
);
|
||||
}, [selectedIds, handleSelectProfile, showManageMode, manageMode, teammateNameDisplay, tutorialWatched]);
|
||||
}, [selectedIds, currentUserId, handleSelectProfile, teammateNameDisplay, tutorialWatched]);
|
||||
|
||||
const renderLoading = useCallback(() => {
|
||||
if (!loading) {
|
||||
@@ -262,16 +201,16 @@ export default function UserList({
|
||||
}, [loading, theme]);
|
||||
|
||||
const renderNoResults = useCallback(() => {
|
||||
if (!showNoResults) {
|
||||
if (!showNoResults || !term) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={noResultsStyle}>
|
||||
{term ? <NoResultsWithTerm term={term}/> : <NoResultsWithButton/>}
|
||||
<View style={noResutsStyle}>
|
||||
<NoResultsWithTerm term={term}/>
|
||||
</View>
|
||||
);
|
||||
}, [showNoResults && style, term, noResultsStyle]);
|
||||
}, [showNoResults && style, term, noResutsStyle]);
|
||||
|
||||
const renderSectionHeader = useCallback(({section}: {section: SectionListData<UserProfile>}) => {
|
||||
return (
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
|
||||
import {storeProfileLongPressTutorial} from '@actions/app/global';
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
import FormattedText from '@components/formatted_text';
|
||||
import ProfilePicture from '@components/profile_picture';
|
||||
import {BotTag, GuestTag} from '@components/tag';
|
||||
import TouchableWithFeedback from '@components/touchable_with_feedback';
|
||||
@@ -20,27 +19,23 @@ import TutorialHighlight from '@components/tutorial_highlight';
|
||||
import TutorialLongPress from '@components/tutorial_highlight/long_press';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {useIsTablet} from '@hooks/device';
|
||||
import {t} from '@i18n';
|
||||
import {makeStyleSheetFromTheme, changeOpacity} from '@utils/theme';
|
||||
import {typography} from '@utils/typography';
|
||||
import {displayUsername, isGuest} from '@utils/user';
|
||||
|
||||
type Props = {
|
||||
highlight?: boolean;
|
||||
id: string;
|
||||
isMyUser: boolean;
|
||||
isChannelAdmin: boolean;
|
||||
manageMode: boolean;
|
||||
onLongPress: (user: UserProfile) => void;
|
||||
highlight?: boolean;
|
||||
user: UserProfile;
|
||||
teammateNameDisplay: string;
|
||||
testID: string;
|
||||
onPress?: (user: UserProfile) => void;
|
||||
onLongPress: (user: UserProfile) => void;
|
||||
selectable: boolean;
|
||||
disabled?: boolean;
|
||||
selected: boolean;
|
||||
showManageMode: boolean;
|
||||
teammateNameDisplay: string;
|
||||
testID: string;
|
||||
tutorialWatched?: boolean;
|
||||
user: UserProfile;
|
||||
}
|
||||
|
||||
const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
@@ -89,15 +84,6 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
selectorManage: {
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
manageText: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.64),
|
||||
...typography('Body', 100, 'Regular'),
|
||||
},
|
||||
tutorial: {
|
||||
top: Platform.select({ios: -74, default: -94}),
|
||||
},
|
||||
@@ -114,26 +100,24 @@ function UserListRow({
|
||||
id,
|
||||
isMyUser,
|
||||
highlight,
|
||||
isChannelAdmin,
|
||||
user,
|
||||
teammateNameDisplay,
|
||||
testID,
|
||||
onPress,
|
||||
onLongPress,
|
||||
manageMode = false,
|
||||
tutorialWatched = false,
|
||||
selectable,
|
||||
disabled,
|
||||
selected,
|
||||
showManageMode = false,
|
||||
teammateNameDisplay,
|
||||
testID,
|
||||
tutorialWatched = false,
|
||||
user,
|
||||
}: Props) {
|
||||
const theme = useTheme();
|
||||
const intl = useIntl();
|
||||
const isTablet = useIsTablet();
|
||||
const [showTutorial, setShowTutorial] = useState(false);
|
||||
const [itemBounds, setItemBounds] = useState<TutorialItemBounds>({startX: 0, startY: 0, endX: 0, endY: 0});
|
||||
const viewRef = useRef<View>(null);
|
||||
const style = getStyleFromTheme(theme);
|
||||
const {formatMessage, locale} = useIntl();
|
||||
const {formatMessage} = intl;
|
||||
const {username} = user;
|
||||
|
||||
const startTutorial = () => {
|
||||
@@ -168,41 +152,13 @@ function UserListRow({
|
||||
}, [highlight, tutorialWatched, isTablet]);
|
||||
|
||||
const handlePress = useCallback(() => {
|
||||
if (isMyUser && manageMode) {
|
||||
return;
|
||||
}
|
||||
onPress?.(user);
|
||||
}, [onPress, isMyUser, manageMode, user]);
|
||||
}, [onPress, user]);
|
||||
|
||||
const handleLongPress = useCallback(() => {
|
||||
onLongPress?.(user);
|
||||
}, [onLongPress, user]);
|
||||
|
||||
const manageModeIcon = useMemo(() => {
|
||||
if (!showManageMode || isMyUser) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const color = changeOpacity(theme.centerChannelColor, 0.64);
|
||||
const i18nId = isChannelAdmin ? t('mobile.manage_members.admin') : t('mobile.manage_members.member');
|
||||
const defaultMessage = isChannelAdmin ? 'Admin' : 'Member';
|
||||
|
||||
return (
|
||||
<View style={style.selectorManage}>
|
||||
<FormattedText
|
||||
id={i18nId}
|
||||
style={style.manageText}
|
||||
defaultMessage={defaultMessage}
|
||||
/>
|
||||
<CompassIcon
|
||||
name={'chevron-down'}
|
||||
size={18}
|
||||
color={color}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}, [isChannelAdmin, showManageMode, theme]);
|
||||
|
||||
const onLayout = useCallback(() => {
|
||||
startTutorial();
|
||||
}, []);
|
||||
@@ -233,7 +189,7 @@ function UserListRow({
|
||||
}, {username});
|
||||
}
|
||||
|
||||
const teammateDisplay = displayUsername(user, locale, teammateNameDisplay);
|
||||
const teammateDisplay = displayUsername(user, intl.locale, teammateNameDisplay);
|
||||
const showTeammateDisplay = teammateDisplay !== username;
|
||||
|
||||
const userItemTestID = `${testID}.${id}`;
|
||||
@@ -301,7 +257,7 @@ function UserListRow({
|
||||
</View>
|
||||
}
|
||||
</View>
|
||||
{manageMode ? manageModeIcon : icon}
|
||||
{icon}
|
||||
</View>
|
||||
</TouchableWithFeedback>
|
||||
{showTutorial &&
|
||||
@@ -311,7 +267,7 @@ function UserListRow({
|
||||
onLayout={onLayout}
|
||||
>
|
||||
<TutorialLongPress
|
||||
message={formatMessage({id: 'user.tutorial.long_press', defaultMessage: "Long-press on an item to view a user's profile"})}
|
||||
message={intl.formatMessage({id: 'user.tutorial.long_press', defaultMessage: "Long-press on an item to view a user's profile"})}
|
||||
style={isTablet ? style.tutorialTablet : style.tutorial}
|
||||
/>
|
||||
</TutorialHighlight>
|
||||
|
||||
@@ -15,8 +15,6 @@ export default keyMirror({
|
||||
LEAVE_TEAM: null,
|
||||
LOADING_CHANNEL_POSTS: null,
|
||||
NOTIFICATION_ERROR: null,
|
||||
REMOVE_USER_FROM_CHANNEL: null,
|
||||
MANAGE_USER_CHANGE_ROLE: null,
|
||||
SERVER_LOGOUT: null,
|
||||
SERVER_VERSION_CHANGED: null,
|
||||
SESSION_EXPIRED: null,
|
||||
|
||||
@@ -29,7 +29,6 @@ export default {
|
||||
SHOW_FULLNAME: 'full_name',
|
||||
},
|
||||
SPECIAL_MENTIONS: new Set(['all', 'channel', 'here']),
|
||||
MAX_USERS_ADD_TO_CHANNEL: 25,
|
||||
MAX_USERS_IN_GM: 7,
|
||||
MIN_USERS_IN_GM: 3,
|
||||
MAX_GROUP_CHANNELS_FOR_PROFILES: 50,
|
||||
|
||||
@@ -19,7 +19,6 @@ import Integrations from './integrations';
|
||||
import Launch from './launch';
|
||||
import License from './license';
|
||||
import List from './list';
|
||||
import Members from './members';
|
||||
import Navigation from './navigation';
|
||||
import Network from './network';
|
||||
import NotificationLevel from './notification_level';
|
||||
@@ -58,7 +57,6 @@ export {
|
||||
Launch,
|
||||
License,
|
||||
List,
|
||||
Members,
|
||||
Navigation,
|
||||
Network,
|
||||
NotificationLevel,
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import keyMirror from '@utils/key_mirror';
|
||||
const ManageOptions = keyMirror({
|
||||
REMOVE_USER: null,
|
||||
MAKE_CHANNEL_ADMIN: null,
|
||||
MAKE_CHANNEL_MEMBER: null,
|
||||
});
|
||||
|
||||
export type ManageOptionsTypes = keyof typeof ManageOptions
|
||||
|
||||
export default {
|
||||
ManageOptions,
|
||||
};
|
||||
|
||||
@@ -1,26 +1,16 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export const CATEGORIES_TO_KEEP: Record<string, string> = {
|
||||
ADVANCED_SETTINGS: 'advanced_settings',
|
||||
DIRECT_CHANNEL_SHOW: 'direct_channel_show',
|
||||
GROUP_CHANNEL_SHOW: 'group_channel_show',
|
||||
DISPLAY_SETTINGS: 'display_settings',
|
||||
EMOJI: 'emoji',
|
||||
NOTIFICATIONS: 'notifications',
|
||||
SAVED_POST: 'flagged_post',
|
||||
SIDEBAR_SETTINGS: 'sidebar_settings',
|
||||
TEAMS_ORDER: 'teams_order',
|
||||
THEME: 'theme',
|
||||
};
|
||||
|
||||
const CATEGORIES: Record<string, string> = {
|
||||
...CATEGORIES_TO_KEEP,
|
||||
FAVORITE_CHANNEL: 'favorite_channel',
|
||||
};
|
||||
|
||||
const Preferences = {
|
||||
CATEGORIES,
|
||||
const Preferences: Record<string, any> = {
|
||||
CATEGORY_CHANNEL_OPEN_TIME: 'channel_open_time',
|
||||
CATEGORY_CHANNEL_APPROXIMATE_VIEW_TIME: 'channel_approximate_view_time',
|
||||
CATEGORY_DIRECT_CHANNEL_SHOW: 'direct_channel_show',
|
||||
CATEGORY_GROUP_CHANNEL_SHOW: 'group_channel_show',
|
||||
CATEGORY_EMOJI: 'emoji',
|
||||
CATEGORY_SAVED_POST: 'flagged_post',
|
||||
CATEGORY_FAVORITE_CHANNEL: 'favorite_channel',
|
||||
CATEGORY_AUTO_RESET_MANUAL_STATUS: 'auto_reset_manual_status',
|
||||
CATEGORY_NOTIFICATIONS: 'notifications',
|
||||
COLLAPSED_REPLY_THREADS: 'collapsed_reply_threads',
|
||||
COLLAPSED_REPLY_THREADS_OFF: 'off',
|
||||
COLLAPSED_REPLY_THREADS_ON: 'on',
|
||||
@@ -37,6 +27,7 @@ const Preferences = {
|
||||
// "immediate" is a 30 second interval
|
||||
INTERVAL_NEVER: 0,
|
||||
INTERVAL_NOT_SET: -1,
|
||||
CATEGORY_DISPLAY_SETTINGS: 'display_settings',
|
||||
NAME_NAME_FORMAT: 'name_format',
|
||||
DISPLAY_PREFER_NICKNAME: 'nickname_full_name',
|
||||
DISPLAY_PREFER_FULL_NAME: 'full_name',
|
||||
@@ -45,14 +36,18 @@ const Preferences = {
|
||||
LINK_PREVIEW_DISPLAY: 'link_previews',
|
||||
MENTION_KEYS: 'mention_keys',
|
||||
USE_MILITARY_TIME: 'use_military_time',
|
||||
CATEGORY_SIDEBAR_SETTINGS: 'sidebar_settings',
|
||||
CHANNEL_SIDEBAR_ORGANIZATION: 'channel_sidebar_organization',
|
||||
CHANNEL_SIDEBAR_LIMIT_DMS: 'limit_visible_dms_gms',
|
||||
CHANNEL_SIDEBAR_LIMIT_DMS_DEFAULT: 20,
|
||||
CHANNEL_SIDEBAR_GROUP_UNREADS: 'show_unread_section',
|
||||
AUTOCLOSE_DMS_ENABLED: 'after_seven_days',
|
||||
CATEGORY_ADVANCED_SETTINGS: 'advanced_settings',
|
||||
ADVANCED_FILTER_JOIN_LEAVE: 'join_leave',
|
||||
ADVANCED_CODE_BLOCK_ON_CTRL_ENTER: 'code_block_ctrl_enter',
|
||||
ADVANCED_SEND_ON_CTRL_ENTER: 'send_on_ctrl_enter',
|
||||
CATEGORY_THEME: 'theme',
|
||||
TEAMS_ORDER: 'teams_order',
|
||||
THEMES: {
|
||||
denim: {
|
||||
type: 'Denim',
|
||||
|
||||
@@ -11,6 +11,7 @@ export const CHANNEL = 'Channel';
|
||||
export const CHANNEL_ADD_PEOPLE = 'ChannelAddPeople';
|
||||
export const CHANNEL_INFO = 'ChannelInfo';
|
||||
export const CHANNEL_MENTION = 'ChannelMention';
|
||||
export const CHANNEL_NOTIFICATION_PREFERENCE = 'ChannelNotificationPreference';
|
||||
export const CODE = 'Code';
|
||||
export const CREATE_DIRECT_MESSAGE = 'CreateDirectMessage';
|
||||
export const CREATE_OR_EDIT_CHANNEL = 'CreateOrEditChannel';
|
||||
@@ -33,7 +34,6 @@ export const IN_APP_NOTIFICATION = 'InAppNotification';
|
||||
export const JOIN_TEAM = 'JoinTeam';
|
||||
export const LATEX = 'Latex';
|
||||
export const LOGIN = 'Login';
|
||||
export const MANAGE_CHANNEL_MEMBERS = 'ManageChannelMembers';
|
||||
export const MENTIONS = 'Mentions';
|
||||
export const MFA = 'MFA';
|
||||
export const ONBOARDING = 'Onboarding';
|
||||
@@ -80,6 +80,7 @@ export default {
|
||||
CHANNEL_ADD_PEOPLE,
|
||||
CHANNEL_INFO,
|
||||
CHANNEL_MENTION,
|
||||
CHANNEL_NOTIFICATION_PREFERENCE,
|
||||
CODE,
|
||||
CREATE_DIRECT_MESSAGE,
|
||||
CREATE_OR_EDIT_CHANNEL,
|
||||
@@ -102,7 +103,6 @@ export default {
|
||||
JOIN_TEAM,
|
||||
LATEX,
|
||||
LOGIN,
|
||||
MANAGE_CHANNEL_MEMBERS,
|
||||
MENTIONS,
|
||||
MFA,
|
||||
ONBOARDING,
|
||||
@@ -142,7 +142,6 @@ export default {
|
||||
export const MODAL_SCREENS_WITHOUT_BACK = new Set<string>([
|
||||
BROWSE_CHANNELS,
|
||||
CHANNEL_INFO,
|
||||
CHANNEL_ADD_PEOPLE,
|
||||
CREATE_DIRECT_MESSAGE,
|
||||
CREATE_TEAM,
|
||||
CUSTOM_STATUS,
|
||||
@@ -151,7 +150,6 @@ export const MODAL_SCREENS_WITHOUT_BACK = new Set<string>([
|
||||
EDIT_SERVER,
|
||||
FIND_CHANNELS,
|
||||
GALLERY,
|
||||
MANAGE_CHANNEL_MEMBERS,
|
||||
INVITE,
|
||||
PERMALINK,
|
||||
]);
|
||||
@@ -172,6 +170,7 @@ export const SCREENS_AS_BOTTOM_SHEET = new Set<string>([
|
||||
]);
|
||||
|
||||
export const NOT_READY = [
|
||||
CHANNEL_ADD_PEOPLE,
|
||||
CHANNEL_MENTION,
|
||||
CREATE_TEAM,
|
||||
];
|
||||
|
||||
@@ -5,12 +5,10 @@ import {t} from '@i18n';
|
||||
import keyMirror from '@utils/key_mirror';
|
||||
|
||||
export const SNACK_BAR_TYPE = keyMirror({
|
||||
ADD_CHANNEL_MEMBERS: null,
|
||||
FAVORITE_CHANNEL: null,
|
||||
LINK_COPIED: null,
|
||||
MESSAGE_COPIED: null,
|
||||
MUTE_CHANNEL: null,
|
||||
REMOVE_CHANNEL_USER: null,
|
||||
UNFAVORITE_CHANNEL: null,
|
||||
UNMUTE_CHANNEL: null,
|
||||
});
|
||||
@@ -23,12 +21,6 @@ type SnackBarConfig = {
|
||||
};
|
||||
|
||||
export const SNACK_BAR_CONFIG: Record<string, SnackBarConfig> = {
|
||||
ADD_CHANNEL_MEMBERS: {
|
||||
id: t('snack.bar.channel.members.added'),
|
||||
defaultMessage: '{numMembers, number} {numMembers, plural, one {member} other {members}} added',
|
||||
iconName: 'check',
|
||||
canUndo: false,
|
||||
},
|
||||
FAVORITE_CHANNEL: {
|
||||
id: t('snack.bar.favorited.channel'),
|
||||
defaultMessage: 'This channel was favorited',
|
||||
@@ -53,12 +45,6 @@ export const SNACK_BAR_CONFIG: Record<string, SnackBarConfig> = {
|
||||
iconName: 'bell-off-outline',
|
||||
canUndo: true,
|
||||
},
|
||||
REMOVE_CHANNEL_USER: {
|
||||
id: t('snack.bar.remove.user'),
|
||||
defaultMessage: '1 member was removed from the channel',
|
||||
iconName: 'check',
|
||||
canUndo: true,
|
||||
},
|
||||
UNFAVORITE_CHANNEL: {
|
||||
id: t('snack.bar.unfavorite.channel'),
|
||||
defaultMessage: 'This channel was unfavorited',
|
||||
|
||||
@@ -6,7 +6,7 @@ import React, {ComponentType, createContext, useEffect, useState} from 'react';
|
||||
import {Appearance} from 'react-native';
|
||||
|
||||
import {Preferences} from '@constants';
|
||||
import {queryThemePreferences} from '@queries/servers/preference';
|
||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||
import {observeCurrentTeamId} from '@queries/servers/system';
|
||||
import {setThemeDefaults, updateThemeIfNeeded} from '@utils/theme';
|
||||
|
||||
@@ -97,7 +97,7 @@ export function useTheme(): Theme {
|
||||
|
||||
const enhancedThemeProvider = withObservables([], ({database}: {database: Database}) => ({
|
||||
currentTeamId: observeCurrentTeamId(database),
|
||||
themes: queryThemePreferences(database).observeWithColumns(['value']),
|
||||
themes: queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_THEME).observeWithColumns(['value']),
|
||||
}));
|
||||
|
||||
export default enhancedThemeProvider(ThemeProvider);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Database, Q} from '@nozbe/watermelondb';
|
||||
import {Database, Model, Q} from '@nozbe/watermelondb';
|
||||
import LokiJSAdapter from '@nozbe/watermelondb/adapters/lokijs';
|
||||
import logger from '@nozbe/watermelondb/utils/common/logger';
|
||||
import {DeviceEventEmitter, Platform} from 'react-native';
|
||||
@@ -45,14 +45,14 @@ class DatabaseManager {
|
||||
private readonly serverModels: Models;
|
||||
|
||||
constructor() {
|
||||
this.appModels = [InfoModel, GlobalModel, ServersModel];
|
||||
this.appModels = [InfoModel, GlobalModel, ServersModel] as unknown[] as Model[];
|
||||
this.serverModels = [
|
||||
CategoryModel, CategoryChannelModel, ChannelModel, ChannelInfoModel, ChannelMembershipModel, ConfigModel, CustomEmojiModel, DraftModel, FileModel,
|
||||
GroupModel, GroupChannelModel, GroupTeamModel, GroupMembershipModel, MyChannelModel, MyChannelSettingsModel, MyTeamModel,
|
||||
PostModel, PostsInChannelModel, PostsInThreadModel, PreferenceModel, ReactionModel, RoleModel,
|
||||
SystemModel, TeamModel, TeamChannelHistoryModel, TeamMembershipModel, TeamSearchHistoryModel,
|
||||
ThreadModel, ThreadParticipantModel, ThreadInTeamModel, TeamThreadsSyncModel, UserModel,
|
||||
];
|
||||
] as unknown[] as Model[];
|
||||
this.databaseDirectory = '';
|
||||
}
|
||||
|
||||
|
||||
@@ -43,14 +43,14 @@ class DatabaseManager {
|
||||
private readonly serverModels: Models;
|
||||
|
||||
constructor() {
|
||||
this.appModels = [InfoModel, GlobalModel, ServersModel];
|
||||
this.appModels = [InfoModel, GlobalModel, ServersModel] as unknown[] as Models;
|
||||
this.serverModels = [
|
||||
CategoryModel, CategoryChannelModel, ChannelModel, ChannelInfoModel, ChannelMembershipModel, ConfigModel, CustomEmojiModel, DraftModel, FileModel,
|
||||
GroupModel, GroupChannelModel, GroupTeamModel, GroupMembershipModel, MyChannelModel, MyChannelSettingsModel, MyTeamModel,
|
||||
PostModel, PostsInChannelModel, PostsInThreadModel, PreferenceModel, ReactionModel, RoleModel,
|
||||
SystemModel, TeamModel, TeamChannelHistoryModel, TeamMembershipModel, TeamSearchHistoryModel,
|
||||
ThreadModel, ThreadParticipantModel, ThreadInTeamModel, TeamThreadsSyncModel, UserModel,
|
||||
];
|
||||
] as unknown[] as Models;
|
||||
|
||||
this.databaseDirectory = Platform.OS === 'ios' ? getIOSAppGroupDetails().appGroupDatabase : `${FileSystem.DocumentDirectoryPath}/databases/`;
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ describe('** APP DATA OPERATOR **', () => {
|
||||
],
|
||||
tableName: 'Info',
|
||||
prepareRecordsOnly: false,
|
||||
}, 'handleInfo');
|
||||
});
|
||||
});
|
||||
|
||||
it('=> HandleGlobal: should write to GLOBAL table', async () => {
|
||||
@@ -79,6 +79,6 @@ describe('** APP DATA OPERATOR **', () => {
|
||||
createOrUpdateRawValues: globals,
|
||||
tableName: 'Global',
|
||||
prepareRecordsOnly: false,
|
||||
}, 'handleGlobal');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -28,7 +28,7 @@ export default class AppDataOperator extends BaseDataOperator {
|
||||
prepareRecordsOnly,
|
||||
createOrUpdateRawValues: getUniqueRawsBy({raws: info, key: 'version_number'}),
|
||||
tableName: INFO,
|
||||
}, 'handleInfo');
|
||||
});
|
||||
};
|
||||
|
||||
handleGlobal = async ({globals, prepareRecordsOnly = true}: HandleGlobalArgs) => {
|
||||
@@ -45,6 +45,6 @@ export default class AppDataOperator extends BaseDataOperator {
|
||||
prepareRecordsOnly,
|
||||
createOrUpdateRawValues: getUniqueRawsBy({raws: globals, key: 'id'}),
|
||||
tableName: GLOBAL,
|
||||
}, 'handleGlobal');
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -22,10 +22,10 @@ import type {
|
||||
|
||||
export interface BaseDataOperatorType {
|
||||
database: Database;
|
||||
handleRecords: <T extends Model>({buildKeyRecordBy, fieldName, transformer, createOrUpdateRawValues, deleteRawValues, tableName, prepareRecordsOnly}: HandleRecordsArgs<T>, description: string) => Promise<Model[]>;
|
||||
processRecords: <T extends Model>({createOrUpdateRawValues, deleteRawValues, tableName, buildKeyRecordBy, fieldName}: ProcessRecordsArgs) => Promise<ProcessRecordResults<T>>;
|
||||
batchRecords: (models: Model[], description: string) => Promise<void>;
|
||||
prepareRecords: <T extends Model>({tableName, createRaws, deleteRaws, updateRaws, transformer}: OperationArgs<T>) => Promise<Model[]>;
|
||||
handleRecords: ({buildKeyRecordBy, fieldName, transformer, createOrUpdateRawValues, deleteRawValues, tableName, prepareRecordsOnly}: HandleRecordsArgs) => Promise<Model[]>;
|
||||
processRecords: ({createOrUpdateRawValues, deleteRawValues, tableName, buildKeyRecordBy, fieldName}: ProcessRecordsArgs) => Promise<ProcessRecordResults>;
|
||||
batchRecords: (models: Model[]) => Promise<void>;
|
||||
prepareRecords: ({tableName, createRaws, deleteRaws, updateRaws, transformer}: OperationArgs) => Promise<Model[]>;
|
||||
}
|
||||
|
||||
export default class BaseDataOperator {
|
||||
@@ -45,7 +45,7 @@ export default class BaseDataOperator {
|
||||
* @param {(existing: Model, newElement: RawValue) => boolean} inputsArg.buildKeyRecordBy
|
||||
* @returns {Promise<{ProcessRecordResults}>}
|
||||
*/
|
||||
processRecords = async <T extends Model>({createOrUpdateRawValues = [], deleteRawValues = [], tableName, buildKeyRecordBy, fieldName}: ProcessRecordsArgs): Promise<ProcessRecordResults<T>> => {
|
||||
processRecords = async ({createOrUpdateRawValues = [], deleteRawValues = [], tableName, buildKeyRecordBy, fieldName}: ProcessRecordsArgs): Promise<ProcessRecordResults> => {
|
||||
const getRecords = async (rawValues: RawValue[]) => {
|
||||
// We will query a table where one of its fields can match a range of values. Hence, here we are extracting all those potential values.
|
||||
const columnValues: string[] = getRangeOfValues({fieldName, raws: rawValues});
|
||||
@@ -60,7 +60,7 @@ export default class BaseDataOperator {
|
||||
return [];
|
||||
}
|
||||
|
||||
const existingRecords = await retrieveRecords<T>({
|
||||
const existingRecords = await retrieveRecords({
|
||||
database: this.database,
|
||||
tableName,
|
||||
condition: Q.where(fieldName, Q.oneOf(columnValues)),
|
||||
@@ -125,13 +125,13 @@ export default class BaseDataOperator {
|
||||
* @param {(TransformerArgs) => Promise<Model>;} transformer
|
||||
* @returns {Promise<Model[]>}
|
||||
*/
|
||||
prepareRecords = async <T extends Model>({tableName, createRaws, deleteRaws, updateRaws, transformer}: OperationArgs<T>): Promise<T[]> => {
|
||||
prepareRecords = async ({tableName, createRaws, deleteRaws, updateRaws, transformer}: OperationArgs): Promise<Model[]> => {
|
||||
if (!this.database) {
|
||||
logWarning('Database not defined in prepareRecords');
|
||||
return [];
|
||||
}
|
||||
|
||||
let preparedRecords: Array<Promise<T>> = [];
|
||||
let preparedRecords: Array<Promise<Model>> = [];
|
||||
|
||||
// create operation
|
||||
if (createRaws?.length) {
|
||||
@@ -165,7 +165,7 @@ export default class BaseDataOperator {
|
||||
preparedRecords = preparedRecords.concat(recordPromises);
|
||||
}
|
||||
|
||||
const results = (await Promise.all(preparedRecords)) as T[];
|
||||
const results = await Promise.all(preparedRecords);
|
||||
|
||||
if (deleteRaws?.length) {
|
||||
deleteRaws.forEach((deleteRecord) => {
|
||||
@@ -182,12 +182,12 @@ export default class BaseDataOperator {
|
||||
* @param {Array} models
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async batchRecords(models: Model[], description: string): Promise<void> {
|
||||
async batchRecords(models: Model[]): Promise<void> {
|
||||
try {
|
||||
if (models.length > 0) {
|
||||
await this.database.write(async (writer) => {
|
||||
await writer.batch(models);
|
||||
}, description);
|
||||
await writer.batch(...models);
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
logWarning('batchRecords error ', e as Error);
|
||||
@@ -205,7 +205,7 @@ export default class BaseDataOperator {
|
||||
* @param {string} handleRecordsArgs.tableName
|
||||
* @returns {Promise<Model[]>}
|
||||
*/
|
||||
async handleRecords<T extends Model>({buildKeyRecordBy, fieldName, transformer, createOrUpdateRawValues, deleteRawValues = [], tableName, prepareRecordsOnly = true}: HandleRecordsArgs<T>, description: string): Promise<T[]> {
|
||||
async handleRecords({buildKeyRecordBy, fieldName, transformer, createOrUpdateRawValues, deleteRawValues = [], tableName, prepareRecordsOnly = true}: HandleRecordsArgs): Promise<Model[]> {
|
||||
if (!createOrUpdateRawValues.length) {
|
||||
logWarning(
|
||||
`An empty "rawValues" array has been passed to the handleRecords method for tableName ${tableName}`,
|
||||
@@ -213,7 +213,7 @@ export default class BaseDataOperator {
|
||||
return [];
|
||||
}
|
||||
|
||||
const {createRaws, deleteRaws, updateRaws} = await this.processRecords<T>({
|
||||
const {createRaws, deleteRaws, updateRaws} = await this.processRecords({
|
||||
createOrUpdateRawValues,
|
||||
deleteRawValues,
|
||||
tableName,
|
||||
@@ -221,8 +221,8 @@ export default class BaseDataOperator {
|
||||
fieldName,
|
||||
});
|
||||
|
||||
let models: T[] = [];
|
||||
models = await this.prepareRecords<T>({
|
||||
let models: Model[] = [];
|
||||
models = await this.prepareRecords({
|
||||
tableName,
|
||||
createRaws,
|
||||
updateRaws,
|
||||
@@ -231,7 +231,7 @@ export default class BaseDataOperator {
|
||||
});
|
||||
|
||||
if (!prepareRecordsOnly && models?.length) {
|
||||
await this.batchRecords(models, description);
|
||||
await this.batchRecords(models);
|
||||
}
|
||||
|
||||
return models;
|
||||
|
||||
@@ -46,7 +46,7 @@ describe('*** Operator: Category Handlers tests ***', () => {
|
||||
tableName: MM_TABLES.SERVER.CATEGORY,
|
||||
prepareRecordsOnly: false,
|
||||
transformer: transformCategoryRecord,
|
||||
}, 'handleCategories');
|
||||
});
|
||||
});
|
||||
|
||||
it('=> handleCategoryChannels: should write to the CATEGORY_CHANNEL table', async () => {
|
||||
@@ -74,6 +74,6 @@ describe('*** Operator: Category Handlers tests ***', () => {
|
||||
tableName: MM_TABLES.SERVER.CATEGORY_CHANNEL,
|
||||
prepareRecordsOnly: false,
|
||||
transformer: transformCategoryChannelRecord,
|
||||
}, 'handleCategoryChannels');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
import {getUniqueRawsBy} from '@database/operator/utils/general';
|
||||
import {logWarning} from '@utils/log';
|
||||
|
||||
import type ServerDataOperatorBase from '.';
|
||||
import type {
|
||||
HandleCategoryChannelArgs,
|
||||
HandleCategoryArgs,
|
||||
@@ -29,7 +28,7 @@ export interface CategoryHandlerMix {
|
||||
handleCategories: ({categories, prepareRecordsOnly}: HandleCategoryArgs) => Promise<CategoryModel[]>;
|
||||
}
|
||||
|
||||
const CategoryHandler = <TBase extends Constructor<ServerDataOperatorBase>>(superclass: TBase) => class extends superclass {
|
||||
const CategoryHandler = (superclass: any) => class extends superclass {
|
||||
/**
|
||||
* handleCategories: Handler responsible for the Create/Update operations occurring on the Category table from the 'Server' schema
|
||||
* @param {HandleCategoryArgs} categoriesArgs
|
||||
@@ -79,7 +78,7 @@ const CategoryHandler = <TBase extends Constructor<ServerDataOperatorBase>>(supe
|
||||
createOrUpdateRawValues,
|
||||
tableName: CATEGORY,
|
||||
prepareRecordsOnly,
|
||||
}, 'handleCategories');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -89,7 +88,7 @@ const CategoryHandler = <TBase extends Constructor<ServerDataOperatorBase>>(supe
|
||||
* @param {boolean} categoriesArgs.prepareRecordsOnly
|
||||
* @returns {Promise<CategoryChannelModel[]>}
|
||||
*/
|
||||
handleCategoryChannels = async ({categoryChannels, prepareRecordsOnly = true}: HandleCategoryChannelArgs): Promise<CategoryChannelModel[]> => {
|
||||
handleCategoryChannels = async ({categoryChannels, prepareRecordsOnly = true}: HandleCategoryChannelArgs): Promise<CategoryModel[]> => {
|
||||
if (!categoryChannels?.length) {
|
||||
logWarning(
|
||||
'An empty or undefined "categoryChannels" array has been passed to the handleCategories method',
|
||||
@@ -106,7 +105,7 @@ const CategoryHandler = <TBase extends Constructor<ServerDataOperatorBase>>(supe
|
||||
createOrUpdateRawValues,
|
||||
tableName: CATEGORY_CHANNEL,
|
||||
prepareRecordsOnly,
|
||||
}, 'handleCategoryChannels');
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ describe('*** Operator: Channel Handlers tests ***', () => {
|
||||
tableName: 'Channel',
|
||||
prepareRecordsOnly: false,
|
||||
transformer: transformChannelRecord,
|
||||
}, 'handleChannel');
|
||||
});
|
||||
});
|
||||
|
||||
it('=> HandleMyChannelSettings: should write to the MY_CHANNEL_SETTINGS table', async () => {
|
||||
@@ -103,7 +103,7 @@ describe('*** Operator: Channel Handlers tests ***', () => {
|
||||
prepareRecordsOnly: false,
|
||||
buildKeyRecordBy: buildMyChannelKey,
|
||||
transformer: transformMyChannelSettingsRecord,
|
||||
}, 'handleMyChannelSettings');
|
||||
});
|
||||
});
|
||||
|
||||
it('=> HandleChannelInfo: should write to the CHANNEL_INFO table', async () => {
|
||||
@@ -134,7 +134,7 @@ describe('*** Operator: Channel Handlers tests ***', () => {
|
||||
tableName: 'ChannelInfo',
|
||||
prepareRecordsOnly: false,
|
||||
transformer: transformChannelInfoRecord,
|
||||
}, 'handleChannelInfo');
|
||||
});
|
||||
});
|
||||
|
||||
it('=> HandleMyChannel: should write to the MY_CHANNEL table', async () => {
|
||||
@@ -195,7 +195,7 @@ describe('*** Operator: Channel Handlers tests ***', () => {
|
||||
prepareRecordsOnly: false,
|
||||
buildKeyRecordBy: buildMyChannelKey,
|
||||
transformer: transformMyChannelRecord,
|
||||
}, 'handleMyChannel');
|
||||
});
|
||||
});
|
||||
|
||||
it('=> HandleMyChannel: should keep the previous lastFetchedAt for MY_CHANNEL', async () => {
|
||||
@@ -314,6 +314,6 @@ describe('*** Operator: Channel Handlers tests ***', () => {
|
||||
prepareRecordsOnly: false,
|
||||
buildKeyRecordBy: buildChannelMembershipKey,
|
||||
transformer: transformChannelMembershipRecord,
|
||||
}, 'handleChannelMembership');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,7 +19,6 @@ import {getUniqueRawsBy} from '@database/operator/utils/general';
|
||||
import {getIsCRTEnabled} from '@queries/servers/thread';
|
||||
import {logWarning} from '@utils/log';
|
||||
|
||||
import type ServerDataOperatorBase from '.';
|
||||
import type {HandleChannelArgs, HandleChannelInfoArgs, HandleChannelMembershipArgs, HandleMyChannelArgs, HandleMyChannelSettingsArgs} from '@typings/database/database';
|
||||
import type ChannelModel from '@typings/database/models/servers/channel';
|
||||
import type ChannelInfoModel from '@typings/database/models/servers/channel_info';
|
||||
@@ -43,7 +42,7 @@ export interface ChannelHandlerMix {
|
||||
handleMyChannel: ({channels, myChannels, isCRTEnabled, prepareRecordsOnly}: HandleMyChannelArgs) => Promise<MyChannelModel[]>;
|
||||
}
|
||||
|
||||
const ChannelHandler = <TBase extends Constructor<ServerDataOperatorBase>>(superclass: TBase) => class extends superclass {
|
||||
const ChannelHandler = (superclass: any) => class extends superclass {
|
||||
/**
|
||||
* handleChannel: Handler responsible for the Create/Update operations occurring on the CHANNEL table from the 'Server' schema
|
||||
* @param {HandleChannelArgs} channelsArgs
|
||||
@@ -90,7 +89,7 @@ const ChannelHandler = <TBase extends Constructor<ServerDataOperatorBase>>(super
|
||||
prepareRecordsOnly,
|
||||
createOrUpdateRawValues,
|
||||
tableName: CHANNEL,
|
||||
}, 'handleChannel');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -147,7 +146,7 @@ const ChannelHandler = <TBase extends Constructor<ServerDataOperatorBase>>(super
|
||||
prepareRecordsOnly,
|
||||
createOrUpdateRawValues,
|
||||
tableName: MY_CHANNEL_SETTINGS,
|
||||
}, 'handleMyChannelSettings');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -206,7 +205,7 @@ const ChannelHandler = <TBase extends Constructor<ServerDataOperatorBase>>(super
|
||||
prepareRecordsOnly,
|
||||
createOrUpdateRawValues,
|
||||
tableName: CHANNEL_INFO,
|
||||
}, 'handleChannelInfo');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -293,7 +292,7 @@ const ChannelHandler = <TBase extends Constructor<ServerDataOperatorBase>>(super
|
||||
prepareRecordsOnly,
|
||||
createOrUpdateRawValues,
|
||||
tableName: MY_CHANNEL,
|
||||
}, 'handleMyChannel');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -349,7 +348,7 @@ const ChannelHandler = <TBase extends Constructor<ServerDataOperatorBase>>(super
|
||||
prepareRecordsOnly,
|
||||
createOrUpdateRawValues,
|
||||
tableName: CHANNEL_MEMBERSHIP,
|
||||
}, 'handleChannelMembership');
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -49,6 +49,6 @@ describe('*** Operator: Group Handlers tests ***', () => {
|
||||
tableName: MM_TABLES.SERVER.GROUP,
|
||||
prepareRecordsOnly: false,
|
||||
transformer: transformGroupRecord,
|
||||
}, 'handleGroups');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,7 +8,6 @@ import {queryGroupChannelForChannel, queryGroupMembershipForMember, queryGroupTe
|
||||
import {generateGroupAssociationId} from '@utils/groups';
|
||||
import {logWarning} from '@utils/log';
|
||||
|
||||
import type ServerDataOperatorBase from '.';
|
||||
import type {HandleGroupArgs, HandleGroupChannelsForChannelArgs, HandleGroupMembershipForMemberArgs, HandleGroupTeamsForTeamArgs} from '@typings/database/database';
|
||||
import type GroupModel from '@typings/database/models/servers/group';
|
||||
import type GroupChannelModel from '@typings/database/models/servers/group_channel';
|
||||
@@ -24,7 +23,7 @@ export interface GroupHandlerMix {
|
||||
handleGroupTeamsForTeam: ({teamId, groups, prepareRecordsOnly}: HandleGroupTeamsForTeamArgs) => Promise<GroupTeamModel[]>;
|
||||
}
|
||||
|
||||
const GroupHandler = <TBase extends Constructor<ServerDataOperatorBase>>(superclass: TBase) => class extends superclass implements GroupHandlerMix {
|
||||
const GroupHandler = (superclass: any) => class extends superclass implements GroupHandlerMix {
|
||||
/**
|
||||
* handleGroups: Handler responsible for the Create/Update operations occurring on the Group table from the 'Server' schema
|
||||
*
|
||||
@@ -47,7 +46,7 @@ const GroupHandler = <TBase extends Constructor<ServerDataOperatorBase>>(supercl
|
||||
createOrUpdateRawValues,
|
||||
tableName: GROUP,
|
||||
prepareRecordsOnly,
|
||||
}, 'handleGroups');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -94,14 +93,14 @@ const GroupHandler = <TBase extends Constructor<ServerDataOperatorBase>>(supercl
|
||||
records.push(...(await this.handleRecords({
|
||||
fieldName: 'id',
|
||||
transformer: transformGroupChannelRecord,
|
||||
createOrUpdateRawValues: rawValues,
|
||||
rawValues,
|
||||
tableName: GROUP_CHANNEL,
|
||||
prepareRecordsOnly: true,
|
||||
}, 'handleGroupChannelsForChannel')));
|
||||
})));
|
||||
|
||||
// Batch update if there are records
|
||||
if (records.length && !prepareRecordsOnly) {
|
||||
await this.batchRecords(records, 'handleGroupChannelsForChannel');
|
||||
await this.batchRecords(records);
|
||||
}
|
||||
|
||||
return records;
|
||||
@@ -151,14 +150,14 @@ const GroupHandler = <TBase extends Constructor<ServerDataOperatorBase>>(supercl
|
||||
records.push(...(await this.handleRecords({
|
||||
fieldName: 'id',
|
||||
transformer: transformGroupMembershipRecord,
|
||||
createOrUpdateRawValues: rawValues,
|
||||
rawValues,
|
||||
tableName: GROUP_MEMBERSHIP,
|
||||
prepareRecordsOnly: true,
|
||||
}, 'handleGroupMembershipsForMember')));
|
||||
})));
|
||||
|
||||
// Batch update if there are records
|
||||
if (records.length && !prepareRecordsOnly) {
|
||||
await this.batchRecords(records, 'handleGroupMembershipsForMember');
|
||||
await this.batchRecords(records);
|
||||
}
|
||||
|
||||
return records;
|
||||
@@ -208,14 +207,14 @@ const GroupHandler = <TBase extends Constructor<ServerDataOperatorBase>>(supercl
|
||||
records.push(...(await this.handleRecords({
|
||||
fieldName: 'id',
|
||||
transformer: transformGroupTeamRecord,
|
||||
createOrUpdateRawValues: rawValues,
|
||||
rawValues,
|
||||
tableName: GROUP_TEAM,
|
||||
prepareRecordsOnly: true,
|
||||
}, 'handleGroupTeamsForTeam')));
|
||||
})));
|
||||
|
||||
// Batch update if there are records
|
||||
if (records.length && !prepareRecordsOnly) {
|
||||
await this.batchRecords(records, 'handleGroupTeamsForTeam');
|
||||
await this.batchRecords(records);
|
||||
}
|
||||
|
||||
return records;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user