Warn when scheduling a post within 5 minutes (#1698)
* Warn when scheduling a post within 5 minutes * Fix NPE when scheduled post time isn't set * Use AlertDialog with option to cancel instead of Toast when a post isn't scheduled far enough in advance * Move schedule validation warning to scheduling bottom sheet * Fix scheduling error display when sending after an initially-valid scheduling time has become invalid
This commit is contained in:
parent
aba36ca6f8
commit
80e0c55b67
4 changed files with 87 additions and 20 deletions
|
@ -65,6 +65,7 @@ import com.keylesspalace.tusky.adapter.OnEmojiSelectedListener
|
|||
import com.keylesspalace.tusky.components.compose.dialog.makeCaptionDialog
|
||||
import com.keylesspalace.tusky.components.compose.dialog.showAddPollDialog
|
||||
import com.keylesspalace.tusky.components.compose.view.ComposeOptionsListener
|
||||
import com.keylesspalace.tusky.components.compose.view.ComposeScheduleView
|
||||
import com.keylesspalace.tusky.db.AccountEntity
|
||||
import com.keylesspalace.tusky.di.Injectable
|
||||
import com.keylesspalace.tusky.di.ViewModelFactory
|
||||
|
@ -695,9 +696,16 @@ class ComposeActivity : BaseActivity(),
|
|||
updateVisibleCharactersLeft()
|
||||
}
|
||||
|
||||
private fun verifyScheduledTime(): Boolean {
|
||||
return composeScheduleView.verifyScheduledTime(composeScheduleView.getDateTime(viewModel.scheduledAt.value))
|
||||
}
|
||||
|
||||
private fun onSendClicked() {
|
||||
enableButtons(false)
|
||||
sendStatus()
|
||||
if (verifyScheduledTime()) {
|
||||
sendStatus()
|
||||
} else {
|
||||
showScheduleView()
|
||||
}
|
||||
}
|
||||
|
||||
/** This is for the fancy keyboards which can insert images and stuff. */
|
||||
|
@ -723,6 +731,7 @@ class ComposeActivity : BaseActivity(),
|
|||
}
|
||||
|
||||
private fun sendStatus() {
|
||||
enableButtons(false)
|
||||
val contentText = composeEditField.text.toString()
|
||||
var spoilerText = ""
|
||||
if (viewModel.showContentWarning.value!!) {
|
||||
|
@ -974,7 +983,11 @@ class ComposeActivity : BaseActivity(),
|
|||
override fun onTimeSet(view: TimePicker, hourOfDay: Int, minute: Int) {
|
||||
composeScheduleView.onTimeSet(hourOfDay, minute)
|
||||
viewModel.updateScheduledAt(composeScheduleView.time)
|
||||
scheduleBehavior.state = BottomSheetBehavior.STATE_HIDDEN
|
||||
if (verifyScheduledTime()) {
|
||||
scheduleBehavior.state = BottomSheetBehavior.STATE_HIDDEN
|
||||
} else {
|
||||
showScheduleView()
|
||||
}
|
||||
}
|
||||
|
||||
private fun resetSchedule() {
|
||||
|
|
|
@ -22,6 +22,8 @@ import android.util.AttributeSet;
|
|||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
|
||||
|
@ -48,8 +50,10 @@ public class ComposeScheduleView extends ConstraintLayout {
|
|||
|
||||
private Button resetScheduleButton;
|
||||
private TextView scheduledDateTimeView;
|
||||
private TextView invalidScheduleWarningView;
|
||||
|
||||
private Calendar scheduleDateTime;
|
||||
public static int MINIMUM_SCHEDULED_SECONDS = 330; // Minimum is 5 minutes, pad 30 seconds for posting
|
||||
|
||||
public ComposeScheduleView(Context context) {
|
||||
super(context);
|
||||
|
@ -76,8 +80,10 @@ public class ComposeScheduleView extends ConstraintLayout {
|
|||
|
||||
resetScheduleButton = findViewById(R.id.resetScheduleButton);
|
||||
scheduledDateTimeView = findViewById(R.id.scheduledDateTime);
|
||||
invalidScheduleWarningView = findViewById(R.id.invalidScheduleWarning);
|
||||
|
||||
scheduledDateTimeView.setOnClickListener(v -> openPickDateDialog());
|
||||
invalidScheduleWarningView.setText(R.string.warning_scheduling_interval);
|
||||
|
||||
scheduleDateTime = null;
|
||||
|
||||
|
@ -89,10 +95,13 @@ public class ComposeScheduleView extends ConstraintLayout {
|
|||
private void setScheduledDateTime() {
|
||||
if (scheduleDateTime == null) {
|
||||
scheduledDateTimeView.setText("");
|
||||
invalidScheduleWarningView.setVisibility(GONE);
|
||||
} else {
|
||||
Date scheduled = scheduleDateTime.getTime();
|
||||
scheduledDateTimeView.setText(String.format("%s %s",
|
||||
dateFormat.format(scheduleDateTime.getTime()),
|
||||
timeFormat.format(scheduleDateTime.getTime())));
|
||||
dateFormat.format(scheduled),
|
||||
timeFormat.format(scheduled)));
|
||||
verifyScheduledTime(scheduled);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -124,9 +133,7 @@ public class ComposeScheduleView extends ConstraintLayout {
|
|||
.setValidator(
|
||||
DateValidatorPointForward.from(yesterday))
|
||||
.build();
|
||||
if (scheduleDateTime == null) {
|
||||
scheduleDateTime = Calendar.getInstance(TimeZone.getDefault());
|
||||
}
|
||||
initializeSuggestedTime();
|
||||
MaterialDatePicker<Long> picker = MaterialDatePicker.Builder
|
||||
.datePicker()
|
||||
.setSelection(scheduleDateTime.getTimeInMillis())
|
||||
|
@ -147,6 +154,16 @@ public class ComposeScheduleView extends ConstraintLayout {
|
|||
picker.show(((AppCompatActivity) getContext()).getSupportFragmentManager(), "time_picker");
|
||||
}
|
||||
|
||||
public Date getDateTime(String scheduledAt) {
|
||||
if (scheduledAt != null) {
|
||||
try {
|
||||
return iso8601.parse(scheduledAt);
|
||||
} catch (ParseException e) {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setDateTime(String scheduledAt) {
|
||||
Date date;
|
||||
try {
|
||||
|
@ -154,27 +171,34 @@ public class ComposeScheduleView extends ConstraintLayout {
|
|||
} catch (ParseException e) {
|
||||
return;
|
||||
}
|
||||
if (scheduleDateTime == null) {
|
||||
scheduleDateTime = Calendar.getInstance(TimeZone.getDefault());
|
||||
}
|
||||
initializeSuggestedTime();
|
||||
scheduleDateTime.setTime(date);
|
||||
setScheduledDateTime();
|
||||
}
|
||||
|
||||
private void onDateSet(long selection) {
|
||||
if (scheduleDateTime == null) {
|
||||
scheduleDateTime = Calendar.getInstance(TimeZone.getDefault());
|
||||
public boolean verifyScheduledTime(@Nullable Date scheduledTime) {
|
||||
boolean valid;
|
||||
if (scheduledTime != null) {
|
||||
Calendar minimumScheduledTime = getCalendar();
|
||||
minimumScheduledTime.add(Calendar.SECOND, MINIMUM_SCHEDULED_SECONDS);
|
||||
valid = scheduledTime.after(minimumScheduledTime.getTime());
|
||||
} else {
|
||||
valid = true;
|
||||
}
|
||||
Calendar newDate = Calendar.getInstance(TimeZone.getDefault());
|
||||
invalidScheduleWarningView.setVisibility(valid ? GONE : VISIBLE);
|
||||
return valid;
|
||||
}
|
||||
|
||||
private void onDateSet(long selection) {
|
||||
initializeSuggestedTime();
|
||||
Calendar newDate = getCalendar();
|
||||
newDate.setTimeInMillis(selection);
|
||||
scheduleDateTime.set(newDate.get(Calendar.YEAR), newDate.get(Calendar.MONTH), newDate.get(Calendar.DATE));
|
||||
openPickTimeDialog();
|
||||
}
|
||||
|
||||
public void onTimeSet(int hourOfDay, int minute) {
|
||||
if (scheduleDateTime == null) {
|
||||
scheduleDateTime = Calendar.getInstance(TimeZone.getDefault());
|
||||
}
|
||||
initializeSuggestedTime();
|
||||
scheduleDateTime.set(Calendar.HOUR_OF_DAY, hourOfDay);
|
||||
scheduleDateTime.set(Calendar.MINUTE, minute);
|
||||
setScheduledDateTime();
|
||||
|
@ -186,4 +210,16 @@ public class ComposeScheduleView extends ConstraintLayout {
|
|||
}
|
||||
return iso8601.format(scheduleDateTime.getTime());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Calendar getCalendar() {
|
||||
return Calendar.getInstance(TimeZone.getDefault());
|
||||
}
|
||||
|
||||
private void initializeSuggestedTime() {
|
||||
if (scheduleDateTime == null) {
|
||||
scheduleDateTime = getCalendar();
|
||||
scheduleDateTime.add(Calendar.MINUTE, 15);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:text="@string/action_reset_schedule"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/invalidScheduleWarning"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<TextView
|
||||
|
@ -23,10 +23,27 @@
|
|||
android:paddingBottom="16dp"
|
||||
android:textColor="?android:textColorTertiary"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/invalidScheduleWarning"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="1"
|
||||
app:layout_constraintStart_toEndOf="@id/resetScheduleButton"
|
||||
tools:text="2020/01/01 00:00:00" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/invalidScheduleWarning"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawablePadding="4dp"
|
||||
android:paddingStart="4dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingBottom="16dp"
|
||||
android:textColor="?android:textColorTertiary"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="1"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:text="@string/warning_scheduling_interval"
|
||||
android:visibility="gone" />
|
||||
|
||||
</merge>
|
|
@ -547,5 +547,6 @@
|
|||
|
||||
<string name="no_saved_status">You don\'t have any drafts.</string>
|
||||
<string name="no_scheduled_status">You don\'t have any scheduled statuses.</string>
|
||||
<string name="warning_scheduling_interval">Mastodon has a minimum scheduling interval of 5 minutes.</string>
|
||||
|
||||
</resources>
|
||||
|
|
Loading…
Reference in a new issue