-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Value class deck #17718
base: main
Are you sure you want to change the base?
Value class deck #17718
Conversation
b3b8b54
to
3dec54c
Compare
AnkiDroid/src/main/java/com/ichi2/libanki/backend/BackendUtils.kt
Outdated
Show resolved
Hide resolved
AnkiDroid/src/main/java/com/ichi2/ui/AppCompatPreferenceActivity.kt
Outdated
Show resolved
Hide resolved
3dec54c
to
0f7f2df
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One question on the 1 and 2 discrepancy
AnkiDroid/src/main/java/com/ichi2/anki/dialogs/EditDeckDescriptionDialog.kt
Outdated
Show resolved
Hide resolved
0f7f2df
to
b5d4aa3
Compare
Test added for each property |
292c595
to
4f95714
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The typings for a number of properties are incorrect, and I'm concerned.
previewGoodSecs
is an Int. if you insert it into JSON as a String, it's quoted
} | ||
|
||
"limit" -> { | ||
ar.getJSONArray(0).put(1, value) | ||
deck.firstFilter.limit = value as String |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this a string?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So the reason is that I misunderstood the way this part of the code worked. I thought all values were strings unless converted to other values. I'll update all of that
c9e9db4
to
b7ba1ae
Compare
This comment was marked as resolved.
This comment was marked as resolved.
Hello 👋, this PR has had no activity for more than 2 weeks and needs a reply from the author. If you think this is a mistake please comment and ping a maintainer to get this merged ASAP! Thanks for contributing! You have 7 days until this gets closed automatically |
d4d5bac
to
b397440
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's been a while, but I don't feel my comment introducing a regression test for bugs that this PR previously had was merged:
deck = if (extras?.containsKey("did") == true) { | ||
col.decks.get(extras.getLong("did")) | ||
} else { | ||
null | ||
} ?: col.decks.current() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
deck = extras?.getNullableLong("did")?.let { col.decks.get(it) } ?: col.decks.current()
b397440
to
03db006
Compare
Sorry for missing your test. It's done |
03db006
to
cd6cff9
Compare
The first interest of this change is that we are not deep cloning all decks when creating a Deck object which should save a little bit of time, especially loading time on collection with a lot of decks. Instead, we have a better typing at zero runtime cost. The second interest is that we are certain that the fact that the Deck is implemented as a JSONObject is hidden from the developer using this class. Instead, they can only use the provided extension, or add the one they needs. While most changes are relatively trivial, moving the implementation from everywhere in the code to Deck.kt, there are some extra changes that were needed. I moved `getLongOrNull` to PythonExtension given that it implements the behavior of a Python function we want to reuse, this seems more consistent. As value class can't be lateinit, I hardcoded AppCompatPreferenceActivity as being a getter for the backing field `_deck: Deck?`. I added in testutils/JsonUtils helper method that accepts a json holder, and a string representing the expected json, to simplify the use of those methods. The code used sometime `optLong("id")` and sometime `getLong("id")`. There seems to be no reason for the difference between both use case, and just be random choice from the developer. Anyway, if a deck lacks an id, any place using `getLong("id")` would have caused the code to crash, so replacing the `optLong` by `getLong` would, in the worst case, move the time at which the user is asked to clean their database. There seems to be a single key we sometime set to JSONObject.NULL, it was `delays`. For the sake of typing, the property `delays` in `deck` is thus of type `JSONArray?`. When the value `null` is assigned to this property, `NULL` is saved in the object, and reciprocally. There is no need to check whether `deck` has "empty" key before removing it. It's not even more efficient.
Deck is now declared as an interface so that it can be inherited. `Deck` contains a facotry to decide whether to return a normal deck or a filtered one.
00541f5
to
6e2969e
Compare
I removed your |
Initial implementation by David This test ensures that every changes possibly done in the deck options are correctly applied Co-authored-by: David Allison <[email protected]>
6e2969e
to
3a14dab
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only a few string fixes, doesn't block reviewer 2
} catch (e: RuntimeException) { | ||
Timber.e(e, "RuntimeException on saving deck") | ||
Timber.e(e, "RuntimeException on saving filteredDeck") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
revert
commit() | ||
} | ||
} | ||
} | ||
} | ||
|
||
// save deck | ||
// save filteredDeck |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
revert
registerExternalStorageListener() | ||
if (deck.isNormal) { | ||
Timber.w("Deck is not a dyn deck") | ||
Timber.w("Deck is not a dyn filteredDeck") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Timber.w("Deck is not a dyn filteredDeck") | |
Timber.w("Deck is not filtered") |
} | ||
} | ||
addPreferences(col) | ||
buildLists() | ||
updateSummaries() | ||
} | ||
|
||
// Set the activity title to include the name of the deck | ||
// Set the activity title to include the name of the filteredDeck |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Set the activity title to include the name of the filteredDeck | |
// Set the activity title to include the name of the deck |
@@ -255,13 +286,13 @@ class FilteredDeckOptions : | |||
private fun addPreferences(col: Collection) { | |||
addPreferencesFromResource(R.xml.cram_deck_options) | |||
if (col.schedVer() != 1) { | |||
Timber.d("sched v2: removing filtered deck custom study steps") | |||
Timber.d("sched v2: removing filtered filteredDeck custom study steps") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Timber.d("sched v2: removing filtered filteredDeck custom study steps") | |
Timber.d("sched v2: removing filtered deck custom study steps") |
@@ -280,9 +311,9 @@ class FilteredDeckOptions : | |||
|
|||
override fun closeWithResult() { | |||
if (prefChanged) { | |||
// Rebuild the filtered deck if a setting has changed | |||
// Rebuild the filtered filteredDeck if a setting has changed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Rebuild the filtered filteredDeck if a setting has changed | |
// Rebuild the filtered deck if a setting has changed |
|
||
fun isFiltered(jsonObject: JSONObject) = jsonObject.getInt(DYN) != 0 | ||
|
||
fun factory(jsonObject: JSONObject): Deck = if (isFiltered(jsonObject)) FilteredDeck(jsonObject) else RegularDeck(jsonObject) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doccomment on these factories
if (filtered) { | ||
private fun newDeckLegacy(filtered: Boolean) = | ||
Deck.factory(BackendUtils.fromJsonBytes(col.backend.newDeckLegacy(filtered))).apply { | ||
if (this is FilteredDeck) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm... I'd use filtered
as the boolean, then require(this is Filtered) { "error" }
@@ -378,7 +373,13 @@ class Decks( | |||
/** Falls back on default config if deck or config missing */ | |||
@LibAnkiAlias("config_dict_for_deck_id") | |||
fun configDictForDeckId(did: DeckId): DeckConfig { | |||
val conf = get(did)?.conf ?: 1 | |||
val deck = get(did) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this work:
val conf = (get(did) as? RegularDeck?)?.conf ?: 1
* | ||
* This better approximates `JSON.get` in the Python | ||
*/ | ||
fun JSONObject.getLongOrNull(key: String): Long? { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very similar to a method in java/com/ichi2/anki/utils/ext/JSONObject.kt
I loved David's recent work in using value class. And thought that we could finally kill the overhead cost of DeepClone in JSONObject. So I transformed
Deck
into a value class, and added the needed properties so that the other part of the code can use Deck without considering it as a JSONObject.