From 8405834a5db1701120e65d17997b14e91853626b Mon Sep 17 00:00:00 2001 From: Stefan Niedermann Date: Mon, 15 Jan 2024 19:16:04 +0100 Subject: chore(done): Limit done support to Deck server 1.12.0 and higher Refs: #1556 Signed-off-by: Stefan Niedermann --- .../nextcloud/deck/database/dao/CardDao.java | 2 +- .../deck/database/migration/Migration_32_33.java | 2 +- .../nextcloud/deck/model/ocs/Version.java | 15 +- .../ui/card/comments/ItemCommentViewHolder.java | 2 +- .../deck/ui/card/details/CardDetailsFragment.java | 9 +- .../deck/ui/card/details/CardDueDateView.java | 165 +++++++++++---------- 6 files changed, 107 insertions(+), 88 deletions(-) (limited to 'app/src/main/java/it') diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/database/dao/CardDao.java b/app/src/main/java/it/niedermann/nextcloud/deck/database/dao/CardDao.java index 5d25f943f..d3d6f4060 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/database/dao/CardDao.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/database/dao/CardDao.java @@ -23,7 +23,7 @@ public interface CardDao extends GenericDao { "and (c.deletedAt is null or c.deletedAt = 0) " + "and (s.deletedAt is null or s.deletedAt = 0) " + "and (b.deletedAt is null or b.deletedAt = 0) " + - // FUll Logic: (hasDueDate AND isIn_PRIVATE_Board) OR (isInSharedBoard AND (assignedToMe OR (hasDueDate AND noAssignees))) + // Full Logic: (hasDueDate AND isIn_PRIVATE_Board) OR (isInSharedBoard AND (assignedToMe OR (hasDueDate AND noAssignees))) "and (" + "(c.dueDate is not null AND NOT exists(select 1 from AccessControl ac where ac.boardId = b.localId and ac.status <> 3))" + //(hasDueDate AND isInPrivateBoard) "OR (" + diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/database/migration/Migration_32_33.java b/app/src/main/java/it/niedermann/nextcloud/deck/database/migration/Migration_32_33.java index c19b06715..d6938dda6 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/database/migration/Migration_32_33.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/database/migration/Migration_32_33.java @@ -16,7 +16,7 @@ public class Migration_32_33 extends Migration { @Override public void migrate(@NonNull SupportSQLiteDatabase database) { database.execSQL("ALTER TABLE `Card` add column done INTEGER"); - // reset etags, so cards will be fetched after app-update + // Reset ETags: Refetch all cards to support Done state which did not change ETags database.execSQL("UPDATE `Account` SET `boardsEtag` = NULL"); database.execSQL("UPDATE `Board` SET `etag` = NULL"); database.execSQL("UPDATE `Stack` SET `etag` = NULL"); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/model/ocs/Version.java b/app/src/main/java/it/niedermann/nextcloud/deck/model/ocs/Version.java index 355307ce0..a0f375329 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/model/ocs/Version.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/model/ocs/Version.java @@ -9,6 +9,7 @@ import java.util.regex.Pattern; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.model.Attachment; +import it.niedermann.nextcloud.deck.model.Card; import it.niedermann.nextcloud.deck.model.ocs.comment.DeckComment; public class Version implements Comparable { @@ -17,6 +18,7 @@ public class Version implements Comparable { private static final Version VERSION_1_0_0 = new Version("1.0.0", 1, 0, 0); private static final Version VERSION_1_0_3 = new Version("1.0.3", 1, 0, 3); private static final Version VERSION_1_3_0 = new Version("1.3.0", 1, 3, 0); + private static final Version VERSION_1_12_0 = new Version("1.12.0", 1, 12, 0); private String originalVersion = "?"; private final int major; @@ -148,11 +150,11 @@ public class Version implements Comparable { /** * Replying to a {@link DeckComment} does cause synchronization errors because the API expected the * parentId to be a {@link String} up until {@link Version} 1.0.3 - * https://github.com/nextcloud/deck/issues/1831#issuecomment-627207849 * * @return whether or not the server supports replying to comments + * @see Deck server issue #1831 */ - public boolean supportsCommentsReplys() { + public boolean supportsCommentsReplies() { return isGreaterOrEqualTo(VERSION_1_0_3); } @@ -170,6 +172,15 @@ public class Version implements Comparable { return isGreaterOrEqualTo(VERSION_1_3_0); } + /** + * Cards started to have an additional property called done with {@link #VERSION_1_12_0}. + * + * @return whether or not the server supports the {@link Card#getDone()} state + */ + public boolean supportsDone() { + return isGreaterOrEqualTo(VERSION_1_12_0); + } + /** * Title max length has been increased from 100 to 255 characters beginning with server {@link Version} 1.0.0 * diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/ItemCommentViewHolder.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/ItemCommentViewHolder.java index 221beea76..e2bf7a84b 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/ItemCommentViewHolder.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/ItemCommentViewHolder.java @@ -64,7 +64,7 @@ public class ItemCommentViewHolder extends RecyclerView.ViewHolder { inflater.inflate(R.menu.comment_menu, menu); menu.findItem(android.R.id.copy).setOnMenuItemClickListener(item -> ClipboardUtil.INSTANCE.copyToClipboard(itemView.getContext(), comment.getComment().getMessage())); final var replyMenuItem = menu.findItem(R.id.reply); - if (comment.getStatusEnum() != DBStatus.LOCAL_EDITED && account.getServerDeckVersionAsObject().supportsCommentsReplys()) { + if (comment.getStatusEnum() != DBStatus.LOCAL_EDITED && account.getServerDeckVersionAsObject().supportsCommentsReplies()) { replyMenuItem.setOnMenuItemClickListener(item -> { selectAsReplyListener.onSelectAsReply(comment); return true; diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/details/CardDetailsFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/details/CardDetailsFragment.java index f5b7149e6..4401eaf08 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/details/CardDetailsFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/details/CardDetailsFragment.java @@ -183,24 +183,27 @@ public class CardDetailsFragment extends Fragment implements CardDueDateView.Due } private void setupDueDate() { + final var version = this.viewModel.getAccount().getServerDeckVersionAsObject(); final var card = this.viewModel.getFullCard().getCard(); binding.cardDueDateView.setDueDateListener(this); binding.cardDueDateView.setEnabled(this.viewModel.canEdit()); - binding.cardDueDateView.setDueDate(getChildFragmentManager(), card.getDueDate(), card.getDone()); + binding.cardDueDateView.setDueDate(getChildFragmentManager(), version, card.getDueDate(), card.getDone()); } @Override public void onDueDateChanged(@Nullable Instant dueDate) { + final var version = this.viewModel.getAccount().getServerDeckVersionAsObject(); final var card = this.viewModel.getFullCard().getCard(); card.setDueDate(dueDate); - binding.cardDueDateView.setDueDate(getChildFragmentManager(), card.getDueDate(), card.getDone()); + binding.cardDueDateView.setDueDate(getChildFragmentManager(), version, card.getDueDate(), card.getDone()); } @Override public void onDoneChanged(@Nullable Instant done) { + final var version = this.viewModel.getAccount().getServerDeckVersionAsObject(); final var card = this.viewModel.getFullCard().getCard(); card.setDone(done); - binding.cardDueDateView.setDueDate(getChildFragmentManager(), card.getDueDate(), card.getDone()); + binding.cardDueDateView.setDueDate(getChildFragmentManager(), version, card.getDueDate(), card.getDone()); } private void setupLabels(@NonNull Account account) { diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/details/CardDueDateView.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/details/CardDueDateView.java index 147b1032a..a9f7e02c2 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/details/CardDueDateView.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/details/CardDueDateView.java @@ -27,6 +27,7 @@ import java.util.stream.Stream; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.ViewCardDueDateBinding; +import it.niedermann.nextcloud.deck.model.ocs.Version; import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils; import it.niedermann.nextcloud.deck.ui.theme.Themed; import it.niedermann.nextcloud.deck.ui.theme.ThemedDatePickerDialog; @@ -40,6 +41,7 @@ public class CardDueDateView extends FrameLayout implements DatePickerDialog.OnD private final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("d. MMM yyyy HH:mm"); private final DateTimeFormatter dateFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM); private final DateTimeFormatter timeFormatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT); + boolean supportsDone = false; @Nullable private Instant dueDate = null; @Nullable @@ -64,6 +66,9 @@ public class CardDueDateView extends FrameLayout implements DatePickerDialog.OnD binding = ViewCardDueDateBinding.inflate(LayoutInflater.from(context), this, true); } + /** + * @noinspection unused + */ public CardDueDateView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); binding = ViewCardDueDateBinding.inflate(LayoutInflater.from(context), this, true); @@ -77,8 +82,9 @@ public class CardDueDateView extends FrameLayout implements DatePickerDialog.OnD } } - public void setDueDate(@NonNull FragmentManager fragmentManager, @Nullable Instant dueDate, @Nullable Instant done) { + public void setDueDate(@NonNull FragmentManager fragmentManager, @NonNull Version version, @Nullable Instant dueDate, @Nullable Instant done) { this.fragmentManager = fragmentManager; + this.supportsDone = version.supportsDone(); this.dueDate = dueDate; this.done = done; render(); @@ -90,6 +96,72 @@ public class CardDueDateView extends FrameLayout implements DatePickerDialog.OnD setInteraction(); } + private void setVisibilityState() { + if (done == null) { + Stream.of( + binding.dueDateDateWrapper, + binding.dueDateTimeWrapper + ).forEach(v -> v.setVisibility(View.VISIBLE)); + + Stream.of( + binding.doneCheck, + binding.doneDueDate, + binding.doneDate, + binding.clearDone + ).forEach(v -> v.setVisibility(View.GONE)); + + binding.markAsDone.setVisibility(supportsDone ? View.VISIBLE : View.GONE); + binding.clearDueDate.setVisibility(dueDate == null || !isEnabled() ? View.GONE : View.VISIBLE); + } else { + + Stream.of( + binding.doneCheck, + binding.doneDate, + binding.clearDone + ).forEach(v -> v.setVisibility(View.VISIBLE)); + + Stream.of( + binding.markAsDone, + binding.dueDateDateWrapper, + binding.dueDateTimeWrapper, + binding.clearDueDate + ).forEach(v -> v.setVisibility(View.GONE)); + + binding.doneDueDate.setVisibility(dueDate == null || !isEnabled() ? View.GONE : View.VISIBLE); + } + } + + private void setTextState() { + if (done == null) { + binding.doneDate.setText(null); + binding.doneDueDate.setText(null); + + if (this.dueDate == null) { + binding.dueDateDate.setText(null); + binding.dueDateTime.setText(null); + + } else { + final var dueDate = this.dueDate.atZone(ZoneId.systemDefault()); + binding.dueDateDate.setText(dueDate.format(dateFormatter)); + binding.dueDateTime.setText(dueDate.format(timeFormatter)); + } + + } else { + binding.dueDateDate.setText(null); + binding.dueDateTime.setText(null); + + binding.doneDate.setText(done.atZone(ZoneId.systemDefault()).format(dateTimeFormatter)); + + if (this.dueDate == null) { + binding.doneDueDate.setText(null); + + } else { + final var dueDate = this.dueDate.atZone(ZoneId.systemDefault()); + binding.doneDueDate.setText(getContext().getString(R.string.label_due_at, dueDate.format(dateTimeFormatter))); + } + } + } + private void setInteraction() { final var enabled = isEnabled(); @@ -103,11 +175,15 @@ public class CardDueDateView extends FrameLayout implements DatePickerDialog.OnD } }); - binding.markAsDone.setOnClickListener(v -> { - if (this.dueDateChangedListener != null) { - this.dueDateChangedListener.onDoneChanged(Instant.now()); - } - }); + if (supportsDone) { + binding.markAsDone.setOnClickListener(v -> { + if (this.dueDateChangedListener != null) { + this.dueDateChangedListener.onDoneChanged(Instant.now()); + } + }); + } else { + binding.markAsDone.setOnClickListener(null); + } binding.clearDueDate.setOnClickListener(v -> { if (this.dueDateChangedListener != null) { @@ -154,72 +230,6 @@ public class CardDueDateView extends FrameLayout implements DatePickerDialog.OnD } } - private void setTextState() { - if (done == null) { - binding.doneDate.setText(null); - binding.doneDueDate.setText(null); - - if (this.dueDate == null) { - binding.dueDateDate.setText(null); - binding.dueDateTime.setText(null); - - } else { - final var dueDate = this.dueDate.atZone(ZoneId.systemDefault()); - binding.dueDateDate.setText(dueDate.format(dateFormatter)); - binding.dueDateTime.setText(dueDate.format(timeFormatter)); - } - - } else { - binding.dueDateDate.setText(null); - binding.dueDateTime.setText(null); - - binding.doneDate.setText(done.atZone(ZoneId.systemDefault()).format(dateTimeFormatter)); - - if (this.dueDate == null) { - binding.doneDueDate.setText(null); - - } else { - final var dueDate = this.dueDate.atZone(ZoneId.systemDefault()); - binding.doneDueDate.setText(getContext().getString(R.string.label_due_at, dueDate.format(dateTimeFormatter))); - } - } - } - - private void setVisibilityState() { - if (done == null) { - Stream.of( - binding.markAsDone, - binding.dueDateDateWrapper, - binding.dueDateTimeWrapper - ).forEach(v -> v.setVisibility(View.VISIBLE)); - - Stream.of( - binding.doneCheck, - binding.doneDueDate, - binding.doneDate, - binding.clearDone - ).forEach(v -> v.setVisibility(View.GONE)); - - binding.clearDueDate.setVisibility(dueDate == null || !isEnabled() ? View.GONE : View.VISIBLE); - } else { - - Stream.of( - binding.doneCheck, - binding.doneDate, - binding.clearDone - ).forEach(v -> v.setVisibility(View.VISIBLE)); - - Stream.of( - binding.markAsDone, - binding.dueDateDateWrapper, - binding.dueDateTimeWrapper, - binding.clearDueDate - ).forEach(v -> v.setVisibility(View.GONE)); - - binding.doneDueDate.setVisibility(dueDate == null || !isEnabled() ? View.GONE : View.VISIBLE); - } - } - @Override public void onDateSet(DatePickerDialog view, int year, int monthOfYear, int dayOfMonth) { int hourOfDay; @@ -230,6 +240,7 @@ public class CardDueDateView extends FrameLayout implements DatePickerDialog.OnD hourOfDay = 0; minute = 0; } else { + assert this.dueDate != null; // Since selectedTime is not empty and is derived from dueDate, dueDate itself shouldn't be null here. final var oldTime = LocalTime.from(this.dueDate.atZone(ZoneId.systemDefault())); hourOfDay = oldTime.getHour(); minute = oldTime.getMinute(); @@ -269,19 +280,13 @@ public class CardDueDateView extends FrameLayout implements DatePickerDialog.OnD binding.dueDateTimeWrapper ).forEach(utils.material::colorTextInputLayout); -// Stream.of( -// binding.doneDueDate, -// binding.doneDate -// ).forEach(v -> utils.platform.colorTextView(v, ColorRole.ON_SURFACE)); - - utils.platform.colorTextView(binding.doneDate, ColorRole.ON_SURFACE); - utils.platform.colorTextView(binding.doneDueDate, ColorRole.ON_SURFACE_VARIANT); - Stream.of( binding.clearDone, binding.clearDueDate ).forEach(v -> utils.platform.colorImageView(v, ColorRole.SECONDARY)); + utils.platform.colorTextView(binding.doneDate, ColorRole.ON_SURFACE); + utils.platform.colorTextView(binding.doneDueDate, ColorRole.ON_SURFACE_VARIANT); utils.material.colorMaterialButtonPrimaryTonal(binding.markAsDone); } -- cgit v1.2.3