Makes the dashboard editable.
This commit is contained in:
@@ -0,0 +1,609 @@
|
|||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 5,
|
||||||
|
"identityHash": "6d497d0f0530c0f46bed1d57492e940e",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"tableName": "monsters",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL DEFAULT '', `size` TEXT NOT NULL DEFAULT '', `type` TEXT NOT NULL DEFAULT '', `subtype` TEXT NOT NULL DEFAULT '', `alignment` TEXT NOT NULL DEFAULT '', `strength_score` INTEGER NOT NULL DEFAULT 10, `strength_saving_throw_advantage` TEXT DEFAULT 'none', `strength_saving_throw_proficiency` TEXT DEFAULT 'none', `dexterity_score` INTEGER NOT NULL DEFAULT 10, `dexterity_saving_throw_advantage` TEXT DEFAULT 'none', `dexterity_saving_throw_proficiency` TEXT DEFAULT 'none', `constitution_score` INTEGER NOT NULL DEFAULT 10, `constitution_saving_throw_advantage` TEXT DEFAULT 'none', `constitution_saving_throw_proficiency` TEXT DEFAULT 'none', `intelligence_score` INTEGER NOT NULL DEFAULT 10, `intelligence_saving_throw_advantage` TEXT DEFAULT 'none', `intelligence_saving_throw_proficiency` TEXT DEFAULT 'none', `wisdom_score` INTEGER NOT NULL DEFAULT 10, `wisdom_saving_throw_advantage` TEXT DEFAULT 'none', `wisdom_saving_throw_proficiency` TEXT DEFAULT 'none', `charisma_score` INTEGER NOT NULL DEFAULT 10, `charisma_saving_throw_advantage` TEXT DEFAULT 'none', `charisma_saving_throw_proficiency` TEXT DEFAULT 'none', `armor_type` TEXT DEFAULT 'none', `shield_bonus` INTEGER NOT NULL DEFAULT 0, `natural_armor_bonus` INTEGER NOT NULL DEFAULT 0, `other_armor_description` TEXT DEFAULT '', `hit_dice` INTEGER NOT NULL DEFAULT 1, `has_custom_hit_points` INTEGER NOT NULL, `custom_hit_points_description` TEXT DEFAULT '', `walk_speed` INTEGER NOT NULL DEFAULT 0, `burrow_speed` INTEGER NOT NULL DEFAULT 0, `climb_speed` INTEGER NOT NULL DEFAULT 0, `fly_speed` INTEGER NOT NULL DEFAULT 0, `can_hover` INTEGER NOT NULL DEFAULT false, `swim_speed` INTEGER NOT NULL DEFAULT 0, `has_custom_speed` INTEGER NOT NULL DEFAULT false, `custom_speed_description` TEXT, `challenge_rating` TEXT DEFAULT '1', `custom_challenge_rating_description` TEXT DEFAULT '', `custom_proficiency_bonus` INTEGER NOT NULL DEFAULT 0, `telepathy_range` INTEGER NOT NULL DEFAULT 0, `understands_but_description` TEXT DEFAULT '', `senses` TEXT DEFAULT '[]', `skills` TEXT DEFAULT '[]', `damage_immunities` TEXT DEFAULT '[]', `damage_resistances` TEXT DEFAULT '[]', `damage_vulnerabilities` TEXT DEFAULT '[]', `condition_immunities` TEXT DEFAULT '[]', `languages` TEXT DEFAULT '[]', `abilities` TEXT DEFAULT '[]', `actions` TEXT DEFAULT '[]', `reactions` TEXT DEFAULT '[]', `lair_actions` TEXT DEFAULT '[]', `legendary_actions` TEXT DEFAULT '[]', `regional_actions` TEXT DEFAULT '[]', PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "''"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "size",
|
||||||
|
"columnName": "size",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "''"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "''"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "subtype",
|
||||||
|
"columnName": "subtype",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "''"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "alignment",
|
||||||
|
"columnName": "alignment",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "''"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "strengthScore",
|
||||||
|
"columnName": "strength_score",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "10"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "strengthSavingThrowAdvantage",
|
||||||
|
"columnName": "strength_saving_throw_advantage",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"defaultValue": "'none'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "strengthSavingThrowProficiency",
|
||||||
|
"columnName": "strength_saving_throw_proficiency",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"defaultValue": "'none'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "dexterityScore",
|
||||||
|
"columnName": "dexterity_score",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "10"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "dexteritySavingThrowAdvantage",
|
||||||
|
"columnName": "dexterity_saving_throw_advantage",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"defaultValue": "'none'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "dexteritySavingThrowProficiency",
|
||||||
|
"columnName": "dexterity_saving_throw_proficiency",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"defaultValue": "'none'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "constitutionScore",
|
||||||
|
"columnName": "constitution_score",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "10"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "constitutionSavingThrowAdvantage",
|
||||||
|
"columnName": "constitution_saving_throw_advantage",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"defaultValue": "'none'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "constitutionSavingThrowProficiency",
|
||||||
|
"columnName": "constitution_saving_throw_proficiency",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"defaultValue": "'none'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "intelligenceScore",
|
||||||
|
"columnName": "intelligence_score",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "10"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "intelligenceSavingThrowAdvantage",
|
||||||
|
"columnName": "intelligence_saving_throw_advantage",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"defaultValue": "'none'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "intelligenceSavingThrowProficiency",
|
||||||
|
"columnName": "intelligence_saving_throw_proficiency",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"defaultValue": "'none'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "wisdomScore",
|
||||||
|
"columnName": "wisdom_score",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "10"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "wisdomSavingThrowAdvantage",
|
||||||
|
"columnName": "wisdom_saving_throw_advantage",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"defaultValue": "'none'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "wisdomSavingThrowProficiency",
|
||||||
|
"columnName": "wisdom_saving_throw_proficiency",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"defaultValue": "'none'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "charismaScore",
|
||||||
|
"columnName": "charisma_score",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "10"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "charismaSavingThrowAdvantage",
|
||||||
|
"columnName": "charisma_saving_throw_advantage",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"defaultValue": "'none'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "charismaSavingThrowProficiency",
|
||||||
|
"columnName": "charisma_saving_throw_proficiency",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"defaultValue": "'none'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "armorType",
|
||||||
|
"columnName": "armor_type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"defaultValue": "'none'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "shieldBonus",
|
||||||
|
"columnName": "shield_bonus",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "naturalArmorBonus",
|
||||||
|
"columnName": "natural_armor_bonus",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "otherArmorDescription",
|
||||||
|
"columnName": "other_armor_description",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"defaultValue": "''"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "hitDice",
|
||||||
|
"columnName": "hit_dice",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "hasCustomHP",
|
||||||
|
"columnName": "has_custom_hit_points",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "customHPDescription",
|
||||||
|
"columnName": "custom_hit_points_description",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"defaultValue": "''"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "walkSpeed",
|
||||||
|
"columnName": "walk_speed",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "burrowSpeed",
|
||||||
|
"columnName": "burrow_speed",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "climbSpeed",
|
||||||
|
"columnName": "climb_speed",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "flySpeed",
|
||||||
|
"columnName": "fly_speed",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "canHover",
|
||||||
|
"columnName": "can_hover",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "swimSpeed",
|
||||||
|
"columnName": "swim_speed",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "hasCustomSpeed",
|
||||||
|
"columnName": "has_custom_speed",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "customSpeedDescription",
|
||||||
|
"columnName": "custom_speed_description",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "challengeRating",
|
||||||
|
"columnName": "challenge_rating",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"defaultValue": "'1'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "customChallengeRatingDescription",
|
||||||
|
"columnName": "custom_challenge_rating_description",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"defaultValue": "''"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "customProficiencyBonus",
|
||||||
|
"columnName": "custom_proficiency_bonus",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "telepathyRange",
|
||||||
|
"columnName": "telepathy_range",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "understandsButDescription",
|
||||||
|
"columnName": "understands_but_description",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"defaultValue": "''"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "senses",
|
||||||
|
"columnName": "senses",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"defaultValue": "'[]'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "skills",
|
||||||
|
"columnName": "skills",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"defaultValue": "'[]'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "damageImmunities",
|
||||||
|
"columnName": "damage_immunities",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"defaultValue": "'[]'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "damageResistances",
|
||||||
|
"columnName": "damage_resistances",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"defaultValue": "'[]'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "damageVulnerabilities",
|
||||||
|
"columnName": "damage_vulnerabilities",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"defaultValue": "'[]'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "conditionImmunities",
|
||||||
|
"columnName": "condition_immunities",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"defaultValue": "'[]'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "languages",
|
||||||
|
"columnName": "languages",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"defaultValue": "'[]'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "abilities",
|
||||||
|
"columnName": "abilities",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"defaultValue": "'[]'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "actions",
|
||||||
|
"columnName": "actions",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"defaultValue": "'[]'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "reactions",
|
||||||
|
"columnName": "reactions",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"defaultValue": "'[]'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lairActions",
|
||||||
|
"columnName": "lair_actions",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"defaultValue": "'[]'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "legendaryActions",
|
||||||
|
"columnName": "legendary_actions",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"defaultValue": "'[]'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "regionalActions",
|
||||||
|
"columnName": "regional_actions",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"defaultValue": "'[]'"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "monsters_fts",
|
||||||
|
"createSql": "CREATE VIRTUAL TABLE IF NOT EXISTS `${TABLE_NAME}` USING FTS4(`name` TEXT, `size` TEXT, `type` TEXT, `subtype` TEXT, `alignment` TEXT, content=`monsters`)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "size",
|
||||||
|
"columnName": "size",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "subtype",
|
||||||
|
"columnName": "subtype",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "alignment",
|
||||||
|
"columnName": "alignment",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": []
|
||||||
|
},
|
||||||
|
"ftsVersion": "FTS4",
|
||||||
|
"ftsOptions": {
|
||||||
|
"tokenizer": "simple",
|
||||||
|
"tokenizerArgs": [],
|
||||||
|
"contentTable": "monsters",
|
||||||
|
"languageIdColumnName": "",
|
||||||
|
"matchInfo": "FTS4",
|
||||||
|
"notIndexedColumns": [],
|
||||||
|
"prefixSizes": [],
|
||||||
|
"preferredOrder": "ASC"
|
||||||
|
},
|
||||||
|
"contentSyncTriggers": [
|
||||||
|
"CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_monsters_fts_BEFORE_UPDATE BEFORE UPDATE ON `monsters` BEGIN DELETE FROM `monsters_fts` WHERE `docid`=OLD.`rowid`; END",
|
||||||
|
"CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_monsters_fts_BEFORE_DELETE BEFORE DELETE ON `monsters` BEGIN DELETE FROM `monsters_fts` WHERE `docid`=OLD.`rowid`; END",
|
||||||
|
"CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_monsters_fts_AFTER_UPDATE AFTER UPDATE ON `monsters` BEGIN INSERT INTO `monsters_fts`(`docid`, `name`, `size`, `type`, `subtype`, `alignment`) VALUES (NEW.`rowid`, NEW.`name`, NEW.`size`, NEW.`type`, NEW.`subtype`, NEW.`alignment`); END",
|
||||||
|
"CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_monsters_fts_AFTER_INSERT AFTER INSERT ON `monsters` BEGIN INSERT INTO `monsters_fts`(`docid`, `name`, `size`, `type`, `subtype`, `alignment`) VALUES (NEW.`rowid`, NEW.`name`, NEW.`size`, NEW.`type`, NEW.`subtype`, NEW.`alignment`); END"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "collections",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL DEFAULT '', `description` TEXT NOT NULL DEFAULT '', PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "''"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "description",
|
||||||
|
"columnName": "description",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "''"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "collection_monsters",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `collection_id` TEXT NOT NULL, `monster_id` TEXT NOT NULL, `ordinal` INTEGER NOT NULL DEFAULT 0, FOREIGN KEY(`collection_id`) REFERENCES `collections`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`monster_id`) REFERENCES `monsters`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "collectionId",
|
||||||
|
"columnName": "collection_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "monsterId",
|
||||||
|
"columnName": "monster_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "ordinal",
|
||||||
|
"columnName": "ordinal",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_collection_monsters_collection_id",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"collection_id"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_collection_monsters_collection_id` ON `${TABLE_NAME}` (`collection_id`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "index_collection_monsters_monster_id",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"monster_id"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_collection_monsters_monster_id` ON `${TABLE_NAME}` (`monster_id`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": [
|
||||||
|
{
|
||||||
|
"table": "collections",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"collection_id"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"table": "monsters",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"monster_id"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "dashboard_monsters",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `monster_id` TEXT NOT NULL, `ordinal` INTEGER NOT NULL DEFAULT 0, FOREIGN KEY(`monster_id`) REFERENCES `monsters`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "monsterId",
|
||||||
|
"columnName": "monster_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "ordinal",
|
||||||
|
"columnName": "ordinal",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true,
|
||||||
|
"defaultValue": "0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_dashboard_monsters_monster_id",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"monster_id"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_dashboard_monsters_monster_id` ON `${TABLE_NAME}` (`monster_id`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": [
|
||||||
|
{
|
||||||
|
"table": "monsters",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"monster_id"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"setupQueries": [
|
||||||
|
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||||
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '6d497d0f0530c0f46bed1d57492e940e')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import androidx.room.RoomDatabase;
|
|||||||
import androidx.room.TypeConverters;
|
import androidx.room.TypeConverters;
|
||||||
|
|
||||||
import com.majinnaibu.monstercards.data.CollectionDAO;
|
import com.majinnaibu.monstercards.data.CollectionDAO;
|
||||||
|
import com.majinnaibu.monstercards.data.DashboardDAO;
|
||||||
import com.majinnaibu.monstercards.data.MonsterDAO;
|
import com.majinnaibu.monstercards.data.MonsterDAO;
|
||||||
import com.majinnaibu.monstercards.data.converters.ArmorTypeConverter;
|
import com.majinnaibu.monstercards.data.converters.ArmorTypeConverter;
|
||||||
import com.majinnaibu.monstercards.data.converters.ChallengeRatingConverter;
|
import com.majinnaibu.monstercards.data.converters.ChallengeRatingConverter;
|
||||||
@@ -15,10 +16,11 @@ import com.majinnaibu.monstercards.data.converters.SetOfStringConverter;
|
|||||||
import com.majinnaibu.monstercards.data.converters.UUIDConverter;
|
import com.majinnaibu.monstercards.data.converters.UUIDConverter;
|
||||||
import com.majinnaibu.monstercards.models.Collection;
|
import com.majinnaibu.monstercards.models.Collection;
|
||||||
import com.majinnaibu.monstercards.models.CollectionMonster;
|
import com.majinnaibu.monstercards.models.CollectionMonster;
|
||||||
|
import com.majinnaibu.monstercards.models.DashboardMonster;
|
||||||
import com.majinnaibu.monstercards.models.Monster;
|
import com.majinnaibu.monstercards.models.Monster;
|
||||||
import com.majinnaibu.monstercards.models.MonsterFTS;
|
import com.majinnaibu.monstercards.models.MonsterFTS;
|
||||||
|
|
||||||
@Database(entities = {Monster.class, MonsterFTS.class, Collection.class, CollectionMonster.class}, version = 4)
|
@Database(entities = {Monster.class, MonsterFTS.class, Collection.class, CollectionMonster.class, DashboardMonster.class}, version = 5)
|
||||||
@TypeConverters({
|
@TypeConverters({
|
||||||
ArmorTypeConverter.class,
|
ArmorTypeConverter.class,
|
||||||
ChallengeRatingConverter.class,
|
ChallengeRatingConverter.class,
|
||||||
@@ -31,4 +33,5 @@ import com.majinnaibu.monstercards.models.MonsterFTS;
|
|||||||
public abstract class AppDatabase extends RoomDatabase {
|
public abstract class AppDatabase extends RoomDatabase {
|
||||||
public abstract MonsterDAO monsterDAO();
|
public abstract MonsterDAO monsterDAO();
|
||||||
public abstract CollectionDAO collectionDAO();
|
public abstract CollectionDAO collectionDAO();
|
||||||
|
public abstract DashboardDAO dashboardDAO();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,14 @@ public class MonsterCardsApplication extends Application {
|
|||||||
database.execSQL("CREATE INDEX IF NOT EXISTS `index_collection_monsters_monster_id` ON `collection_monsters` (`monster_id`)");
|
database.execSQL("CREATE INDEX IF NOT EXISTS `index_collection_monsters_monster_id` ON `collection_monsters` (`monster_id`)");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
private static final Migration MIGRATION_4_5 = new Migration(4, 5) {
|
||||||
|
@Override
|
||||||
|
public void migrate(@NonNull SupportSQLiteDatabase database) {
|
||||||
|
database.execSQL("CREATE TABLE IF NOT EXISTS `dashboard_monsters` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `monster_id` TEXT NOT NULL, `ordinal` INTEGER NOT NULL DEFAULT 0, FOREIGN KEY(`monster_id`) REFERENCES `monsters`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE)");
|
||||||
|
database.execSQL("CREATE INDEX IF NOT EXISTS `index_dashboard_monsters_monster_id` ON `dashboard_monsters` (`monster_id`)");
|
||||||
|
database.execSQL("INSERT INTO dashboard_monsters(monster_id, ordinal) SELECT id, 0 FROM monsters");
|
||||||
|
}
|
||||||
|
};
|
||||||
private MonsterRepository m_monsterLibraryRepository;
|
private MonsterRepository m_monsterLibraryRepository;
|
||||||
|
|
||||||
|
|
||||||
@@ -66,6 +74,7 @@ public class MonsterCardsApplication extends Application {
|
|||||||
.addMigrations(MIGRATION_1_2)
|
.addMigrations(MIGRATION_1_2)
|
||||||
.addMigrations(MIGRATION_2_3)
|
.addMigrations(MIGRATION_2_3)
|
||||||
.addMigrations(MIGRATION_3_4)
|
.addMigrations(MIGRATION_3_4)
|
||||||
|
.addMigrations(MIGRATION_4_5)
|
||||||
.fallbackToDestructiveMigrationOnDowngrade()
|
.fallbackToDestructiveMigrationOnDowngrade()
|
||||||
// .fallbackToDestructiveMigration()
|
// .fallbackToDestructiveMigration()
|
||||||
.build();
|
.build();
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package com.majinnaibu.monstercards.data;
|
||||||
|
|
||||||
|
import androidx.room.Dao;
|
||||||
|
import androidx.room.Insert;
|
||||||
|
import androidx.room.Query;
|
||||||
|
import androidx.room.Update;
|
||||||
|
|
||||||
|
import com.majinnaibu.monstercards.models.DashboardMonster;
|
||||||
|
import com.majinnaibu.monstercards.models.Monster;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import io.reactivex.rxjava3.core.Completable;
|
||||||
|
import io.reactivex.rxjava3.core.Flowable;
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
public interface DashboardDAO {
|
||||||
|
|
||||||
|
@Query("SELECT monsters.* FROM monsters INNER JOIN dashboard_monsters ON monsters.id = dashboard_monsters.monster_id ORDER BY dashboard_monsters.ordinal ASC, dashboard_monsters.id ASC")
|
||||||
|
Flowable<List<Monster>> getDashboardMonsters();
|
||||||
|
|
||||||
|
@Query("SELECT * FROM dashboard_monsters ORDER BY ordinal ASC, id ASC")
|
||||||
|
Flowable<List<DashboardMonster>> getDashboardMonsterEntries();
|
||||||
|
|
||||||
|
@Insert
|
||||||
|
Completable addMonsterToDashboard(DashboardMonster... dashboardMonsters);
|
||||||
|
|
||||||
|
@Query("DELETE FROM dashboard_monsters WHERE id = :id")
|
||||||
|
Completable removeDashboardMonsterById(long id);
|
||||||
|
|
||||||
|
@Query("DELETE FROM dashboard_monsters WHERE monster_id = :monsterId")
|
||||||
|
Completable removeMonsterFromDashboard(String monsterId);
|
||||||
|
|
||||||
|
@Query("DELETE FROM dashboard_monsters")
|
||||||
|
Completable clearDashboard();
|
||||||
|
|
||||||
|
@Update
|
||||||
|
Completable updateDashboardMonsters(List<DashboardMonster> dashboardMonsters);
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import com.majinnaibu.monstercards.helpers.StringHelper;
|
|||||||
import com.majinnaibu.monstercards.models.Collection;
|
import com.majinnaibu.monstercards.models.Collection;
|
||||||
import com.majinnaibu.monstercards.models.CollectionMonster;
|
import com.majinnaibu.monstercards.models.CollectionMonster;
|
||||||
import com.majinnaibu.monstercards.models.CollectionWithCount;
|
import com.majinnaibu.monstercards.models.CollectionWithCount;
|
||||||
|
import com.majinnaibu.monstercards.models.DashboardMonster;
|
||||||
import com.majinnaibu.monstercards.models.Monster;
|
import com.majinnaibu.monstercards.models.Monster;
|
||||||
import com.majinnaibu.monstercards.models.SearchResultItem;
|
import com.majinnaibu.monstercards.models.SearchResultItem;
|
||||||
|
|
||||||
@@ -197,6 +198,68 @@ public class MonsterRepository {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Flowable<List<Monster>> getDashboardMonsters() {
|
||||||
|
return m_db.dashboardDAO()
|
||||||
|
.getDashboardMonsters()
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Flowable<List<DashboardMonster>> getDashboardMonsterEntries() {
|
||||||
|
return m_db.dashboardDAO()
|
||||||
|
.getDashboardMonsterEntries()
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Completable addMonsterToDashboard(@NonNull UUID monsterId) {
|
||||||
|
DashboardMonster entry = new DashboardMonster(monsterId, 0);
|
||||||
|
Completable result = m_db.dashboardDAO().addMonsterToDashboard(entry);
|
||||||
|
result.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Completable addCollectionToDashboard(@NonNull UUID collectionId) {
|
||||||
|
return m_db.collectionDAO().getMonstersForCollection(collectionId.toString())
|
||||||
|
.firstOrError()
|
||||||
|
.flatMapCompletable(monsters -> {
|
||||||
|
if (monsters.isEmpty()) {
|
||||||
|
return Completable.complete();
|
||||||
|
}
|
||||||
|
DashboardMonster[] entries = new DashboardMonster[monsters.size()];
|
||||||
|
for (int i = 0; i < monsters.size(); i++) {
|
||||||
|
entries[i] = new DashboardMonster(monsters.get(i).id, i);
|
||||||
|
}
|
||||||
|
return m_db.dashboardDAO().addMonsterToDashboard(entries);
|
||||||
|
})
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Completable removeMonsterFromDashboard(@NonNull UUID monsterId) {
|
||||||
|
Completable result = m_db.dashboardDAO().removeMonsterFromDashboard(monsterId.toString());
|
||||||
|
result.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Completable removeDashboardMonsterById(long id) {
|
||||||
|
Completable result = m_db.dashboardDAO().removeDashboardMonsterById(id);
|
||||||
|
result.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Completable clearDashboard() {
|
||||||
|
Completable result = m_db.dashboardDAO().clearDashboard();
|
||||||
|
result.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Completable updateDashboardMonsters(List<DashboardMonster> items) {
|
||||||
|
Completable result = m_db.dashboardDAO().updateDashboardMonsters(items);
|
||||||
|
result.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private static class Helpers {
|
private static class Helpers {
|
||||||
static boolean monsterMatchesSearch(Monster monster, String searchText) {
|
static boolean monsterMatchesSearch(Monster monster, String searchText) {
|
||||||
if (StringHelper.isNullOrEmpty(searchText)) {
|
if (StringHelper.isNullOrEmpty(searchText)) {
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package com.majinnaibu.monstercards.models;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.room.ColumnInfo;
|
||||||
|
import androidx.room.Entity;
|
||||||
|
import androidx.room.ForeignKey;
|
||||||
|
import androidx.room.Index;
|
||||||
|
import androidx.room.PrimaryKey;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Entity(
|
||||||
|
tableName = "dashboard_monsters",
|
||||||
|
foreignKeys = @ForeignKey(
|
||||||
|
entity = Monster.class,
|
||||||
|
parentColumns = "id",
|
||||||
|
childColumns = "monster_id",
|
||||||
|
onDelete = ForeignKey.CASCADE
|
||||||
|
),
|
||||||
|
indices = @Index(value = {"monster_id"})
|
||||||
|
)
|
||||||
|
public class DashboardMonster {
|
||||||
|
|
||||||
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
public long id;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@ColumnInfo(name = "monster_id")
|
||||||
|
public UUID monsterId;
|
||||||
|
|
||||||
|
@ColumnInfo(name = "ordinal", defaultValue = "0")
|
||||||
|
public int ordinal;
|
||||||
|
|
||||||
|
public DashboardMonster() {
|
||||||
|
this.monsterId = UUID.fromString("00000000-0000-0000-0000-000000000000");
|
||||||
|
this.ordinal = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DashboardMonster(@NonNull UUID monsterId, int ordinal) {
|
||||||
|
this.monsterId = monsterId;
|
||||||
|
this.ordinal = ordinal;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,9 @@ import android.content.Context;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
@@ -44,6 +47,7 @@ public class CollectionDetailFragment extends MCFragment {
|
|||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
|
setHasOptionsMenu(true);
|
||||||
View root = inflater.inflate(R.layout.fragment_collection_detail, container, false);
|
View root = inflater.inflate(R.layout.fragment_collection_detail, container, false);
|
||||||
|
|
||||||
Bundle arguments = getArguments();
|
Bundle arguments = getArguments();
|
||||||
@@ -172,6 +176,36 @@ public class CollectionDetailFragment extends MCFragment {
|
|||||||
Navigation.findNavController(requireView()).navigate(action);
|
Navigation.findNavController(requireView()).navigate(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
|
||||||
|
inflater.inflate(R.menu.collection_detail_menu, menu);
|
||||||
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||||
|
if (item.getItemId() == R.id.menu_action_add_collection_to_dashboard) {
|
||||||
|
addCollectionToDashboard();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addCollectionToDashboard() {
|
||||||
|
MonsterRepository repository = getMonsterRepository();
|
||||||
|
mDisposables.add(repository.addCollectionToDashboard(mCollectionId)
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(() -> {
|
||||||
|
View view = getView();
|
||||||
|
if (view != null) {
|
||||||
|
Collection collection = mViewModel.getCollection().getValue();
|
||||||
|
String collectionName = collection != null ? collection.name : "";
|
||||||
|
Snackbar.make(view, getString(R.string.snackbar_collection_added_to_dashboard, collectionName), Snackbar.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
}, Logger::logError));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroyView() {
|
public void onDestroyView() {
|
||||||
super.onDestroyView();
|
super.onDestroyView();
|
||||||
|
|||||||
@@ -3,52 +3,77 @@ package com.majinnaibu.monstercards.ui.dashboard;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.lifecycle.LiveData;
|
import androidx.lifecycle.LiveData;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
import androidx.navigation.NavDirections;
|
import androidx.navigation.NavDirections;
|
||||||
import androidx.navigation.Navigation;
|
import androidx.navigation.Navigation;
|
||||||
import androidx.recyclerview.widget.GridLayoutManager;
|
import androidx.recyclerview.widget.GridLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
import com.majinnaibu.monstercards.R;
|
import com.majinnaibu.monstercards.R;
|
||||||
|
import com.majinnaibu.monstercards.data.MonsterRepository;
|
||||||
|
import com.majinnaibu.monstercards.models.Collection;
|
||||||
import com.majinnaibu.monstercards.models.Monster;
|
import com.majinnaibu.monstercards.models.Monster;
|
||||||
import com.majinnaibu.monstercards.ui.shared.MCFragment;
|
import com.majinnaibu.monstercards.ui.shared.MCFragment;
|
||||||
import com.majinnaibu.monstercards.utils.Logger;
|
import com.majinnaibu.monstercards.utils.Logger;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||||
|
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||||
|
|
||||||
public class DashboardFragment extends MCFragment {
|
public class DashboardFragment extends MCFragment {
|
||||||
private DashboardViewModel mViewModel;
|
private DashboardViewModel mViewModel;
|
||||||
private ViewHolder mHolder;
|
private ViewHolder mHolder;
|
||||||
private DashboardRecyclerViewAdapter mAdapter;
|
private DashboardRecyclerViewAdapter mAdapter;
|
||||||
|
private final CompositeDisposable mDisposables = new CompositeDisposable();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||||
ViewGroup container, Bundle savedInstanceState) {
|
@Nullable ViewGroup container,
|
||||||
|
@Nullable Bundle savedInstanceState) {
|
||||||
|
setHasOptionsMenu(true);
|
||||||
mViewModel = new ViewModelProvider(this).get(DashboardViewModel.class);
|
mViewModel = new ViewModelProvider(this).get(DashboardViewModel.class);
|
||||||
View root = inflater.inflate(R.layout.fragment_dashboard, container, false);
|
View root = inflater.inflate(R.layout.fragment_dashboard, container, false);
|
||||||
mHolder = new ViewHolder(root);
|
mHolder = new ViewHolder(root);
|
||||||
|
|
||||||
setupRecyclerView(mHolder.list);
|
FloatingActionButton fab = root.findViewById(R.id.fab_add_to_dashboard);
|
||||||
|
if (fab != null) {
|
||||||
|
fab.setOnClickListener(v -> showAddToDashboardOptionsDialog());
|
||||||
|
}
|
||||||
|
|
||||||
getMonsterRepository()
|
setupRecyclerView(mHolder.list);
|
||||||
.getMonsters()
|
loadDashboardMonsters();
|
||||||
.subscribeOn(Schedulers.io())
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.subscribe(monsters -> mViewModel.setMonsters(monsters));
|
|
||||||
|
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void loadDashboardMonsters() {
|
||||||
|
MonsterRepository repository = getMonsterRepository();
|
||||||
|
mDisposables.add(repository.getDashboardMonsters()
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(monsters -> mViewModel.setMonsters(monsters), Logger::logError));
|
||||||
|
}
|
||||||
|
|
||||||
private void setupRecyclerView(@NonNull RecyclerView recyclerView) {
|
private void setupRecyclerView(@NonNull RecyclerView recyclerView) {
|
||||||
int columnCount = Math.max(1, getResources().getConfiguration().screenWidthDp / 396);
|
int columnCount = Math.max(1, getResources().getConfiguration().screenWidthDp / 360);
|
||||||
Logger.logWTF(String.format(Locale.US, "Setting column count to %d", columnCount));
|
Logger.logWTF(String.format(Locale.US, "Setting column count to %d", columnCount));
|
||||||
Context context = requireContext();
|
Context context = requireContext();
|
||||||
GridLayoutManager layoutManager = new GridLayoutManager(context, columnCount);
|
GridLayoutManager layoutManager = new GridLayoutManager(context, columnCount);
|
||||||
@@ -66,6 +91,165 @@ public class DashboardFragment extends MCFragment {
|
|||||||
monsterData.observe(getViewLifecycleOwner(), monsters -> mAdapter.submitList(monsters));
|
monsterData.observe(getViewLifecycleOwner(), monsters -> mAdapter.submitList(monsters));
|
||||||
}
|
}
|
||||||
recyclerView.setAdapter(mAdapter);
|
recyclerView.setAdapter(mAdapter);
|
||||||
|
|
||||||
|
ItemTouchHelper.SimpleCallback touchHelperCallback = new ItemTouchHelper.SimpleCallback(
|
||||||
|
ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT,
|
||||||
|
ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT
|
||||||
|
) {
|
||||||
|
@Override
|
||||||
|
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
|
||||||
|
int fromPos = viewHolder.getAdapterPosition();
|
||||||
|
int toPos = target.getAdapterPosition();
|
||||||
|
if (fromPos != RecyclerView.NO_POSITION && toPos != RecyclerView.NO_POSITION) {
|
||||||
|
List<Monster> currentList = new ArrayList<>(mAdapter.getCurrentList());
|
||||||
|
if (fromPos < currentList.size() && toPos < currentList.size()) {
|
||||||
|
Monster moved = currentList.remove(fromPos);
|
||||||
|
currentList.add(toPos, moved);
|
||||||
|
mAdapter.submitList(currentList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
|
||||||
|
int position = viewHolder.getAdapterPosition();
|
||||||
|
if (position != RecyclerView.NO_POSITION) {
|
||||||
|
List<Monster> currentList = mAdapter.getCurrentList();
|
||||||
|
if (position < currentList.size()) {
|
||||||
|
Monster monster = currentList.get(position);
|
||||||
|
removeMonsterFromDashboard(monster);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
new ItemTouchHelper(touchHelperCallback).attachToRecyclerView(recyclerView);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showAddToDashboardOptionsDialog() {
|
||||||
|
String[] options = new String[]{
|
||||||
|
getString(R.string.action_add_single_monster),
|
||||||
|
getString(R.string.action_add_collection_option),
|
||||||
|
getString(R.string.action_clear_dashboard)
|
||||||
|
};
|
||||||
|
new AlertDialog.Builder(requireContext())
|
||||||
|
.setTitle(R.string.dialog_add_to_dashboard)
|
||||||
|
.setItems(options, (dialog, which) -> {
|
||||||
|
if (which == 0) {
|
||||||
|
showAddMonsterPicker();
|
||||||
|
} else if (which == 1) {
|
||||||
|
showAddCollectionPicker();
|
||||||
|
} else if (which == 2) {
|
||||||
|
clearDashboard();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.setNegativeButton(R.string.dialog_cancel, null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showAddMonsterPicker() {
|
||||||
|
MonsterRepository repository = getMonsterRepository();
|
||||||
|
mDisposables.add(repository.getMonsters()
|
||||||
|
.firstOrError()
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(monsters -> {
|
||||||
|
if (monsters.isEmpty()) {
|
||||||
|
View view = getView();
|
||||||
|
if (view != null) {
|
||||||
|
Snackbar.make(view, getString(R.string.no_monsters_available), Snackbar.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String[] names = new String[monsters.size()];
|
||||||
|
for (int i = 0; i < monsters.size(); i++) {
|
||||||
|
names[i] = monsters.get(i).name;
|
||||||
|
}
|
||||||
|
new AlertDialog.Builder(requireContext())
|
||||||
|
.setTitle(R.string.action_add_single_monster)
|
||||||
|
.setItems(names, (dialog, which) -> {
|
||||||
|
Monster selected = monsters.get(which);
|
||||||
|
addMonsterToDashboard(selected);
|
||||||
|
})
|
||||||
|
.setNegativeButton(R.string.dialog_cancel, null)
|
||||||
|
.show();
|
||||||
|
}, Logger::logError));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showAddCollectionPicker() {
|
||||||
|
MonsterRepository repository = getMonsterRepository();
|
||||||
|
mDisposables.add(repository.getCollections()
|
||||||
|
.firstOrError()
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(collections -> {
|
||||||
|
if (collections.isEmpty()) {
|
||||||
|
View view = getView();
|
||||||
|
if (view != null) {
|
||||||
|
Snackbar.make(view, getString(R.string.no_collections_available), Snackbar.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String[] names = new String[collections.size()];
|
||||||
|
for (int i = 0; i < collections.size(); i++) {
|
||||||
|
names[i] = collections.get(i).name;
|
||||||
|
}
|
||||||
|
new AlertDialog.Builder(requireContext())
|
||||||
|
.setTitle(R.string.action_add_collection_option)
|
||||||
|
.setItems(names, (dialog, which) -> {
|
||||||
|
Collection selected = collections.get(which);
|
||||||
|
addCollectionToDashboard(selected);
|
||||||
|
})
|
||||||
|
.setNegativeButton(R.string.dialog_cancel, null)
|
||||||
|
.show();
|
||||||
|
}, Logger::logError));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addMonsterToDashboard(@NonNull Monster monster) {
|
||||||
|
MonsterRepository repository = getMonsterRepository();
|
||||||
|
mDisposables.add(repository.addMonsterToDashboard(monster.id)
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(() -> {
|
||||||
|
View view = getView();
|
||||||
|
if (view != null) {
|
||||||
|
Snackbar.make(view, getString(R.string.snackbar_added_to_dashboard, monster.name), Snackbar.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
}, Logger::logError));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addCollectionToDashboard(@NonNull Collection collection) {
|
||||||
|
MonsterRepository repository = getMonsterRepository();
|
||||||
|
mDisposables.add(repository.addCollectionToDashboard(collection.id)
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(() -> {
|
||||||
|
View view = getView();
|
||||||
|
if (view != null) {
|
||||||
|
Snackbar.make(view, getString(R.string.snackbar_collection_added_to_dashboard, collection.name), Snackbar.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
}, Logger::logError));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeMonsterFromDashboard(@NonNull Monster monster) {
|
||||||
|
MonsterRepository repository = getMonsterRepository();
|
||||||
|
mDisposables.add(repository.removeMonsterFromDashboard(monster.id)
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(() -> {}, Logger::logError));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearDashboard() {
|
||||||
|
MonsterRepository repository = getMonsterRepository();
|
||||||
|
mDisposables.add(repository.clearDashboard()
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(() -> {
|
||||||
|
View view = getView();
|
||||||
|
if (view != null) {
|
||||||
|
Snackbar.make(view, getString(R.string.snackbar_dashboard_cleared), Snackbar.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
}, Logger::logError));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void navigateToMonsterDetail(Monster monster) {
|
private void navigateToMonsterDetail(Monster monster) {
|
||||||
@@ -73,6 +257,33 @@ public class DashboardFragment extends MCFragment {
|
|||||||
Navigation.findNavController(requireView()).navigate(action);
|
Navigation.findNavController(requireView()).navigate(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
|
||||||
|
inflater.inflate(R.menu.dashboard_menu, menu);
|
||||||
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||||
|
if (item.getItemId() == R.id.menu_action_add_single_monster) {
|
||||||
|
showAddMonsterPicker();
|
||||||
|
return true;
|
||||||
|
} else if (item.getItemId() == R.id.menu_action_add_collection_option) {
|
||||||
|
showAddCollectionPicker();
|
||||||
|
return true;
|
||||||
|
} else if (item.getItemId() == R.id.menu_action_clear_dashboard) {
|
||||||
|
clearDashboard();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
mDisposables.clear();
|
||||||
|
}
|
||||||
|
|
||||||
private static class ViewHolder {
|
private static class ViewHolder {
|
||||||
final RecyclerView list;
|
final RecyclerView list;
|
||||||
|
|
||||||
@@ -80,5 +291,4 @@ public class DashboardFragment extends MCFragment {
|
|||||||
list = root.findViewById(R.id.list);
|
list = root.findViewById(R.id.list);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import android.view.View;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.navigation.NavController;
|
||||||
import androidx.navigation.NavDirections;
|
import androidx.navigation.NavDirections;
|
||||||
import androidx.navigation.Navigation;
|
import androidx.navigation.Navigation;
|
||||||
import androidx.recyclerview.widget.DividerItemDecoration;
|
import androidx.recyclerview.widget.DividerItemDecoration;
|
||||||
@@ -19,6 +20,7 @@ import com.google.android.material.snackbar.Snackbar;
|
|||||||
import com.majinnaibu.monstercards.R;
|
import com.majinnaibu.monstercards.R;
|
||||||
import com.majinnaibu.monstercards.data.MonsterRepository;
|
import com.majinnaibu.monstercards.data.MonsterRepository;
|
||||||
import com.majinnaibu.monstercards.models.Monster;
|
import com.majinnaibu.monstercards.models.Monster;
|
||||||
|
import com.majinnaibu.monstercards.ui.monster.MonsterDetailFragmentDirections;
|
||||||
import com.majinnaibu.monstercards.ui.shared.MCFragment;
|
import com.majinnaibu.monstercards.ui.shared.MCFragment;
|
||||||
import com.majinnaibu.monstercards.ui.shared.SwipeToDeleteCallback;
|
import com.majinnaibu.monstercards.ui.shared.SwipeToDeleteCallback;
|
||||||
import com.majinnaibu.monstercards.utils.Logger;
|
import com.majinnaibu.monstercards.utils.Logger;
|
||||||
@@ -92,23 +94,16 @@ public class LibraryFragment extends MCFragment {
|
|||||||
new DisposableCompletableObserver() {
|
new DisposableCompletableObserver() {
|
||||||
@Override
|
@Override
|
||||||
public void onComplete() {
|
public void onComplete() {
|
||||||
View view = getView();
|
navigateToEditMonster(monster.id);
|
||||||
assert view != null;
|
|
||||||
Snackbar.make(
|
|
||||||
view,
|
|
||||||
getString(R.string.snackbar_monster_created, monster.name),
|
|
||||||
Snackbar.LENGTH_LONG)
|
|
||||||
.setAction("Action", (_view) -> navigateToMonsterDetail(monster.id))
|
|
||||||
.show();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(@io.reactivex.rxjava3.annotations.NonNull Throwable e) {
|
public void onError(@io.reactivex.rxjava3.annotations.NonNull Throwable e) {
|
||||||
Logger.logError("Error creating monster", e);
|
Logger.logError("Error creating monster", e);
|
||||||
View view = getView();
|
View view = getView();
|
||||||
assert view != null;
|
if (view != null) {
|
||||||
Snackbar.make(view, getString(R.string.snackbar_failed_to_create_monster), Snackbar.LENGTH_LONG)
|
Snackbar.make(view, getString(R.string.snackbar_failed_to_create_monster), Snackbar.LENGTH_LONG).show();
|
||||||
.setAction("Action", null).show();
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -118,4 +113,12 @@ public class LibraryFragment extends MCFragment {
|
|||||||
NavDirections action = LibraryFragmentDirections.actionNavigationLibraryToNavigationMonster(monsterId.toString());
|
NavDirections action = LibraryFragmentDirections.actionNavigationLibraryToNavigationMonster(monsterId.toString());
|
||||||
Navigation.findNavController(requireView()).navigate(action);
|
Navigation.findNavController(requireView()).navigate(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void navigateToEditMonster(@NonNull UUID monsterId) {
|
||||||
|
NavController navController = Navigation.findNavController(requireView());
|
||||||
|
NavDirections action = LibraryFragmentDirections.actionNavigationLibraryToNavigationMonster(monsterId.toString());
|
||||||
|
navController.navigate(action);
|
||||||
|
action = MonsterDetailFragmentDirections.actionNavigationMonsterToEditMonsterFragment(monsterId.toString());
|
||||||
|
navController.navigate(action);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -181,10 +181,31 @@ public class MonsterDetailFragment extends MCFragment {
|
|||||||
} else if (item.getItemId() == R.id.menu_action_add_to_collection) {
|
} else if (item.getItemId() == R.id.menu_action_add_to_collection) {
|
||||||
showAddToCollectionDialog();
|
showAddToCollectionDialog();
|
||||||
return true;
|
return true;
|
||||||
|
} else if (item.getItemId() == R.id.menu_action_add_to_dashboard) {
|
||||||
|
addCurrentMonsterToDashboard();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addCurrentMonsterToDashboard() {
|
||||||
|
UUID monsterId = mViewModel.getId().getValue();
|
||||||
|
if (monsterId == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
MonsterRepository repository = getMonsterRepository();
|
||||||
|
repository.addMonsterToDashboard(monsterId)
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(() -> {
|
||||||
|
View view = getView();
|
||||||
|
if (view != null) {
|
||||||
|
String monsterName = mViewModel.getName().getValue();
|
||||||
|
Snackbar.make(view, getString(R.string.snackbar_added_to_dashboard, monsterName), Snackbar.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
}, Logger::logError);
|
||||||
|
}
|
||||||
|
|
||||||
private void showAddToCollectionDialog() {
|
private void showAddToCollectionDialog() {
|
||||||
UUID monsterId = mViewModel.getId().getValue();
|
UUID monsterId = mViewModel.getId().getValue();
|
||||||
if (monsterId == null) {
|
if (monsterId == null) {
|
||||||
|
|||||||
@@ -20,4 +20,16 @@
|
|||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:listitem="@layout/card_monster" />
|
tools:listitem="@layout/card_monster" />
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
android:id="@+id/fab_add_to_dashboard"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom|end"
|
||||||
|
android:layout_margin="@dimen/fab_margin"
|
||||||
|
android:contentDescription="@string/action_add_to_dashboard"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:srcCompat="@android:drawable/ic_input_add"
|
||||||
|
app:tint="@android:color/white" />
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|||||||
9
Android/app/src/main/res/menu/collection_detail_menu.xml
Normal file
9
Android/app/src/main/res/menu/collection_detail_menu.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/menu_action_add_collection_to_dashboard"
|
||||||
|
android:title="@string/action_add_collection_to_dashboard"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
</menu>
|
||||||
19
Android/app/src/main/res/menu/dashboard_menu.xml
Normal file
19
Android/app/src/main/res/menu/dashboard_menu.xml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/menu_action_add_single_monster"
|
||||||
|
android:title="@string/action_add_single_monster"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/menu_action_add_collection_option"
|
||||||
|
android:title="@string/action_add_collection_option"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/menu_action_clear_dashboard"
|
||||||
|
android:title="@string/action_clear_dashboard"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
</menu>
|
||||||
@@ -3,14 +3,17 @@
|
|||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_action_add_to_collection"
|
android:id="@+id/menu_action_edit_monster"
|
||||||
android:icon="@android:drawable/ic_input_add"
|
android:title="@string/action_edit"
|
||||||
android:title="@string/action_add_to_collection"
|
app:showAsAction="never" />
|
||||||
app:showAsAction="ifRoom" />
|
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_action_edit_monster"
|
android:id="@+id/menu_action_add_to_collection"
|
||||||
android:icon="@drawable/ic_edit_24"
|
android:title="@string/action_add_to_collection"
|
||||||
android:title="@string/action_edit"
|
app:showAsAction="never" />
|
||||||
app:showAsAction="always" />
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/menu_action_add_to_dashboard"
|
||||||
|
android:title="@string/action_add_to_dashboard"
|
||||||
|
app:showAsAction="never" />
|
||||||
</menu>
|
</menu>
|
||||||
|
|||||||
@@ -152,4 +152,14 @@
|
|||||||
<string name="no_collections_available">No collections available. Create one first!</string>
|
<string name="no_collections_available">No collections available. Create one first!</string>
|
||||||
<string name="format_monster_count_one">1 monster</string>
|
<string name="format_monster_count_one">1 monster</string>
|
||||||
<string name="format_monster_count_other">%d monsters</string>
|
<string name="format_monster_count_other">%d monsters</string>
|
||||||
|
<string name="action_add_to_dashboard">Add to Dashboard</string>
|
||||||
|
<string name="action_add_collection_to_dashboard">Add Collection to Dashboard</string>
|
||||||
|
<string name="action_add_single_monster">Add Monster</string>
|
||||||
|
<string name="action_add_collection_option">Add Collection</string>
|
||||||
|
<string name="action_clear_dashboard">Clear Dashboard</string>
|
||||||
|
<string name="dialog_add_to_dashboard">Add to Dashboard</string>
|
||||||
|
<string name="snackbar_added_to_dashboard">Added %1$s to Dashboard</string>
|
||||||
|
<string name="snackbar_collection_added_to_dashboard">Added collection %1$s to Dashboard</string>
|
||||||
|
<string name="snackbar_dashboard_cleared">Dashboard cleared</string>
|
||||||
|
<string name="no_monsters_available">No monsters available in library. Create one first!</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user