diff --git a/core/data/schemas/zed.rainxch.core.data.local.db.AppDatabase/4.json b/core/data/schemas/zed.rainxch.core.data.local.db.AppDatabase/4.json new file mode 100644 index 00000000..762d6b87 --- /dev/null +++ b/core/data/schemas/zed.rainxch.core.data.local.db.AppDatabase/4.json @@ -0,0 +1,499 @@ +{ + "formatVersion": 1, + "database": { + "version": 4, + "identityHash": "7d8cd14625a6c8b570092980957d57ce", + "entities": [ + { + "tableName": "installed_apps", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`packageName` TEXT NOT NULL, `repoId` INTEGER NOT NULL, `repoName` TEXT NOT NULL, `repoOwner` TEXT NOT NULL, `repoOwnerAvatarUrl` TEXT NOT NULL, `repoDescription` TEXT, `primaryLanguage` TEXT, `repoUrl` TEXT NOT NULL, `installedVersion` TEXT NOT NULL, `installedAssetName` TEXT, `installedAssetUrl` TEXT, `latestVersion` TEXT, `latestAssetName` TEXT, `latestAssetUrl` TEXT, `latestAssetSize` INTEGER, `appName` TEXT NOT NULL, `installSource` TEXT NOT NULL, `installedAt` INTEGER NOT NULL, `lastCheckedAt` INTEGER NOT NULL, `lastUpdatedAt` INTEGER NOT NULL, `isUpdateAvailable` INTEGER NOT NULL, `updateCheckEnabled` INTEGER NOT NULL, `releaseNotes` TEXT, `systemArchitecture` TEXT NOT NULL, `fileExtension` TEXT NOT NULL, `isPendingInstall` INTEGER NOT NULL, `installedVersionName` TEXT, `installedVersionCode` INTEGER NOT NULL, `latestVersionName` TEXT, `latestVersionCode` INTEGER, PRIMARY KEY(`packageName`))", + "fields": [ + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoId", + "columnName": "repoId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "repoName", + "columnName": "repoName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoOwner", + "columnName": "repoOwner", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoOwnerAvatarUrl", + "columnName": "repoOwnerAvatarUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoDescription", + "columnName": "repoDescription", + "affinity": "TEXT" + }, + { + "fieldPath": "primaryLanguage", + "columnName": "primaryLanguage", + "affinity": "TEXT" + }, + { + "fieldPath": "repoUrl", + "columnName": "repoUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "installedVersion", + "columnName": "installedVersion", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "installedAssetName", + "columnName": "installedAssetName", + "affinity": "TEXT" + }, + { + "fieldPath": "installedAssetUrl", + "columnName": "installedAssetUrl", + "affinity": "TEXT" + }, + { + "fieldPath": "latestVersion", + "columnName": "latestVersion", + "affinity": "TEXT" + }, + { + "fieldPath": "latestAssetName", + "columnName": "latestAssetName", + "affinity": "TEXT" + }, + { + "fieldPath": "latestAssetUrl", + "columnName": "latestAssetUrl", + "affinity": "TEXT" + }, + { + "fieldPath": "latestAssetSize", + "columnName": "latestAssetSize", + "affinity": "INTEGER" + }, + { + "fieldPath": "appName", + "columnName": "appName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "installSource", + "columnName": "installSource", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "installedAt", + "columnName": "installedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastCheckedAt", + "columnName": "lastCheckedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastUpdatedAt", + "columnName": "lastUpdatedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isUpdateAvailable", + "columnName": "isUpdateAvailable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "updateCheckEnabled", + "columnName": "updateCheckEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "releaseNotes", + "columnName": "releaseNotes", + "affinity": "TEXT" + }, + { + "fieldPath": "systemArchitecture", + "columnName": "systemArchitecture", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "fileExtension", + "columnName": "fileExtension", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isPendingInstall", + "columnName": "isPendingInstall", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "installedVersionName", + "columnName": "installedVersionName", + "affinity": "TEXT" + }, + { + "fieldPath": "installedVersionCode", + "columnName": "installedVersionCode", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latestVersionName", + "columnName": "latestVersionName", + "affinity": "TEXT" + }, + { + "fieldPath": "latestVersionCode", + "columnName": "latestVersionCode", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "packageName" + ] + } + }, + { + "tableName": "favorite_repos", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`repoId` INTEGER NOT NULL, `repoName` TEXT NOT NULL, `repoOwner` TEXT NOT NULL, `repoOwnerAvatarUrl` TEXT NOT NULL, `repoDescription` TEXT, `primaryLanguage` TEXT, `repoUrl` TEXT NOT NULL, `isInstalled` INTEGER NOT NULL, `installedPackageName` TEXT, `latestVersion` TEXT, `latestReleaseUrl` TEXT, `addedAt` INTEGER NOT NULL, `lastSyncedAt` INTEGER NOT NULL, PRIMARY KEY(`repoId`))", + "fields": [ + { + "fieldPath": "repoId", + "columnName": "repoId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "repoName", + "columnName": "repoName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoOwner", + "columnName": "repoOwner", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoOwnerAvatarUrl", + "columnName": "repoOwnerAvatarUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoDescription", + "columnName": "repoDescription", + "affinity": "TEXT" + }, + { + "fieldPath": "primaryLanguage", + "columnName": "primaryLanguage", + "affinity": "TEXT" + }, + { + "fieldPath": "repoUrl", + "columnName": "repoUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isInstalled", + "columnName": "isInstalled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "installedPackageName", + "columnName": "installedPackageName", + "affinity": "TEXT" + }, + { + "fieldPath": "latestVersion", + "columnName": "latestVersion", + "affinity": "TEXT" + }, + { + "fieldPath": "latestReleaseUrl", + "columnName": "latestReleaseUrl", + "affinity": "TEXT" + }, + { + "fieldPath": "addedAt", + "columnName": "addedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastSyncedAt", + "columnName": "lastSyncedAt", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "repoId" + ] + } + }, + { + "tableName": "update_history", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `packageName` TEXT NOT NULL, `appName` TEXT NOT NULL, `repoOwner` TEXT NOT NULL, `repoName` TEXT NOT NULL, `fromVersion` TEXT NOT NULL, `toVersion` TEXT NOT NULL, `updatedAt` INTEGER NOT NULL, `updateSource` TEXT NOT NULL, `success` INTEGER NOT NULL, `errorMessage` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "appName", + "columnName": "appName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoOwner", + "columnName": "repoOwner", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoName", + "columnName": "repoName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "fromVersion", + "columnName": "fromVersion", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "toVersion", + "columnName": "toVersion", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "updateSource", + "columnName": "updateSource", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "success", + "columnName": "success", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "errorMessage", + "columnName": "errorMessage", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + } + }, + { + "tableName": "starred_repos", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`repoId` INTEGER NOT NULL, `repoName` TEXT NOT NULL, `repoOwner` TEXT NOT NULL, `repoOwnerAvatarUrl` TEXT NOT NULL, `repoDescription` TEXT, `primaryLanguage` TEXT, `repoUrl` TEXT NOT NULL, `stargazersCount` INTEGER NOT NULL, `forksCount` INTEGER NOT NULL, `openIssuesCount` INTEGER NOT NULL, `isInstalled` INTEGER NOT NULL, `installedPackageName` TEXT, `latestVersion` TEXT, `latestReleaseUrl` TEXT, `starredAt` INTEGER, `addedAt` INTEGER NOT NULL, `lastSyncedAt` INTEGER NOT NULL, PRIMARY KEY(`repoId`))", + "fields": [ + { + "fieldPath": "repoId", + "columnName": "repoId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "repoName", + "columnName": "repoName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoOwner", + "columnName": "repoOwner", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoOwnerAvatarUrl", + "columnName": "repoOwnerAvatarUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoDescription", + "columnName": "repoDescription", + "affinity": "TEXT" + }, + { + "fieldPath": "primaryLanguage", + "columnName": "primaryLanguage", + "affinity": "TEXT" + }, + { + "fieldPath": "repoUrl", + "columnName": "repoUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "stargazersCount", + "columnName": "stargazersCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "forksCount", + "columnName": "forksCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "openIssuesCount", + "columnName": "openIssuesCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isInstalled", + "columnName": "isInstalled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "installedPackageName", + "columnName": "installedPackageName", + "affinity": "TEXT" + }, + { + "fieldPath": "latestVersion", + "columnName": "latestVersion", + "affinity": "TEXT" + }, + { + "fieldPath": "latestReleaseUrl", + "columnName": "latestReleaseUrl", + "affinity": "TEXT" + }, + { + "fieldPath": "starredAt", + "columnName": "starredAt", + "affinity": "INTEGER" + }, + { + "fieldPath": "addedAt", + "columnName": "addedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastSyncedAt", + "columnName": "lastSyncedAt", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "repoId" + ] + } + }, + { + "tableName": "cache_entries", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `jsonData` TEXT NOT NULL, `cachedAt` INTEGER NOT NULL, `expiresAt` INTEGER NOT NULL, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "jsonData", + "columnName": "jsonData", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "cachedAt", + "columnName": "cachedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "expiresAt", + "columnName": "expiresAt", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "key" + ] + } + } + ], + "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, '7d8cd14625a6c8b570092980957d57ce')" + ] + } +} \ No newline at end of file diff --git a/core/data/schemas/zed.rainxch.core.data.local.db.AppDatabase/5.json b/core/data/schemas/zed.rainxch.core.data.local.db.AppDatabase/5.json new file mode 100644 index 00000000..85c649dc --- /dev/null +++ b/core/data/schemas/zed.rainxch.core.data.local.db.AppDatabase/5.json @@ -0,0 +1,504 @@ +{ + "formatVersion": 1, + "database": { + "version": 5, + "identityHash": "9d5111af6d583511c868ab59557e4ea8", + "entities": [ + { + "tableName": "installed_apps", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`packageName` TEXT NOT NULL, `repoId` INTEGER NOT NULL, `repoName` TEXT NOT NULL, `repoOwner` TEXT NOT NULL, `repoOwnerAvatarUrl` TEXT NOT NULL, `repoDescription` TEXT, `primaryLanguage` TEXT, `repoUrl` TEXT NOT NULL, `installedVersion` TEXT NOT NULL, `installedAssetName` TEXT, `installedAssetUrl` TEXT, `latestVersion` TEXT, `latestAssetName` TEXT, `latestAssetUrl` TEXT, `latestAssetSize` INTEGER, `appName` TEXT NOT NULL, `installSource` TEXT NOT NULL, `signingFingerprint` TEXT, `installedAt` INTEGER NOT NULL, `lastCheckedAt` INTEGER NOT NULL, `lastUpdatedAt` INTEGER NOT NULL, `isUpdateAvailable` INTEGER NOT NULL, `updateCheckEnabled` INTEGER NOT NULL, `releaseNotes` TEXT, `systemArchitecture` TEXT NOT NULL, `fileExtension` TEXT NOT NULL, `isPendingInstall` INTEGER NOT NULL, `installedVersionName` TEXT, `installedVersionCode` INTEGER NOT NULL, `latestVersionName` TEXT, `latestVersionCode` INTEGER, PRIMARY KEY(`packageName`))", + "fields": [ + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoId", + "columnName": "repoId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "repoName", + "columnName": "repoName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoOwner", + "columnName": "repoOwner", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoOwnerAvatarUrl", + "columnName": "repoOwnerAvatarUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoDescription", + "columnName": "repoDescription", + "affinity": "TEXT" + }, + { + "fieldPath": "primaryLanguage", + "columnName": "primaryLanguage", + "affinity": "TEXT" + }, + { + "fieldPath": "repoUrl", + "columnName": "repoUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "installedVersion", + "columnName": "installedVersion", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "installedAssetName", + "columnName": "installedAssetName", + "affinity": "TEXT" + }, + { + "fieldPath": "installedAssetUrl", + "columnName": "installedAssetUrl", + "affinity": "TEXT" + }, + { + "fieldPath": "latestVersion", + "columnName": "latestVersion", + "affinity": "TEXT" + }, + { + "fieldPath": "latestAssetName", + "columnName": "latestAssetName", + "affinity": "TEXT" + }, + { + "fieldPath": "latestAssetUrl", + "columnName": "latestAssetUrl", + "affinity": "TEXT" + }, + { + "fieldPath": "latestAssetSize", + "columnName": "latestAssetSize", + "affinity": "INTEGER" + }, + { + "fieldPath": "appName", + "columnName": "appName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "installSource", + "columnName": "installSource", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "signingFingerprint", + "columnName": "signingFingerprint", + "affinity": "TEXT" + }, + { + "fieldPath": "installedAt", + "columnName": "installedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastCheckedAt", + "columnName": "lastCheckedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastUpdatedAt", + "columnName": "lastUpdatedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isUpdateAvailable", + "columnName": "isUpdateAvailable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "updateCheckEnabled", + "columnName": "updateCheckEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "releaseNotes", + "columnName": "releaseNotes", + "affinity": "TEXT" + }, + { + "fieldPath": "systemArchitecture", + "columnName": "systemArchitecture", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "fileExtension", + "columnName": "fileExtension", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isPendingInstall", + "columnName": "isPendingInstall", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "installedVersionName", + "columnName": "installedVersionName", + "affinity": "TEXT" + }, + { + "fieldPath": "installedVersionCode", + "columnName": "installedVersionCode", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latestVersionName", + "columnName": "latestVersionName", + "affinity": "TEXT" + }, + { + "fieldPath": "latestVersionCode", + "columnName": "latestVersionCode", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "packageName" + ] + } + }, + { + "tableName": "favorite_repos", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`repoId` INTEGER NOT NULL, `repoName` TEXT NOT NULL, `repoOwner` TEXT NOT NULL, `repoOwnerAvatarUrl` TEXT NOT NULL, `repoDescription` TEXT, `primaryLanguage` TEXT, `repoUrl` TEXT NOT NULL, `isInstalled` INTEGER NOT NULL, `installedPackageName` TEXT, `latestVersion` TEXT, `latestReleaseUrl` TEXT, `addedAt` INTEGER NOT NULL, `lastSyncedAt` INTEGER NOT NULL, PRIMARY KEY(`repoId`))", + "fields": [ + { + "fieldPath": "repoId", + "columnName": "repoId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "repoName", + "columnName": "repoName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoOwner", + "columnName": "repoOwner", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoOwnerAvatarUrl", + "columnName": "repoOwnerAvatarUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoDescription", + "columnName": "repoDescription", + "affinity": "TEXT" + }, + { + "fieldPath": "primaryLanguage", + "columnName": "primaryLanguage", + "affinity": "TEXT" + }, + { + "fieldPath": "repoUrl", + "columnName": "repoUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isInstalled", + "columnName": "isInstalled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "installedPackageName", + "columnName": "installedPackageName", + "affinity": "TEXT" + }, + { + "fieldPath": "latestVersion", + "columnName": "latestVersion", + "affinity": "TEXT" + }, + { + "fieldPath": "latestReleaseUrl", + "columnName": "latestReleaseUrl", + "affinity": "TEXT" + }, + { + "fieldPath": "addedAt", + "columnName": "addedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastSyncedAt", + "columnName": "lastSyncedAt", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "repoId" + ] + } + }, + { + "tableName": "update_history", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `packageName` TEXT NOT NULL, `appName` TEXT NOT NULL, `repoOwner` TEXT NOT NULL, `repoName` TEXT NOT NULL, `fromVersion` TEXT NOT NULL, `toVersion` TEXT NOT NULL, `updatedAt` INTEGER NOT NULL, `updateSource` TEXT NOT NULL, `success` INTEGER NOT NULL, `errorMessage` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "appName", + "columnName": "appName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoOwner", + "columnName": "repoOwner", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoName", + "columnName": "repoName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "fromVersion", + "columnName": "fromVersion", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "toVersion", + "columnName": "toVersion", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "updateSource", + "columnName": "updateSource", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "success", + "columnName": "success", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "errorMessage", + "columnName": "errorMessage", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + } + }, + { + "tableName": "starred_repos", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`repoId` INTEGER NOT NULL, `repoName` TEXT NOT NULL, `repoOwner` TEXT NOT NULL, `repoOwnerAvatarUrl` TEXT NOT NULL, `repoDescription` TEXT, `primaryLanguage` TEXT, `repoUrl` TEXT NOT NULL, `stargazersCount` INTEGER NOT NULL, `forksCount` INTEGER NOT NULL, `openIssuesCount` INTEGER NOT NULL, `isInstalled` INTEGER NOT NULL, `installedPackageName` TEXT, `latestVersion` TEXT, `latestReleaseUrl` TEXT, `starredAt` INTEGER, `addedAt` INTEGER NOT NULL, `lastSyncedAt` INTEGER NOT NULL, PRIMARY KEY(`repoId`))", + "fields": [ + { + "fieldPath": "repoId", + "columnName": "repoId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "repoName", + "columnName": "repoName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoOwner", + "columnName": "repoOwner", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoOwnerAvatarUrl", + "columnName": "repoOwnerAvatarUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoDescription", + "columnName": "repoDescription", + "affinity": "TEXT" + }, + { + "fieldPath": "primaryLanguage", + "columnName": "primaryLanguage", + "affinity": "TEXT" + }, + { + "fieldPath": "repoUrl", + "columnName": "repoUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "stargazersCount", + "columnName": "stargazersCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "forksCount", + "columnName": "forksCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "openIssuesCount", + "columnName": "openIssuesCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isInstalled", + "columnName": "isInstalled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "installedPackageName", + "columnName": "installedPackageName", + "affinity": "TEXT" + }, + { + "fieldPath": "latestVersion", + "columnName": "latestVersion", + "affinity": "TEXT" + }, + { + "fieldPath": "latestReleaseUrl", + "columnName": "latestReleaseUrl", + "affinity": "TEXT" + }, + { + "fieldPath": "starredAt", + "columnName": "starredAt", + "affinity": "INTEGER" + }, + { + "fieldPath": "addedAt", + "columnName": "addedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastSyncedAt", + "columnName": "lastSyncedAt", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "repoId" + ] + } + }, + { + "tableName": "cache_entries", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `jsonData` TEXT NOT NULL, `cachedAt` INTEGER NOT NULL, `expiresAt` INTEGER NOT NULL, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "jsonData", + "columnName": "jsonData", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "cachedAt", + "columnName": "cachedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "expiresAt", + "columnName": "expiresAt", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "key" + ] + } + } + ], + "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, '9d5111af6d583511c868ab59557e4ea8')" + ] + } +} \ No newline at end of file diff --git a/core/data/schemas/zed.rainxch.core.data.local.db.AppDatabase/6.json b/core/data/schemas/zed.rainxch.core.data.local.db.AppDatabase/6.json new file mode 100644 index 00000000..1636bd78 --- /dev/null +++ b/core/data/schemas/zed.rainxch.core.data.local.db.AppDatabase/6.json @@ -0,0 +1,528 @@ +{ + "formatVersion": 1, + "database": { + "version": 6, + "identityHash": "ce64b232de8d6ab9d95e3adabf7c7c43", + "entities": [ + { + "tableName": "installed_apps", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`packageName` TEXT NOT NULL, `repoId` INTEGER NOT NULL, `repoName` TEXT NOT NULL, `repoOwner` TEXT NOT NULL, `repoOwnerAvatarUrl` TEXT NOT NULL, `repoDescription` TEXT, `primaryLanguage` TEXT, `repoUrl` TEXT NOT NULL, `installedVersion` TEXT NOT NULL, `installedAssetName` TEXT, `installedAssetUrl` TEXT, `latestVersion` TEXT, `latestAssetName` TEXT, `latestAssetUrl` TEXT, `latestAssetSize` INTEGER, `appName` TEXT NOT NULL, `installSource` TEXT NOT NULL, `signingFingerprint` TEXT, `installedAt` INTEGER NOT NULL, `lastCheckedAt` INTEGER NOT NULL, `lastUpdatedAt` INTEGER NOT NULL, `isUpdateAvailable` INTEGER NOT NULL, `updateCheckEnabled` INTEGER NOT NULL, `releaseNotes` TEXT, `systemArchitecture` TEXT NOT NULL, `fileExtension` TEXT NOT NULL, `isPendingInstall` INTEGER NOT NULL, `installedVersionName` TEXT, `installedVersionCode` INTEGER NOT NULL, `latestVersionName` TEXT, `latestVersionCode` INTEGER, PRIMARY KEY(`packageName`))", + "fields": [ + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoId", + "columnName": "repoId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "repoName", + "columnName": "repoName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoOwner", + "columnName": "repoOwner", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoOwnerAvatarUrl", + "columnName": "repoOwnerAvatarUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoDescription", + "columnName": "repoDescription", + "affinity": "TEXT" + }, + { + "fieldPath": "primaryLanguage", + "columnName": "primaryLanguage", + "affinity": "TEXT" + }, + { + "fieldPath": "repoUrl", + "columnName": "repoUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "installedVersion", + "columnName": "installedVersion", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "installedAssetName", + "columnName": "installedAssetName", + "affinity": "TEXT" + }, + { + "fieldPath": "installedAssetUrl", + "columnName": "installedAssetUrl", + "affinity": "TEXT" + }, + { + "fieldPath": "latestVersion", + "columnName": "latestVersion", + "affinity": "TEXT" + }, + { + "fieldPath": "latestAssetName", + "columnName": "latestAssetName", + "affinity": "TEXT" + }, + { + "fieldPath": "latestAssetUrl", + "columnName": "latestAssetUrl", + "affinity": "TEXT" + }, + { + "fieldPath": "latestAssetSize", + "columnName": "latestAssetSize", + "affinity": "INTEGER" + }, + { + "fieldPath": "appName", + "columnName": "appName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "installSource", + "columnName": "installSource", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "signingFingerprint", + "columnName": "signingFingerprint", + "affinity": "TEXT" + }, + { + "fieldPath": "installedAt", + "columnName": "installedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastCheckedAt", + "columnName": "lastCheckedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastUpdatedAt", + "columnName": "lastUpdatedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isUpdateAvailable", + "columnName": "isUpdateAvailable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "updateCheckEnabled", + "columnName": "updateCheckEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "releaseNotes", + "columnName": "releaseNotes", + "affinity": "TEXT" + }, + { + "fieldPath": "systemArchitecture", + "columnName": "systemArchitecture", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "fileExtension", + "columnName": "fileExtension", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isPendingInstall", + "columnName": "isPendingInstall", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "installedVersionName", + "columnName": "installedVersionName", + "affinity": "TEXT" + }, + { + "fieldPath": "installedVersionCode", + "columnName": "installedVersionCode", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latestVersionName", + "columnName": "latestVersionName", + "affinity": "TEXT" + }, + { + "fieldPath": "latestVersionCode", + "columnName": "latestVersionCode", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "packageName" + ] + } + }, + { + "tableName": "favorite_repos", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`repoId` INTEGER NOT NULL, `repoName` TEXT NOT NULL, `repoOwner` TEXT NOT NULL, `repoOwnerAvatarUrl` TEXT NOT NULL, `repoDescription` TEXT, `primaryLanguage` TEXT, `repoUrl` TEXT NOT NULL, `isInstalled` INTEGER NOT NULL, `installedPackageName` TEXT, `latestVersion` TEXT, `latestReleaseUrl` TEXT, `addedAt` INTEGER NOT NULL, `lastSyncedAt` INTEGER NOT NULL, PRIMARY KEY(`repoId`))", + "fields": [ + { + "fieldPath": "repoId", + "columnName": "repoId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "repoName", + "columnName": "repoName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoOwner", + "columnName": "repoOwner", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoOwnerAvatarUrl", + "columnName": "repoOwnerAvatarUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoDescription", + "columnName": "repoDescription", + "affinity": "TEXT" + }, + { + "fieldPath": "primaryLanguage", + "columnName": "primaryLanguage", + "affinity": "TEXT" + }, + { + "fieldPath": "repoUrl", + "columnName": "repoUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isInstalled", + "columnName": "isInstalled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "installedPackageName", + "columnName": "installedPackageName", + "affinity": "TEXT" + }, + { + "fieldPath": "latestVersion", + "columnName": "latestVersion", + "affinity": "TEXT" + }, + { + "fieldPath": "latestReleaseUrl", + "columnName": "latestReleaseUrl", + "affinity": "TEXT" + }, + { + "fieldPath": "addedAt", + "columnName": "addedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastSyncedAt", + "columnName": "lastSyncedAt", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "repoId" + ] + } + }, + { + "tableName": "update_history", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `packageName` TEXT NOT NULL, `appName` TEXT NOT NULL, `repoOwner` TEXT NOT NULL, `repoName` TEXT NOT NULL, `fromVersion` TEXT NOT NULL, `toVersion` TEXT NOT NULL, `updatedAt` INTEGER NOT NULL, `updateSource` TEXT NOT NULL, `success` INTEGER NOT NULL, `errorMessage` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "appName", + "columnName": "appName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoOwner", + "columnName": "repoOwner", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoName", + "columnName": "repoName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "fromVersion", + "columnName": "fromVersion", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "toVersion", + "columnName": "toVersion", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "updateSource", + "columnName": "updateSource", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "success", + "columnName": "success", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "errorMessage", + "columnName": "errorMessage", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + } + }, + { + "tableName": "starred_repos", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`repoId` INTEGER NOT NULL, `repoName` TEXT NOT NULL, `repoOwner` TEXT NOT NULL, `repoOwnerAvatarUrl` TEXT NOT NULL, `repoDescription` TEXT, `primaryLanguage` TEXT, `repoUrl` TEXT NOT NULL, `stargazersCount` INTEGER NOT NULL, `forksCount` INTEGER NOT NULL, `openIssuesCount` INTEGER NOT NULL, `isInstalled` INTEGER NOT NULL, `installedPackageName` TEXT, `latestVersion` TEXT, `latestReleaseUrl` TEXT, `starredAt` INTEGER, `addedAt` INTEGER NOT NULL, `lastSyncedAt` INTEGER NOT NULL, PRIMARY KEY(`repoId`))", + "fields": [ + { + "fieldPath": "repoId", + "columnName": "repoId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "repoName", + "columnName": "repoName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoOwner", + "columnName": "repoOwner", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoOwnerAvatarUrl", + "columnName": "repoOwnerAvatarUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "repoDescription", + "columnName": "repoDescription", + "affinity": "TEXT" + }, + { + "fieldPath": "primaryLanguage", + "columnName": "primaryLanguage", + "affinity": "TEXT" + }, + { + "fieldPath": "repoUrl", + "columnName": "repoUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "stargazersCount", + "columnName": "stargazersCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "forksCount", + "columnName": "forksCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "openIssuesCount", + "columnName": "openIssuesCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isInstalled", + "columnName": "isInstalled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "installedPackageName", + "columnName": "installedPackageName", + "affinity": "TEXT" + }, + { + "fieldPath": "latestVersion", + "columnName": "latestVersion", + "affinity": "TEXT" + }, + { + "fieldPath": "latestReleaseUrl", + "columnName": "latestReleaseUrl", + "affinity": "TEXT" + }, + { + "fieldPath": "starredAt", + "columnName": "starredAt", + "affinity": "INTEGER" + }, + { + "fieldPath": "addedAt", + "columnName": "addedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastSyncedAt", + "columnName": "lastSyncedAt", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "repoId" + ] + } + }, + { + "tableName": "cache_entries", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `jsonData` TEXT NOT NULL, `cachedAt` INTEGER NOT NULL, `expiresAt` INTEGER NOT NULL, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "jsonData", + "columnName": "jsonData", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "cachedAt", + "columnName": "cachedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "expiresAt", + "columnName": "expiresAt", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "key" + ] + } + }, + { + "tableName": "seen_repos", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`repoId` INTEGER NOT NULL, `seenAt` INTEGER NOT NULL, PRIMARY KEY(`repoId`))", + "fields": [ + { + "fieldPath": "repoId", + "columnName": "repoId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "seenAt", + "columnName": "seenAt", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "repoId" + ] + } + } + ], + "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, 'ce64b232de8d6ab9d95e3adabf7c7c43')" + ] + } +} \ No newline at end of file diff --git a/core/data/src/androidMain/kotlin/zed/rainxch/core/data/local/db/initDatabase.kt b/core/data/src/androidMain/kotlin/zed/rainxch/core/data/local/db/initDatabase.kt index 8396d543..d21090d9 100644 --- a/core/data/src/androidMain/kotlin/zed/rainxch/core/data/local/db/initDatabase.kt +++ b/core/data/src/androidMain/kotlin/zed/rainxch/core/data/local/db/initDatabase.kt @@ -7,6 +7,7 @@ import zed.rainxch.core.data.local.db.migrations.MIGRATION_1_2 import zed.rainxch.core.data.local.db.migrations.MIGRATION_2_3 import zed.rainxch.core.data.local.db.migrations.MIGRATION_3_4 import zed.rainxch.core.data.local.db.migrations.MIGRATION_4_5 +import zed.rainxch.core.data.local.db.migrations.MIGRATION_5_6 fun initDatabase(context: Context): AppDatabase { val appContext = context.applicationContext @@ -21,5 +22,6 @@ fun initDatabase(context: Context): AppDatabase { MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5, + MIGRATION_5_6, ).build() } diff --git a/core/data/src/androidMain/kotlin/zed/rainxch/core/data/local/db/migrations/MIGRATION_5_6.kt b/core/data/src/androidMain/kotlin/zed/rainxch/core/data/local/db/migrations/MIGRATION_5_6.kt new file mode 100644 index 00000000..8c7c6b56 --- /dev/null +++ b/core/data/src/androidMain/kotlin/zed/rainxch/core/data/local/db/migrations/MIGRATION_5_6.kt @@ -0,0 +1,18 @@ +package zed.rainxch.core.data.local.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +val MIGRATION_5_6 = + object : Migration(5, 6) { + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL( + """ + CREATE TABLE IF NOT EXISTS seen_repos ( + repoId INTEGER NOT NULL PRIMARY KEY, + seenAt INTEGER NOT NULL + ) + """.trimIndent(), + ) + } + } diff --git a/core/data/src/commonMain/kotlin/zed/rainxch/core/data/di/SharedModule.kt b/core/data/src/commonMain/kotlin/zed/rainxch/core/data/di/SharedModule.kt index c008ab88..e81577f8 100644 --- a/core/data/src/commonMain/kotlin/zed/rainxch/core/data/di/SharedModule.kt +++ b/core/data/src/commonMain/kotlin/zed/rainxch/core/data/di/SharedModule.kt @@ -15,6 +15,7 @@ import zed.rainxch.core.data.local.db.AppDatabase import zed.rainxch.core.data.local.db.dao.CacheDao import zed.rainxch.core.data.local.db.dao.FavoriteRepoDao import zed.rainxch.core.data.local.db.dao.InstalledAppDao +import zed.rainxch.core.data.local.db.dao.SeenRepoDao import zed.rainxch.core.data.local.db.dao.StarredRepoDao import zed.rainxch.core.data.local.db.dao.UpdateHistoryDao import zed.rainxch.core.data.logging.KermitLogger @@ -26,6 +27,7 @@ import zed.rainxch.core.data.repository.FavouritesRepositoryImpl import zed.rainxch.core.data.repository.InstalledAppsRepositoryImpl import zed.rainxch.core.data.repository.ProxyRepositoryImpl import zed.rainxch.core.data.repository.RateLimitRepositoryImpl +import zed.rainxch.core.data.repository.SeenReposRepositoryImpl import zed.rainxch.core.data.repository.StarredRepositoryImpl import zed.rainxch.core.data.repository.TweaksRepositoryImpl import zed.rainxch.core.domain.getPlatform @@ -37,6 +39,7 @@ import zed.rainxch.core.domain.repository.FavouritesRepository import zed.rainxch.core.domain.repository.InstalledAppsRepository import zed.rainxch.core.domain.repository.ProxyRepository import zed.rainxch.core.domain.repository.RateLimitRepository +import zed.rainxch.core.domain.repository.SeenReposRepository import zed.rainxch.core.domain.repository.StarredRepository import zed.rainxch.core.domain.repository.TweaksRepository import zed.rainxch.core.domain.use_cases.SyncInstalledAppsUseCase @@ -94,6 +97,12 @@ val coreModule = ) } + single { + SeenReposRepositoryImpl( + seenRepoDao = get(), + ) + } + single { ProxyRepositoryImpl( preferences = get(), @@ -203,4 +212,8 @@ val databaseModule = single { get().cacheDao } + + single { + get().seenRepoDao + } } diff --git a/core/data/src/commonMain/kotlin/zed/rainxch/core/data/local/db/AppDatabase.kt b/core/data/src/commonMain/kotlin/zed/rainxch/core/data/local/db/AppDatabase.kt index 5eb9ddf3..a79c3ceb 100644 --- a/core/data/src/commonMain/kotlin/zed/rainxch/core/data/local/db/AppDatabase.kt +++ b/core/data/src/commonMain/kotlin/zed/rainxch/core/data/local/db/AppDatabase.kt @@ -5,11 +5,13 @@ import androidx.room.RoomDatabase import zed.rainxch.core.data.local.db.dao.CacheDao import zed.rainxch.core.data.local.db.dao.FavoriteRepoDao import zed.rainxch.core.data.local.db.dao.InstalledAppDao +import zed.rainxch.core.data.local.db.dao.SeenRepoDao import zed.rainxch.core.data.local.db.dao.StarredRepoDao import zed.rainxch.core.data.local.db.dao.UpdateHistoryDao import zed.rainxch.core.data.local.db.entities.CacheEntryEntity import zed.rainxch.core.data.local.db.entities.FavoriteRepoEntity import zed.rainxch.core.data.local.db.entities.InstalledAppEntity +import zed.rainxch.core.data.local.db.entities.SeenRepoEntity import zed.rainxch.core.data.local.db.entities.StarredRepositoryEntity import zed.rainxch.core.data.local.db.entities.UpdateHistoryEntity @@ -20,8 +22,9 @@ import zed.rainxch.core.data.local.db.entities.UpdateHistoryEntity UpdateHistoryEntity::class, StarredRepositoryEntity::class, CacheEntryEntity::class, + SeenRepoEntity::class, ], - version = 5, + version = 6, exportSchema = true, ) abstract class AppDatabase : RoomDatabase() { @@ -30,4 +33,5 @@ abstract class AppDatabase : RoomDatabase() { abstract val updateHistoryDao: UpdateHistoryDao abstract val starredReposDao: StarredRepoDao abstract val cacheDao: CacheDao + abstract val seenRepoDao: SeenRepoDao } diff --git a/core/data/src/commonMain/kotlin/zed/rainxch/core/data/local/db/dao/SeenRepoDao.kt b/core/data/src/commonMain/kotlin/zed/rainxch/core/data/local/db/dao/SeenRepoDao.kt new file mode 100644 index 00000000..6d3e557d --- /dev/null +++ b/core/data/src/commonMain/kotlin/zed/rainxch/core/data/local/db/dao/SeenRepoDao.kt @@ -0,0 +1,20 @@ +package zed.rainxch.core.data.local.db.dao + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import kotlinx.coroutines.flow.Flow +import zed.rainxch.core.data.local.db.entities.SeenRepoEntity + +@Dao +interface SeenRepoDao { + @Query("SELECT repoId FROM seen_repos") + fun getAllSeenRepoIds(): Flow> + + @Insert(onConflict = OnConflictStrategy.IGNORE) + suspend fun insert(entity: SeenRepoEntity) + + @Query("DELETE FROM seen_repos") + suspend fun clearAll() +} diff --git a/core/data/src/commonMain/kotlin/zed/rainxch/core/data/local/db/entities/SeenRepoEntity.kt b/core/data/src/commonMain/kotlin/zed/rainxch/core/data/local/db/entities/SeenRepoEntity.kt new file mode 100644 index 00000000..095720b8 --- /dev/null +++ b/core/data/src/commonMain/kotlin/zed/rainxch/core/data/local/db/entities/SeenRepoEntity.kt @@ -0,0 +1,11 @@ +package zed.rainxch.core.data.local.db.entities + +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "seen_repos") +data class SeenRepoEntity( + @PrimaryKey + val repoId: Long, + val seenAt: Long, +) diff --git a/core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/SeenReposRepositoryImpl.kt b/core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/SeenReposRepositoryImpl.kt new file mode 100644 index 00000000..91cd9ce9 --- /dev/null +++ b/core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/SeenReposRepositoryImpl.kt @@ -0,0 +1,27 @@ +package zed.rainxch.core.data.repository + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import zed.rainxch.core.data.local.db.dao.SeenRepoDao +import zed.rainxch.core.data.local.db.entities.SeenRepoEntity +import zed.rainxch.core.domain.repository.SeenReposRepository + +class SeenReposRepositoryImpl( + private val seenRepoDao: SeenRepoDao, +) : SeenReposRepository { + override fun getAllSeenRepoIds(): Flow> = + seenRepoDao.getAllSeenRepoIds().map { it.toSet() } + + override suspend fun markAsSeen(repoId: Long) { + seenRepoDao.insert( + SeenRepoEntity( + repoId = repoId, + seenAt = System.currentTimeMillis(), + ), + ) + } + + override suspend fun clearAll() { + seenRepoDao.clearAll() + } +} diff --git a/core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/TweaksRepositoryImpl.kt b/core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/TweaksRepositoryImpl.kt index 0cfc3fa4..73898856 100644 --- a/core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/TweaksRepositoryImpl.kt +++ b/core/data/src/commonMain/kotlin/zed/rainxch/core/data/repository/TweaksRepositoryImpl.kt @@ -26,6 +26,7 @@ class TweaksRepositoryImpl( private val UPDATE_CHECK_INTERVAL_KEY = longPreferencesKey("update_check_interval_hours") private val INCLUDE_PRE_RELEASES_KEY = booleanPreferencesKey("include_pre_releases") private val LIQUID_GLASS_ENABLED_KEY = booleanPreferencesKey("liquid_glass_enabled") + private val HIDE_SEEN_ENABLED_KEY = booleanPreferencesKey("hide_seen_enabled") override fun getThemeColor(): Flow = preferences.data.map { prefs -> @@ -144,6 +145,17 @@ class TweaksRepositoryImpl( } } + override fun getHideSeenEnabled(): Flow = + preferences.data.map { prefs -> + prefs[HIDE_SEEN_ENABLED_KEY] ?: false + } + + override suspend fun setHideSeenEnabled(enabled: Boolean) { + preferences.edit { prefs -> + prefs[HIDE_SEEN_ENABLED_KEY] = enabled + } + } + companion object { const val DEFAULT_UPDATE_CHECK_INTERVAL_HOURS = 6L } diff --git a/core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/repository/SeenReposRepository.kt b/core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/repository/SeenReposRepository.kt new file mode 100644 index 00000000..e506db75 --- /dev/null +++ b/core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/repository/SeenReposRepository.kt @@ -0,0 +1,11 @@ +package zed.rainxch.core.domain.repository + +import kotlinx.coroutines.flow.Flow + +interface SeenReposRepository { + fun getAllSeenRepoIds(): Flow> + + suspend fun markAsSeen(repoId: Long) + + suspend fun clearAll() +} diff --git a/core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/repository/TweaksRepository.kt b/core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/repository/TweaksRepository.kt index d7e5f5ce..9bd102a7 100644 --- a/core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/repository/TweaksRepository.kt +++ b/core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/repository/TweaksRepository.kt @@ -45,4 +45,8 @@ interface TweaksRepository { fun getLiquidGlassEnabled(): Flow suspend fun setLiquidGlassEnabled(enabled: Boolean) + + fun getHideSeenEnabled(): Flow + + suspend fun setHideSeenEnabled(enabled: Boolean) } diff --git a/core/presentation/src/commonMain/composeResources/values-ar/strings-ar.xml b/core/presentation/src/commonMain/composeResources/values-ar/strings-ar.xml index 4a561172..8158b3d0 100644 --- a/core/presentation/src/commonMain/composeResources/values-ar/strings-ar.xml +++ b/core/presentation/src/commonMain/composeResources/values-ar/strings-ar.xml @@ -585,4 +585,11 @@ عدم تطابق مفتاح التوقيع: تم توقيع التحديث بواسطة مطور مختلف. تم حظر التحديث. تأثير الزجاج السائل تحسين الواجهة بمظهر زجاجي ناعم + + إخفاء المستودعات المشاهَدة + إخفاء المستودعات التي شاهدتها بالفعل من خلاصات الاكتشاف + مسح سجل المشاهدة + إعادة تعيين جميع المستودعات المشاهَدة لتظهر مجدداً في الخلاصات + تم مسح سجل المشاهدة + تمت المشاهدة diff --git a/core/presentation/src/commonMain/composeResources/values-bn/strings-bn.xml b/core/presentation/src/commonMain/composeResources/values-bn/strings-bn.xml index aa745340..ca0c6847 100644 --- a/core/presentation/src/commonMain/composeResources/values-bn/strings-bn.xml +++ b/core/presentation/src/commonMain/composeResources/values-bn/strings-bn.xml @@ -584,4 +584,11 @@ সাইনিং কী অমিল: আপডেটটি একজন ভিন্ন ডেভেলপার দ্বারা সাইন করা হয়েছে। আপডেট ব্লক করা হয়েছে। লিকুইড গ্লাস ইফেক্ট একটি মসৃণ কাচের মতো চেহারা দিয়ে ইন্টারফেস উন্নত করুন + + দেখা রিপোজিটরি লুকান + আপনি ইতিমধ্যে দেখেছেন এমন রিপোজিটরি আবিষ্কার ফিড থেকে লুকান + দেখার ইতিহাস মুছুন + সমস্ত দেখা রিপোজিটরি রিসেট করুন যাতে সেগুলো ফিডে আবার দেখা যায় + দেখার ইতিহাস মুছে ফেলা হয়েছে + দেখা হয়েছে diff --git a/core/presentation/src/commonMain/composeResources/values-es/strings-es.xml b/core/presentation/src/commonMain/composeResources/values-es/strings-es.xml index 44d95c36..87a0ad82 100644 --- a/core/presentation/src/commonMain/composeResources/values-es/strings-es.xml +++ b/core/presentation/src/commonMain/composeResources/values-es/strings-es.xml @@ -545,4 +545,11 @@ Clave de firma no coincide: la actualización fue firmada por un desarrollador diferente. Actualización bloqueada. Efecto de cristal líquido Mejora la interfaz con una apariencia suave tipo cristal + + Ocultar repositorios vistos + Ocultar repositorios que ya has visto de las fuentes de descubrimiento + Borrar historial de vistos + Restablecer todos los repositorios vistos para que aparezcan de nuevo en las fuentes + Historial de vistos borrado + Visto \ No newline at end of file diff --git a/core/presentation/src/commonMain/composeResources/values-fr/strings-fr.xml b/core/presentation/src/commonMain/composeResources/values-fr/strings-fr.xml index 2fb12319..8f01c8b8 100644 --- a/core/presentation/src/commonMain/composeResources/values-fr/strings-fr.xml +++ b/core/presentation/src/commonMain/composeResources/values-fr/strings-fr.xml @@ -546,4 +546,11 @@ Incompatibilité de clé de signature : la mise à jour a été signée par un développeur différent. Mise à jour bloquée. Effet verre liquide Améliorez l\'interface avec une apparence vitreuse et fluide + + Masquer les dépôts consultés + Masquer les dépôts que vous avez déjà consultés des flux de découverte + Effacer l\'historique des consultations + Réinitialiser tous les dépôts consultés pour qu\'ils réapparaissent dans les flux + Historique des consultations effacé + Consulté \ No newline at end of file diff --git a/core/presentation/src/commonMain/composeResources/values-hi/strings-hi.xml b/core/presentation/src/commonMain/composeResources/values-hi/strings-hi.xml index d18c2342..ff3b2b75 100644 --- a/core/presentation/src/commonMain/composeResources/values-hi/strings-hi.xml +++ b/core/presentation/src/commonMain/composeResources/values-hi/strings-hi.xml @@ -584,4 +584,11 @@ साइनिंग कुंजी मेल नहीं खाती: अपडेट किसी अन्य डेवलपर द्वारा साइन किया गया था। अपडेट ब्लॉक किया गया। लिक्विड ग्लास इफ़ेक्ट एक चिकने काँच जैसे रूप से इंटरफ़ेस को बेहतर बनाएं + + देखे गए रिपॉजिटरी छुपाएँ + पहले से देखे गए रिपॉजिटरी को डिस्कवरी फ़ीड से छुपाएँ + देखने का इतिहास साफ़ करें + सभी देखे गए रिपॉजिटरी रीसेट करें ताकि वे फ़ीड में फिर से दिखें + देखने का इतिहास साफ़ किया गया + देखा गया diff --git a/core/presentation/src/commonMain/composeResources/values-it/strings-it.xml b/core/presentation/src/commonMain/composeResources/values-it/strings-it.xml index 3f5a02c4..498f721e 100644 --- a/core/presentation/src/commonMain/composeResources/values-it/strings-it.xml +++ b/core/presentation/src/commonMain/composeResources/values-it/strings-it.xml @@ -584,4 +584,11 @@ Chiave di firma non corrispondente: l\'aggiornamento è stato firmato da uno sviluppatore diverso. Aggiornamento bloccato. Effetto vetro liquido Migliora l\'interfaccia con un aspetto liscio simile al vetro + + Nascondi repository visualizzati + Nascondi i repository già visualizzati dai feed di scoperta + Cancella cronologia visualizzazioni + Reimposta tutti i repository visualizzati in modo che ricompaiano nei feed + Cronologia visualizzazioni cancellata + Visualizzato \ No newline at end of file diff --git a/core/presentation/src/commonMain/composeResources/values-ja/strings-ja.xml b/core/presentation/src/commonMain/composeResources/values-ja/strings-ja.xml index 5772689d..65d3a228 100644 --- a/core/presentation/src/commonMain/composeResources/values-ja/strings-ja.xml +++ b/core/presentation/src/commonMain/composeResources/values-ja/strings-ja.xml @@ -546,4 +546,11 @@ 署名キーの不一致: 更新は別の開発者によって署名されています。更新がブロックされました。 リキッドグラスエフェクト 滑らかなガラスのような外観でインターフェースを向上させます + + 閲覧済みリポジトリを非表示 + すでに閲覧したリポジトリをディスカバリーフィードから非表示にします + 閲覧履歴をクリア + すべての閲覧済みリポジトリをリセットしてフィードに再表示します + 閲覧履歴をクリアしました + 閲覧済み \ No newline at end of file diff --git a/core/presentation/src/commonMain/composeResources/values-ko/strings-ko.xml b/core/presentation/src/commonMain/composeResources/values-ko/strings-ko.xml index 704cf4d5..d1f949f8 100644 --- a/core/presentation/src/commonMain/composeResources/values-ko/strings-ko.xml +++ b/core/presentation/src/commonMain/composeResources/values-ko/strings-ko.xml @@ -581,4 +581,11 @@ 서명 키 불일치: 업데이트가 다른 개발자에 의해 서명되었습니다. 업데이트가 차단되었습니다. 리퀴드 글라스 효과 매끄러운 유리 같은 외관으로 인터페이스를 향상시킵니다 + + 본 저장소 숨기기 + 이미 본 저장소를 디스커버리 피드에서 숨깁니다 + 조회 기록 삭제 + 모든 조회 기록을 초기화하여 피드에 다시 표시합니다 + 조회 기록이 삭제되었습니다 + 확인함 \ No newline at end of file diff --git a/core/presentation/src/commonMain/composeResources/values-pl/strings-pl.xml b/core/presentation/src/commonMain/composeResources/values-pl/strings-pl.xml index db869a7d..971e8658 100644 --- a/core/presentation/src/commonMain/composeResources/values-pl/strings-pl.xml +++ b/core/presentation/src/commonMain/composeResources/values-pl/strings-pl.xml @@ -548,4 +548,11 @@ Niezgodność klucza podpisu: aktualizacja została podpisana przez innego programistę. Aktualizacja zablokowana. Efekt płynnego szkła Ulepsz interfejs o gładki, szklany wygląd + + Ukryj przeglądane repozytoria + Ukryj repozytoria, które już przeglądałeś, z kanałów odkrywania + Wyczyść historię przeglądania + Zresetuj wszystkie przeglądane repozytoria, aby ponownie pojawiły się w kanałach + Historia przeglądania wyczyszczona + Przeglądane \ No newline at end of file diff --git a/core/presentation/src/commonMain/composeResources/values-ru/strings-ru.xml b/core/presentation/src/commonMain/composeResources/values-ru/strings-ru.xml index e658a621..249956a8 100644 --- a/core/presentation/src/commonMain/composeResources/values-ru/strings-ru.xml +++ b/core/presentation/src/commonMain/composeResources/values-ru/strings-ru.xml @@ -548,4 +548,11 @@ Несоответствие ключа подписи: обновление подписано другим разработчиком. Обновление заблокировано. Эффект жидкого стекла Улучшите интерфейс с помощью гладкого стеклянного оформления + + Скрыть просмотренные репозитории + Скрыть уже просмотренные репозитории из лент обнаружения + Очистить историю просмотров + Сбросить все просмотренные репозитории, чтобы они снова появились в лентах + История просмотров очищена + Просмотрено \ No newline at end of file diff --git a/core/presentation/src/commonMain/composeResources/values-tr/strings-tr.xml b/core/presentation/src/commonMain/composeResources/values-tr/strings-tr.xml index 68d7fccf..6ec426a8 100644 --- a/core/presentation/src/commonMain/composeResources/values-tr/strings-tr.xml +++ b/core/presentation/src/commonMain/composeResources/values-tr/strings-tr.xml @@ -582,4 +582,11 @@ İmza anahtarı uyumsuzluğu: güncelleme farklı bir geliştirici tarafından imzalanmış. Güncelleme engellendi. Sıvı Cam Efekti Arayüzü pürüzsüz cam benzeri bir görünümle geliştirin + + Görülen depoları gizle + Zaten görüntülediğiniz depoları keşif akışlarından gizleyin + Görüntüleme geçmişini temizle + Tüm görüntülenen depoları sıfırlayarak akışlarda tekrar görünmelerini sağlayın + Görüntüleme geçmişi temizlendi + Görüntülendi diff --git a/core/presentation/src/commonMain/composeResources/values-zh-rCN/strings-zh-rCN.xml b/core/presentation/src/commonMain/composeResources/values-zh-rCN/strings-zh-rCN.xml index 91f789a8..7836bf1f 100644 --- a/core/presentation/src/commonMain/composeResources/values-zh-rCN/strings-zh-rCN.xml +++ b/core/presentation/src/commonMain/composeResources/values-zh-rCN/strings-zh-rCN.xml @@ -547,4 +547,11 @@ 签名密钥不匹配:更新由不同的开发者签名。更新已阻止。 液态玻璃效果 以流畅的玻璃质感提升界面外观 + + 隐藏已浏览的仓库 + 在发现信息流中隐藏你已经浏览过的仓库 + 清除浏览记录 + 重置所有已浏览的仓库,使其重新出现在信息流中 + 浏览记录已清除 + 已浏览 \ No newline at end of file diff --git a/core/presentation/src/commonMain/composeResources/values/strings.xml b/core/presentation/src/commonMain/composeResources/values/strings.xml index 9c3b2964..1017ec08 100644 --- a/core/presentation/src/commonMain/composeResources/values/strings.xml +++ b/core/presentation/src/commonMain/composeResources/values/strings.xml @@ -586,4 +586,11 @@ Liquid Glass Effect Enhance the interface with a smooth glass-like appearance + + Hide Seen Repositories + Hide repositories you have already viewed from discovery feeds + Clear Seen History + Reset all seen repositories so they appear again in feeds + Seen history cleared + Viewed \ No newline at end of file diff --git a/core/presentation/src/commonMain/kotlin/zed/rainxch/core/presentation/components/RepositoryCard.kt b/core/presentation/src/commonMain/kotlin/zed/rainxch/core/presentation/components/RepositoryCard.kt index 2f14548b..03d47e50 100644 --- a/core/presentation/src/commonMain/kotlin/zed/rainxch/core/presentation/components/RepositoryCard.kt +++ b/core/presentation/src/commonMain/kotlin/zed/rainxch/core/presentation/components/RepositoryCard.kt @@ -23,6 +23,7 @@ import androidx.compose.material.icons.filled.OpenInBrowser import androidx.compose.material.icons.filled.Share import androidx.compose.material.icons.filled.Star import androidx.compose.material.icons.filled.Update +import androidx.compose.material.icons.outlined.Visibility import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -54,6 +55,7 @@ import zed.rainxch.githubstore.core.presentation.res.forked_repository import zed.rainxch.githubstore.core.presentation.res.home_view_details import zed.rainxch.githubstore.core.presentation.res.installed import zed.rainxch.githubstore.core.presentation.res.open_in_browser +import zed.rainxch.githubstore.core.presentation.res.seen_badge import zed.rainxch.githubstore.core.presentation.res.share_repository import zed.rainxch.githubstore.core.presentation.res.update_available @@ -221,12 +223,23 @@ fun RepositoryCard( } } - if (discoveryRepositoryUi.isInstalled) { + if (discoveryRepositoryUi.isInstalled || discoveryRepositoryUi.isSeen) { Spacer(Modifier.height(12.dp)) - InstallStatusBadge( - isUpdateAvailable = discoveryRepositoryUi.isUpdateAvailable, - ) + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + if (discoveryRepositoryUi.isInstalled) { + InstallStatusBadge( + isUpdateAvailable = discoveryRepositoryUi.isUpdateAvailable, + ) + } + + if (discoveryRepositoryUi.isSeen) { + SeenBadge() + } + } } if (discoveryRepositoryUi.repository.availablePlatforms.isNotEmpty()) { @@ -434,6 +447,34 @@ fun InstallStatusBadge( } } +@Composable +fun SeenBadge(modifier: Modifier = Modifier) { + Surface( + modifier = modifier, + shape = RoundedCornerShape(12.dp), + color = MaterialTheme.colorScheme.surfaceContainerHighest, + ) { + Row( + modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(4.dp), + ) { + Icon( + imageVector = Icons.Outlined.Visibility, + contentDescription = null, + modifier = Modifier.size(14.dp), + tint = MaterialTheme.colorScheme.onSurfaceVariant, + ) + Text( + text = stringResource(Res.string.seen_badge), + style = MaterialTheme.typography.labelSmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + fontWeight = FontWeight.SemiBold, + ) + } + } +} + @Preview @Composable fun RepositoryCardPreview() { diff --git a/core/presentation/src/commonMain/kotlin/zed/rainxch/core/presentation/model/DiscoveryRepositoryUi.kt b/core/presentation/src/commonMain/kotlin/zed/rainxch/core/presentation/model/DiscoveryRepositoryUi.kt index 47f2a43c..a65e05b6 100644 --- a/core/presentation/src/commonMain/kotlin/zed/rainxch/core/presentation/model/DiscoveryRepositoryUi.kt +++ b/core/presentation/src/commonMain/kotlin/zed/rainxch/core/presentation/model/DiscoveryRepositoryUi.kt @@ -5,5 +5,6 @@ data class DiscoveryRepositoryUi( val isUpdateAvailable: Boolean, val isFavourite: Boolean, val isStarred: Boolean, + val isSeen: Boolean = false, val repository: GithubRepoSummaryUi, ) diff --git a/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsViewModel.kt b/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsViewModel.kt index fc3a5f9d..188cd270 100644 --- a/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsViewModel.kt +++ b/feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsViewModel.kt @@ -33,6 +33,7 @@ import zed.rainxch.core.domain.model.RateLimitException import zed.rainxch.core.domain.network.Downloader import zed.rainxch.core.domain.repository.FavouritesRepository import zed.rainxch.core.domain.repository.InstalledAppsRepository +import zed.rainxch.core.domain.repository.SeenReposRepository import zed.rainxch.core.domain.repository.StarredRepository import zed.rainxch.core.domain.repository.TweaksRepository import zed.rainxch.core.domain.system.Installer @@ -90,6 +91,7 @@ class DetailsViewModel( private val logger: GitHubStoreLogger, private val isComingFromUpdate: Boolean, private val tweaksRepository: TweaksRepository, + private val seenReposRepository: SeenReposRepository, ) : ViewModel() { private var hasLoadedInitialData = false private var currentDownloadJob: Job? = null @@ -160,6 +162,8 @@ class DetailsViewModel( } else { detailsRepository.getRepositoryById(repositoryId) } + launch { seenReposRepository.markAsSeen(repo.id) } + val isFavoriteDeferred = async { try { diff --git a/feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/HomeRoot.kt b/feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/HomeRoot.kt index b54e52d8..cbef7216 100644 --- a/feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/HomeRoot.kt +++ b/feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/HomeRoot.kt @@ -244,7 +244,17 @@ private fun MainState( bottomNavLiquidState: LiquidState, homeTopBarLiquidState: LiquidState, ) { - if (state.repos.isNotEmpty()) { + val visibleRepos by remember(state.repos, state.isHideSeenEnabled, state.seenRepoIds) { + derivedStateOf { + if (state.isHideSeenEnabled && state.seenRepoIds.isNotEmpty()) { + state.repos.filter { it.repository.id !in state.seenRepoIds } + } else { + state.repos + } + } + } + + if (visibleRepos.isNotEmpty()) { LazyVerticalStaggeredGrid( state = listState, columns = StaggeredGridCells.Adaptive(350.dp), @@ -254,7 +264,7 @@ private fun MainState( modifier = Modifier.fillMaxSize(), ) { items( - items = state.repos, + items = visibleRepos, key = { it.repository.id }, contentType = { "repo" }, ) { discoveryRepository -> diff --git a/feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/HomeState.kt b/feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/HomeState.kt index fefc0378..9d16b7ab 100644 --- a/feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/HomeState.kt +++ b/feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/HomeState.kt @@ -20,4 +20,6 @@ data class HomeState( val currentPlatform: DiscoveryPlatform = DiscoveryPlatform.All, val isPlatformPopupVisible: Boolean = false, val isLiquidGlassEnabled: Boolean = true, + val isHideSeenEnabled: Boolean = false, + val seenRepoIds: Set = emptySet(), ) diff --git a/feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/HomeViewModel.kt b/feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/HomeViewModel.kt index 58149c31..0e6cf86d 100644 --- a/feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/HomeViewModel.kt +++ b/feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/HomeViewModel.kt @@ -21,6 +21,7 @@ import zed.rainxch.core.domain.model.DiscoveryPlatform import zed.rainxch.core.domain.model.Platform import zed.rainxch.core.domain.repository.FavouritesRepository import zed.rainxch.core.domain.repository.InstalledAppsRepository +import zed.rainxch.core.domain.repository.SeenReposRepository import zed.rainxch.core.domain.repository.StarredRepository import zed.rainxch.core.domain.use_cases.SyncInstalledAppsUseCase import zed.rainxch.core.domain.repository.TweaksRepository @@ -42,6 +43,7 @@ class HomeViewModel( private val logger: GitHubStoreLogger, private val shareManager: ShareManager, private val tweaksRepository: TweaksRepository, + private val seenReposRepository: SeenReposRepository, ) : ViewModel() { private var hasLoadedInitialData = false private var currentJob: Job? = null @@ -61,6 +63,8 @@ class HomeViewModel( observeFavourites() observeStarredRepos() observeLiquidGlassEnabled() + observeSeenRepos() + observeHideSeenEnabled() hasLoadedInitialData = true } @@ -198,6 +202,8 @@ class HomeViewModel( .first() .associateBy { it.repoId } + val seenIds = _state.value.seenRepoIds + val newReposWithStatus = paginatedRepos.repos.map { repo -> val app = installedAppsMap[repo.id] @@ -208,6 +214,7 @@ class HomeViewModel( isInstalled = app != null, isFavourite = favourite != null, isStarred = starred != null, + isSeen = repo.id in seenIds, isUpdateAvailable = app?.isUpdateAvailable ?: false, repository = repo.toUi(), ) @@ -365,6 +372,31 @@ class HomeViewModel( } } + private fun observeSeenRepos() { + viewModelScope.launch { + seenReposRepository.getAllSeenRepoIds().collect { ids -> + _state.update { current -> + current.copy( + seenRepoIds = ids, + repos = + current.repos + .map { repo -> + repo.copy(isSeen = repo.repository.id in ids) + }.toImmutableList(), + ) + } + } + } + } + + private fun observeHideSeenEnabled() { + viewModelScope.launch { + tweaksRepository.getHideSeenEnabled().collect { enabled -> + _state.update { it.copy(isHideSeenEnabled = enabled) } + } + } + } + private fun observeFavourites() { viewModelScope.launch { favouritesRepository.getAllFavorites().collect { favourites -> diff --git a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileAction.kt b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileAction.kt index 57e77905..260ed337 100644 --- a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileAction.kt +++ b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileAction.kt @@ -94,5 +94,11 @@ sealed interface ProfileAction { val enabled: Boolean, ) : ProfileAction + data class OnHideSeenToggled( + val enabled: Boolean, + ) : ProfileAction + + data object OnClearSeenRepos : ProfileAction + data object OnSponsorClick : ProfileAction } diff --git a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileEvent.kt b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileEvent.kt index f49e1615..98626c2a 100644 --- a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileEvent.kt +++ b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileEvent.kt @@ -18,4 +18,6 @@ sealed interface ProfileEvent { data class OnCacheClearError( val message: String, ) : ProfileEvent + + data object OnSeenHistoryCleared : ProfileEvent } diff --git a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileRoot.kt b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileRoot.kt index f28e73e4..e877e2b4 100644 --- a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileRoot.kt +++ b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileRoot.kt @@ -92,6 +92,12 @@ fun ProfileRoot( snackbarState.showSnackbar(event.message) } } + + ProfileEvent.OnSeenHistoryCleared -> { + coroutineScope.launch { + snackbarState.showSnackbar(getString(Res.string.seen_history_cleared)) + } + } } } diff --git a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileState.kt b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileState.kt index d462c615..13a67512 100644 --- a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileState.kt +++ b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileState.kt @@ -30,4 +30,5 @@ data class ProfileState( val updateCheckIntervalHours: Long = 6L, val includePreReleases: Boolean = false, val isLiquidGlassEnabled: Boolean = true, + val isHideSeenEnabled: Boolean = false, ) diff --git a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileViewModel.kt b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileViewModel.kt index ec82ca42..f3ddc365 100644 --- a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileViewModel.kt +++ b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileViewModel.kt @@ -14,6 +14,7 @@ import kotlinx.coroutines.launch import org.jetbrains.compose.resources.getString import zed.rainxch.core.domain.model.ProxyConfig import zed.rainxch.core.domain.repository.ProxyRepository +import zed.rainxch.core.domain.repository.SeenReposRepository import zed.rainxch.core.domain.repository.TweaksRepository import zed.rainxch.core.domain.system.InstallerStatusProvider import zed.rainxch.core.domain.system.UpdateScheduleManager @@ -32,6 +33,7 @@ class ProfileViewModel( private val installerStatusProvider: InstallerStatusProvider, private val proxyRepository: ProxyRepository, private val updateScheduleManager: UpdateScheduleManager, + private val seenReposRepository: SeenReposRepository, ) : ViewModel() { private var userProfileJob: Job? = null @@ -51,6 +53,7 @@ class ProfileViewModel( loadUpdateCheckInterval() loadIncludePreReleases() loadLiquidGlassEnabled() + loadHideSeenEnabled() observeLoggedInStatus() @@ -257,6 +260,16 @@ class ProfileViewModel( } } + private fun loadHideSeenEnabled() { + viewModelScope.launch { + tweaksRepository.getHideSeenEnabled().collect { enabled -> + _state.update { + it.copy(isHideSeenEnabled = enabled) + } + } + } + } + private fun loadIncludePreReleases() { viewModelScope.launch { tweaksRepository.getIncludePreReleases().collect { enabled -> @@ -429,6 +442,19 @@ class ProfileViewModel( } } + is ProfileAction.OnHideSeenToggled -> { + viewModelScope.launch { + tweaksRepository.setHideSeenEnabled(action.enabled) + } + } + + ProfileAction.OnClearSeenRepos -> { + viewModelScope.launch { + seenReposRepository.clearAll() + _events.send(ProfileEvent.OnSeenHistoryCleared) + } + } + is ProfileAction.OnInstallerTypeSelected -> { viewModelScope.launch { tweaksRepository.setInstallerType(action.type) diff --git a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/Appearance.kt b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/Appearance.kt index 2ba42de6..852df7a9 100644 --- a/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/Appearance.kt +++ b/feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/components/sections/Appearance.kt @@ -145,6 +145,25 @@ fun LazyListScope.appearanceSection( onAction(ProfileAction.OnAutoDetectClipboardToggled(enabled)) }, ) + + VerticalSpacer(8.dp) + + ToggleSettingCard( + title = stringResource(Res.string.hide_seen_title), + description = stringResource(Res.string.hide_seen_description), + checked = state.isHideSeenEnabled, + onCheckedChange = { enabled -> + onAction(ProfileAction.OnHideSeenToggled(enabled)) + }, + ) + + VerticalSpacer(8.dp) + + ClearSeenHistoryCard( + onClick = { + onAction(ProfileAction.OnClearSeenRepos) + }, + ) } } @@ -431,3 +450,39 @@ private fun ToggleSettingCard( } } } + +@Composable +private fun ClearSeenHistoryCard( + onClick: () -> Unit, +) { + ExpressiveCard { + Row( + modifier = + Modifier + .fillMaxWidth() + .clickable(onClick = onClick) + .padding(16.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + ) { + Column( + modifier = Modifier.weight(1f), + ) { + Text( + text = stringResource(Res.string.clear_seen_history), + style = MaterialTheme.typography.titleMedium, + color = MaterialTheme.colorScheme.onSurface, + fontWeight = FontWeight.SemiBold, + ) + + VerticalSpacer(4.dp) + + Text( + text = stringResource(Res.string.clear_seen_history_description), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } + } +} diff --git a/feature/search/presentation/src/commonMain/kotlin/zed/rainxch/search/presentation/SearchRoot.kt b/feature/search/presentation/src/commonMain/kotlin/zed/rainxch/search/presentation/SearchRoot.kt index 1a16d880..d7c7aced 100644 --- a/feature/search/presentation/src/commonMain/kotlin/zed/rainxch/search/presentation/SearchRoot.kt +++ b/feature/search/presentation/src/commonMain/kotlin/zed/rainxch/search/presentation/SearchRoot.kt @@ -483,6 +483,16 @@ fun SearchScreen( ) } + val visibleRepos by remember(state.repositories, state.isHideSeenEnabled, state.seenRepoIds) { + derivedStateOf { + if (state.isHideSeenEnabled && state.seenRepoIds.isNotEmpty()) { + state.repositories.filter { it.repository.id !in state.seenRepoIds } + } else { + state.repositories + } + } + } + Box(Modifier.fillMaxSize()) { if (state.isLoading && state.repositories.isEmpty()) { Box( @@ -515,7 +525,7 @@ fun SearchScreen( } } - if (state.repositories.isNotEmpty()) { + if (visibleRepos.isNotEmpty()) { LazyVerticalStaggeredGrid( state = listState, columns = StaggeredGridCells.Adaptive(350.dp), @@ -534,7 +544,7 @@ fun SearchScreen( ), ) { items( - items = state.repositories, + items = visibleRepos, key = { it.repository.id }, ) { discoveryRepository -> RepositoryCard( diff --git a/feature/search/presentation/src/commonMain/kotlin/zed/rainxch/search/presentation/SearchState.kt b/feature/search/presentation/src/commonMain/kotlin/zed/rainxch/search/presentation/SearchState.kt index efa529c9..969e7f2b 100644 --- a/feature/search/presentation/src/commonMain/kotlin/zed/rainxch/search/presentation/SearchState.kt +++ b/feature/search/presentation/src/commonMain/kotlin/zed/rainxch/search/presentation/SearchState.kt @@ -18,6 +18,8 @@ data class SearchState( val selectedLanguage: ProgrammingLanguageUi = ProgrammingLanguageUi.All, val isLoading: Boolean = false, val isLiquidGlassEnabled: Boolean = true, + val isHideSeenEnabled: Boolean = false, + val seenRepoIds: Set = emptySet(), val isLoadingMore: Boolean = false, val errorMessage: String? = null, val hasMorePages: Boolean = true, diff --git a/feature/search/presentation/src/commonMain/kotlin/zed/rainxch/search/presentation/SearchViewModel.kt b/feature/search/presentation/src/commonMain/kotlin/zed/rainxch/search/presentation/SearchViewModel.kt index 45c11ef9..7d7ef567 100644 --- a/feature/search/presentation/src/commonMain/kotlin/zed/rainxch/search/presentation/SearchViewModel.kt +++ b/feature/search/presentation/src/commonMain/kotlin/zed/rainxch/search/presentation/SearchViewModel.kt @@ -22,6 +22,7 @@ import zed.rainxch.core.domain.model.Platform import zed.rainxch.core.domain.model.RateLimitException import zed.rainxch.core.domain.repository.FavouritesRepository import zed.rainxch.core.domain.repository.InstalledAppsRepository +import zed.rainxch.core.domain.repository.SeenReposRepository import zed.rainxch.core.domain.repository.StarredRepository import zed.rainxch.core.domain.repository.TweaksRepository import zed.rainxch.core.domain.use_cases.SyncInstalledAppsUseCase @@ -51,6 +52,7 @@ class SearchViewModel( private val platform: Platform, private val clipboardHelper: ClipboardHelper, private val tweaksRepository: TweaksRepository, + private val seenReposRepository: SeenReposRepository, ) : ViewModel() { private var hasLoadedInitialData = false private var currentSearchJob: Job? = null @@ -73,6 +75,8 @@ class SearchViewModel( observeFavouriteApps() observeStarredRepos() observeLiquidGlassEnabled() + observeSeenRepos() + observeHideSeenEnabled() observeClipboardSetting() checkClipboardForLinks() @@ -96,6 +100,31 @@ class SearchViewModel( } } + private fun observeSeenRepos() { + viewModelScope.launch { + seenReposRepository.getAllSeenRepoIds().collect { ids -> + _state.update { current -> + current.copy( + seenRepoIds = ids, + repositories = + current.repositories + .map { repo -> + repo.copy(isSeen = repo.repository.id in ids) + }.toImmutableList(), + ) + } + } + } + } + + private fun observeHideSeenEnabled() { + viewModelScope.launch { + tweaksRepository.getHideSeenEnabled().collect { enabled -> + _state.update { it.copy(isHideSeenEnabled = enabled) } + } + } + } + private val _events = Channel() val events = _events.receiveAsFlow() @@ -277,6 +306,8 @@ class SearchViewModel( ).collect { paginatedRepos -> currentPage = paginatedRepos.nextPageIndex + val seenIds = _state.value.seenRepoIds + val newReposWithStatus = paginatedRepos.repos.map { repo -> val app = installedMap[repo.id] @@ -287,6 +318,7 @@ class SearchViewModel( isInstalled = app != null, isFavourite = favourite != null, isStarred = starred != null, + isSeen = repo.id in seenIds, isUpdateAvailable = app?.isUpdateAvailable ?: false, repository = repo.toUi(), ) diff --git a/packaging/flatpak/README.md b/packaging/flatpak/README.md new file mode 100644 index 00000000..4ee85fd3 --- /dev/null +++ b/packaging/flatpak/README.md @@ -0,0 +1,116 @@ +# Flatpak Packaging for GitHub Store + +## Prerequisites + +Install Flatpak and the build tools: + +```bash +# Fedora +sudo dnf install flatpak flatpak-builder + +# Ubuntu/Debian +sudo apt install flatpak flatpak-builder + +# Arch +sudo pacman -S flatpak flatpak-builder +``` + +Install the required runtimes: + +```bash +flatpak install flathub org.freedesktop.Platform//24.08 +flatpak install flathub org.freedesktop.Sdk//24.08 +flatpak install flathub org.freedesktop.Sdk.Extension.openjdk21//24.08 +``` + +## Setup (One-Time) + +### 1. Generate Gradle dependency sources + +Flatpak builds run without network access, so all Maven/Gradle dependencies must +be pre-downloaded and listed in a JSON manifest. + +Add the plugin to your root `build.gradle.kts`: + +```kotlin +plugins { + id("io.github.jwharm.flatpak-gradle-generator") version "1.7.0" +} +``` + +Then generate the sources file: + +```bash +./gradlew flatpakGradleGenerator --no-configuration-cache +``` + +This creates `flatpak-sources.json` in the project root. Move it to this directory: + +```bash +mv flatpak-sources.json packaging/flatpak/ +``` + +### 2. Verify SHA256 hashes + +The manifest uses pre-computed SHA256 hashes. To verify or update them: + +```bash +# Gradle distribution +curl -sL https://services.gradle.org/distributions/gradle-8.14.3-bin.zip | sha256sum + +# JBR x64 (check latest at https://github.com/JetBrains/JetBrainsRuntime/releases) +curl -sL https://cache-redirector.jetbrains.com/intellij-jbr/jbr-21.0.10-linux-x64-b1163.105.tar.gz | sha256sum + +# JBR aarch64 +curl -sL https://cache-redirector.jetbrains.com/intellij-jbr/jbr-21.0.10-linux-aarch64-b1163.105.tar.gz | sha256sum +``` + +### 3. Update screenshot URLs + +Edit `zed.rainxch.githubstore.metainfo.xml` to point to hosted screenshot images. +Flathub requires at least one screenshot with a publicly accessible URL. + +## Building Locally + +```bash +cd packaging/flatpak + +# Build +flatpak-builder --force-clean build-dir zed.rainxch.githubstore.yml + +# Test run +flatpak-builder --run build-dir zed.rainxch.githubstore.yml githubstore + +# Install locally +flatpak-builder --user --install --force-clean build-dir zed.rainxch.githubstore.yml +``` + +## Validating + +```bash +# Validate AppStream metainfo +flatpak run org.freedesktop.appstream-glib validate zed.rainxch.githubstore.metainfo.xml + +# Lint manifest (requires org.flatpak.Builder) +flatpak run --command=flatpak-builder-lint org.flatpak.Builder manifest zed.rainxch.githubstore.yml +``` + +## Publishing to Flathub + +1. Fork `https://github.com/flathub/flathub` +2. Checkout the `new-pr` branch +3. Copy the manifest YAML and `flatpak-sources.json` to the repo root +4. Open a PR titled "Add zed.rainxch.githubstore" +5. Reviewers will trigger test builds with `bot, build` +6. After approval, you get write access to `flathub/zed.rainxch.githubstore` + +## File Reference + +| File | Purpose | +|------|---------| +| `zed.rainxch.githubstore.yml` | Flatpak build manifest | +| `zed.rainxch.githubstore.desktop` | Desktop launcher entry | +| `zed.rainxch.githubstore.metainfo.xml` | AppStream metadata for Flathub listing | +| `githubstore.sh` | Shell launcher (invokes `java -jar` with bundled JRE) | +| `disable-android-for-flatpak.sh` | Strips Android targets for sandbox build | +| `flatpak-sources.json` | Pre-downloaded Gradle dependencies (generated) |