Add mute/unmute messages and mentions

This commit is contained in:
Ammar Githam 2021-01-17 17:02:43 +09:00
parent 13d95523a3
commit 52d2baa128
6 changed files with 248 additions and 29 deletions

View File

@ -7,6 +7,7 @@ import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.CompoundButton;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -176,6 +177,7 @@ public class DirectMessageSettingsFragment extends Fragment {
if (usersAdapter == null) return; if (usersAdapter == null) return;
usersAdapter.setAdminUserIds(adminUserIds); usersAdapter.setAdminUserIds(adminUserIds);
}); });
viewModel.getMuted().observe(getViewLifecycleOwner(), muted -> binding.muteMessages.setChecked(muted));
final NavController navController = NavHostFragment.findNavController(this); final NavController navController = NavHostFragment.findNavController(this);
final NavBackStackEntry backStackEntry = navController.getCurrentBackStackEntry(); final NavBackStackEntry backStackEntry = navController.getCurrentBackStackEntry();
if (backStackEntry != null) { if (backStackEntry != null) {
@ -202,7 +204,7 @@ public class DirectMessageSettingsFragment extends Fragment {
detailsChangeResourceLiveData = viewModel.addMembers(users); detailsChangeResourceLiveData = viewModel.addMembers(users);
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "search users result: ", e); Log.e(TAG, "search users result: ", e);
Snackbar.make(binding.getRoot(), e.getMessage(), Snackbar.LENGTH_LONG).show(); Snackbar.make(binding.getRoot(), e.getMessage() != null ? e.getMessage() : "", Snackbar.LENGTH_LONG).show();
} }
} }
if (detailsChangeResourceLiveData != null) { if (detailsChangeResourceLiveData != null) {
@ -274,6 +276,36 @@ public class DirectMessageSettingsFragment extends Fragment {
.setMultiple(true); .setMultiple(true);
navController.navigate(actionGlobalUserSearch); navController.navigate(actionGlobalUserSearch);
}); });
binding.muteMessagesLabel.setOnClickListener(v -> binding.muteMessages.toggle());
binding.muteMessages.setOnCheckedChangeListener((buttonView, isChecked) -> {
final LiveData<Resource<Object>> resourceLiveData = isChecked ? viewModel.mute() : viewModel.unmute();
handleMuteChangeResource(resourceLiveData, buttonView);
});
binding.muteMentionsLabel.setOnClickListener(v -> binding.muteMentions.toggle());
binding.muteMentions.setOnCheckedChangeListener((buttonView, isChecked) -> {
final LiveData<Resource<Object>> resourceLiveData = isChecked ? viewModel.muteMentions() : viewModel.unmuteMentions();
handleMuteChangeResource(resourceLiveData, buttonView);
});
}
private void handleMuteChangeResource(final LiveData<Resource<Object>> resourceLiveData, final CompoundButton buttonView) {
resourceLiveData.observe(getViewLifecycleOwner(), resource -> {
if (resource == null) return;
switch (resource.status) {
case SUCCESS:
buttonView.setEnabled(true);
break;
case ERROR:
buttonView.setEnabled(true);
if (resource.message != null) {
Snackbar.make(binding.getRoot(), resource.message, Snackbar.LENGTH_LONG).show();
}
break;
case LOADING:
buttonView.setEnabled(false);
break;
}
});
} }
private void setupMembers() { private void setupMembers() {

View File

@ -75,4 +75,24 @@ public interface DirectMessagesRepository {
@FormUrlEncoded @FormUrlEncoded
@POST("/api/v1/direct_v2/create_group_thread/") @POST("/api/v1/direct_v2/create_group_thread/")
Call<DirectThread> createThread(@FieldMap final Map<String, String> signedForm); Call<DirectThread> createThread(@FieldMap final Map<String, String> signedForm);
@FormUrlEncoded
@POST("/api/v1/direct_v2/threads/{threadId}/mute/")
Call<String> mute(@Path("threadId") String threadId,
@FieldMap final Map<String, String> form);
@FormUrlEncoded
@POST("/api/v1/direct_v2/threads/{threadId}/unmute/")
Call<String> unmute(@Path("threadId") String threadId,
@FieldMap final Map<String, String> form);
@FormUrlEncoded
@POST("/api/v1/direct_v2/threads/{threadId}/mute_mentions/")
Call<String> muteMentions(@Path("threadId") String threadId,
@FieldMap final Map<String, String> form);
@FormUrlEncoded
@POST("/api/v1/direct_v2/threads/{threadId}/unmute_mentions/")
Call<String> unmuteMentions(@Path("threadId") String threadId,
@FieldMap final Map<String, String> form);
} }

View File

@ -17,7 +17,7 @@ public class DirectThread implements Serializable {
private final List<Long> adminUserIds; private final List<Long> adminUserIds;
private final List<DirectItem> items; private final List<DirectItem> items;
private final long lastActivityAt; private final long lastActivityAt;
private final boolean muted; private boolean muted;
private final boolean isPin; private final boolean isPin;
private final boolean named; private final boolean named;
private final boolean canonical; private final boolean canonical;
@ -31,7 +31,7 @@ public class DirectThread implements Serializable {
private final long folder; private final long folder;
private final boolean vcMuted; private final boolean vcMuted;
private final boolean isGroup; private final boolean isGroup;
private final boolean mentionsMuted; private boolean mentionsMuted;
private final User inviter; private final User inviter;
private final boolean hasOlder; private final boolean hasOlder;
private final boolean hasNewer; private final boolean hasNewer;
@ -138,6 +138,10 @@ public class DirectThread implements Serializable {
return muted; return muted;
} }
public void setMuted(final boolean muted) {
this.muted = muted;
}
public boolean isPin() { public boolean isPin() {
return isPin; return isPin;
} }
@ -194,6 +198,10 @@ public class DirectThread implements Serializable {
return mentionsMuted; return mentionsMuted;
} }
public void setMentionsMuted(final boolean mentionsMuted) {
this.mentionsMuted = mentionsMuted;
}
public User getInviter() { public User getInviter() {
return inviter; return inviter;
} }

View File

@ -59,14 +59,16 @@ public class DirectSettingsViewModel extends AndroidViewModel {
new Pair<>(Collections.emptyList(), Collections.emptyList())); new Pair<>(Collections.emptyList(), Collections.emptyList()));
private final MutableLiveData<String> title = new MutableLiveData<>(""); private final MutableLiveData<String> title = new MutableLiveData<>("");
private final MutableLiveData<List<Long>> adminUserIds = new MutableLiveData<>(Collections.emptyList()); private final MutableLiveData<List<Long>> adminUserIds = new MutableLiveData<>(Collections.emptyList());
private final MutableLiveData<Boolean> muted = new MutableLiveData<>(false);
private final MutableLiveData<Boolean> mentionsMuted = new MutableLiveData<>(false);
private final DirectMessagesService directMessagesService; private final DirectMessagesService directMessagesService;
private DirectThread thread;
private final long userId; private final long userId;
private boolean viewerIsAdmin;
private final Resources resources; private final Resources resources;
private final FriendshipService friendshipService; private final FriendshipService friendshipService;
private final String csrfToken; private final String csrfToken;
private DirectThread thread;
private boolean viewerIsAdmin;
private User viewer; private User viewer;
public DirectSettingsViewModel(final Application application) { public DirectSettingsViewModel(final Application application) {
@ -103,6 +105,8 @@ public class DirectSettingsViewModel extends AndroidViewModel {
final List<Long> adminUserIds = thread.getAdminUserIds(); final List<Long> adminUserIds = thread.getAdminUserIds();
this.adminUserIds.postValue(adminUserIds); this.adminUserIds.postValue(adminUserIds);
viewerIsAdmin = adminUserIds.contains(userId); viewerIsAdmin = adminUserIds.contains(userId);
muted.postValue(thread.isMuted());
mentionsMuted.postValue(thread.isMentionsMuted());
} }
public boolean isGroup() { public boolean isGroup() {
@ -132,6 +136,10 @@ public class DirectSettingsViewModel extends AndroidViewModel {
return adminUserIds; return adminUserIds;
} }
public LiveData<Boolean> getMuted() {
return muted;
}
public LiveData<Resource<Object>> updateTitle(final String newTitle) { public LiveData<Resource<Object>> updateTitle(final String newTitle) {
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>(); final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
final Call<DirectThreadDetailsChangeResponse> addUsersRequest = directMessagesService final Call<DirectThreadDetailsChangeResponse> addUsersRequest = directMessagesService
@ -156,7 +164,7 @@ public class DirectSettingsViewModel extends AndroidViewModel {
@Override @Override
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) { public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
if (!response.isSuccessful()) { if (!response.isSuccessful()) {
handleAdminChangeResponseError(response, data); handleSettingChangeResponseError(response, data);
return; return;
} }
Pair<List<User>, List<User>> usersValue = users.getValue(); Pair<List<User>, List<User>> usersValue = users.getValue();
@ -198,7 +206,7 @@ public class DirectSettingsViewModel extends AndroidViewModel {
@Override @Override
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) { public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
if (!response.isSuccessful()) { if (!response.isSuccessful()) {
handleAdminChangeResponseError(response, data); handleSettingChangeResponseError(response, data);
return; return;
} }
final List<Long> currentAdmins = adminUserIds.getValue(); final List<Long> currentAdmins = adminUserIds.getValue();
@ -225,7 +233,7 @@ public class DirectSettingsViewModel extends AndroidViewModel {
@Override @Override
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) { public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
if (!response.isSuccessful()) { if (!response.isSuccessful()) {
handleAdminChangeResponseError(response, data); handleSettingChangeResponseError(response, data);
return; return;
} }
final List<Long> currentAdmins = adminUserIds.getValue(); final List<Long> currentAdmins = adminUserIds.getValue();
@ -244,8 +252,124 @@ public class DirectSettingsViewModel extends AndroidViewModel {
return data; return data;
} }
private void handleAdminChangeResponseError(@NonNull final Response<String> response, public LiveData<Resource<Object>> mute() {
final MutableLiveData<Resource<Object>> data) { final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
data.postValue(Resource.loading(null));
if (thread.isMuted()) {
data.postValue(Resource.success(new Object()));
return data;
}
final Call<String> request = directMessagesService.mute(thread.getThreadId());
request.enqueue(new Callback<String>() {
@Override
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
if (!response.isSuccessful()) {
handleSettingChangeResponseError(response, data);
return;
}
thread.setMuted(true);
muted.postValue(true);
data.postValue(Resource.success(new Object()));
}
@Override
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
Log.e(TAG, "onFailure: ", t);
data.postValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
public LiveData<Resource<Object>> unmute() {
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
data.postValue(Resource.loading(null));
if (!thread.isMuted()) {
data.postValue(Resource.success(new Object()));
return data;
}
final Call<String> request = directMessagesService.unmute(thread.getThreadId());
request.enqueue(new Callback<String>() {
@Override
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
if (!response.isSuccessful()) {
handleSettingChangeResponseError(response, data);
return;
}
thread.setMuted(false);
muted.postValue(false);
data.postValue(Resource.success(new Object()));
}
@Override
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
Log.e(TAG, "onFailure: ", t);
data.postValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
public LiveData<Resource<Object>> muteMentions() {
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
data.postValue(Resource.loading(null));
if (thread.isMentionsMuted()) {
data.postValue(Resource.success(new Object()));
return data;
}
final Call<String> request = directMessagesService.muteMentions(thread.getThreadId());
request.enqueue(new Callback<String>() {
@Override
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
if (!response.isSuccessful()) {
handleSettingChangeResponseError(response, data);
return;
}
thread.setMentionsMuted(true);
mentionsMuted.postValue(true);
data.postValue(Resource.success(new Object()));
}
@Override
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
Log.e(TAG, "onFailure: ", t);
data.postValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
public LiveData<Resource<Object>> unmuteMentions() {
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
data.postValue(Resource.loading(null));
if (!thread.isMentionsMuted()) {
data.postValue(Resource.success(new Object()));
return data;
}
final Call<String> request = directMessagesService.unmuteMentions(thread.getThreadId());
request.enqueue(new Callback<String>() {
@Override
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
if (!response.isSuccessful()) {
handleSettingChangeResponseError(response, data);
return;
}
thread.setMentionsMuted(false);
mentionsMuted.postValue(false);
data.postValue(Resource.success(new Object()));
}
@Override
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
Log.e(TAG, "onFailure: ", t);
data.postValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
private void handleSettingChangeResponseError(@NonNull final Response<String> response,
final MutableLiveData<Resource<Object>> data) {
final ResponseBody errorBody = response.errorBody(); final ResponseBody errorBody = response.errorBody();
if (errorBody == null) { if (errorBody == null) {
handleErrorResponse(response, data); handleErrorResponse(response, data);

View File

@ -317,4 +317,36 @@ public class DirectMessagesService extends BaseService {
final Map<String, String> signedForm = Utils.sign(formBuilder.build()); final Map<String, String> signedForm = Utils.sign(formBuilder.build());
return repository.createThread(signedForm); return repository.createThread(signedForm);
} }
public Call<String> mute(@NonNull final String threadId) {
final ImmutableMap<String, String> form = ImmutableMap.of(
"_csrftoken", csrfToken,
"_uuid", deviceUuid
);
return repository.mute(threadId, form);
}
public Call<String> unmute(@NonNull final String threadId) {
final ImmutableMap<String, String> form = ImmutableMap.of(
"_csrftoken", csrfToken,
"_uuid", deviceUuid
);
return repository.unmute(threadId, form);
}
public Call<String> muteMentions(@NonNull final String threadId) {
final ImmutableMap<String, String> form = ImmutableMap.of(
"_csrftoken", csrfToken,
"_uuid", deviceUuid
);
return repository.muteMentions(threadId, form);
}
public Call<String> unmuteMentions(@NonNull final String threadId) {
final ImmutableMap<String, String> form = ImmutableMap.of(
"_csrftoken", csrfToken,
"_uuid", deviceUuid
);
return repository.unmuteMentions(threadId, form);
}
} }

View File

@ -50,11 +50,11 @@
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.button.MaterialButton <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/mute_messages_label" android:id="@+id/mute_messages_label"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="0dp"
android:background="?selectableItemBackground"
android:gravity="center_vertical" android:gravity="center_vertical"
android:paddingStart="16dp" android:paddingStart="16dp"
android:paddingEnd="16dp" android:paddingEnd="16dp"
@ -70,18 +70,18 @@
android:id="@+id/mute_messages" android:id="@+id/mute_messages"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_marginEnd="8dp"
android:paddingStart="0dp" android:paddingStart="0dp"
android:paddingEnd="8dp" android:paddingEnd="8dp"
android:layout_margin="8dp"
app:layout_constraintBottom_toTopOf="@id/mute_mentions" app:layout_constraintBottom_toTopOf="@id/mute_mentions"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/title_edit_input_layout" /> app:layout_constraintTop_toBottomOf="@id/title_edit_input_layout" />
<com.google.android.material.button.MaterialButton <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/mute_mentions_label" android:id="@+id/mute_mentions_label"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="0dp"
android:gravity="center_vertical" android:gravity="center_vertical"
android:paddingStart="16dp" android:paddingStart="16dp"
android:paddingEnd="16dp" android:paddingEnd="16dp"
@ -97,44 +97,47 @@
android:id="@+id/mute_mentions" android:id="@+id/mute_mentions"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_marginEnd="8dp"
android:paddingStart="0dp" android:paddingStart="0dp"
android:paddingEnd="8dp" android:paddingEnd="8dp"
android:layout_margin="8dp"
app:layout_constraintBottom_toTopOf="@id/leave" app:layout_constraintBottom_toTopOf="@id/leave"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/mute_messages" /> app:layout_constraintTop_toBottomOf="@id/mute_messages" />
<com.google.android.material.button.MaterialButton <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/leave" android:id="@+id/leave"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:background="?selectableItemBackground"
android:gravity="center_vertical" android:gravity="center_vertical"
android:paddingStart="16dp" android:paddingStart="16dp"
android:paddingTop="8dp" android:paddingTop="12dp"
android:paddingEnd="16dp" android:paddingEnd="16dp"
android:paddingBottom="8dp" android:paddingBottom="12dp"
android:text="@string/dms_action_leave" android:text="@string/dms_action_leave"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1" android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
android:textColor="@color/red_600" android:textColor="@color/red_600"
app:layout_constraintBottom_toTopOf="@id/add_members" app:layout_constraintBottom_toTopOf="@id/add_members"
app:layout_constraintTop_toBottomOf="@id/mute_mentions" /> app:layout_constraintTop_toBottomOf="@id/mute_mentions" />
<com.google.android.material.button.MaterialButton <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/add_members" android:id="@+id/add_members"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:background="?selectableItemBackground"
android:gravity="center_vertical" android:gravity="center_vertical"
android:paddingStart="16dp" android:paddingStart="16dp"
android:paddingTop="8dp" android:paddingTop="12dp"
android:paddingEnd="16dp" android:paddingEnd="16dp"
android:paddingBottom="8dp" android:paddingBottom="12dp"
android:text="@string/add_members" android:text="@string/add_members"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1" android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
android:textColor="?android:textColorPrimary" android:textColor="?android:textColorPrimary"
app:icon="@drawable/ic_add" app:drawableStartCompat="@drawable/ic_add"
app:iconTint="?android:textColorPrimary" app:drawableTint="?android:textColorPrimary"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/leave" /> app:layout_constraintTop_toBottomOf="@id/leave" />