Add support for muting conversations (#1732)
* Add support for muting conversations Implements #1731 * Fix CI * Apply code review feedback
This commit is contained in:
parent
8e54e4ae16
commit
8cb83050ac
21 changed files with 904 additions and 19 deletions
741
app/schemas/com.keylesspalace.tusky.db.AppDatabase/23.json
Normal file
741
app/schemas/com.keylesspalace.tusky.db.AppDatabase/23.json
Normal file
|
@ -0,0 +1,741 @@
|
||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 23,
|
||||||
|
"identityHash": "03a7436643ef356198742c5f8e054f5f",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"tableName": "TootEntity",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `text` TEXT, `urls` TEXT, `descriptions` TEXT, `contentWarning` TEXT, `inReplyToId` TEXT, `inReplyToText` TEXT, `inReplyToUsername` TEXT, `visibility` INTEGER, `poll` TEXT)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "uid",
|
||||||
|
"columnName": "uid",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "text",
|
||||||
|
"columnName": "text",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "urls",
|
||||||
|
"columnName": "urls",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "descriptions",
|
||||||
|
"columnName": "descriptions",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "contentWarning",
|
||||||
|
"columnName": "contentWarning",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "inReplyToId",
|
||||||
|
"columnName": "inReplyToId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "inReplyToText",
|
||||||
|
"columnName": "inReplyToText",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "inReplyToUsername",
|
||||||
|
"columnName": "inReplyToUsername",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "visibility",
|
||||||
|
"columnName": "visibility",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "poll",
|
||||||
|
"columnName": "poll",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"uid"
|
||||||
|
],
|
||||||
|
"autoGenerate": true
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "AccountEntity",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `domain` TEXT NOT NULL, `accessToken` TEXT NOT NULL, `isActive` INTEGER NOT NULL, `accountId` TEXT NOT NULL, `username` TEXT NOT NULL, `displayName` TEXT NOT NULL, `profilePictureUrl` TEXT NOT NULL, `notificationsEnabled` INTEGER NOT NULL, `notificationsMentioned` INTEGER NOT NULL, `notificationsFollowed` INTEGER NOT NULL, `notificationsFollowRequested` INTEGER NOT NULL, `notificationsReblogged` INTEGER NOT NULL, `notificationsFavorited` INTEGER NOT NULL, `notificationsPolls` INTEGER NOT NULL, `notificationSound` INTEGER NOT NULL, `notificationVibration` INTEGER NOT NULL, `notificationLight` INTEGER NOT NULL, `defaultPostPrivacy` INTEGER NOT NULL, `defaultMediaSensitivity` INTEGER NOT NULL, `alwaysShowSensitiveMedia` INTEGER NOT NULL, `alwaysOpenSpoiler` INTEGER NOT NULL, `mediaPreviewEnabled` INTEGER NOT NULL, `lastNotificationId` TEXT NOT NULL, `activeNotifications` TEXT NOT NULL, `emojis` TEXT NOT NULL, `tabPreferences` TEXT NOT NULL, `notificationsFilter` TEXT NOT NULL)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "domain",
|
||||||
|
"columnName": "domain",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "accessToken",
|
||||||
|
"columnName": "accessToken",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isActive",
|
||||||
|
"columnName": "isActive",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "accountId",
|
||||||
|
"columnName": "accountId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "username",
|
||||||
|
"columnName": "username",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "displayName",
|
||||||
|
"columnName": "displayName",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "profilePictureUrl",
|
||||||
|
"columnName": "profilePictureUrl",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "notificationsEnabled",
|
||||||
|
"columnName": "notificationsEnabled",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "notificationsMentioned",
|
||||||
|
"columnName": "notificationsMentioned",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "notificationsFollowed",
|
||||||
|
"columnName": "notificationsFollowed",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "notificationsFollowRequested",
|
||||||
|
"columnName": "notificationsFollowRequested",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "notificationsReblogged",
|
||||||
|
"columnName": "notificationsReblogged",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "notificationsFavorited",
|
||||||
|
"columnName": "notificationsFavorited",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "notificationsPolls",
|
||||||
|
"columnName": "notificationsPolls",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "notificationSound",
|
||||||
|
"columnName": "notificationSound",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "notificationVibration",
|
||||||
|
"columnName": "notificationVibration",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "notificationLight",
|
||||||
|
"columnName": "notificationLight",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "defaultPostPrivacy",
|
||||||
|
"columnName": "defaultPostPrivacy",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "defaultMediaSensitivity",
|
||||||
|
"columnName": "defaultMediaSensitivity",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "alwaysShowSensitiveMedia",
|
||||||
|
"columnName": "alwaysShowSensitiveMedia",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "alwaysOpenSpoiler",
|
||||||
|
"columnName": "alwaysOpenSpoiler",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "mediaPreviewEnabled",
|
||||||
|
"columnName": "mediaPreviewEnabled",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastNotificationId",
|
||||||
|
"columnName": "lastNotificationId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "activeNotifications",
|
||||||
|
"columnName": "activeNotifications",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "emojis",
|
||||||
|
"columnName": "emojis",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "tabPreferences",
|
||||||
|
"columnName": "tabPreferences",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "notificationsFilter",
|
||||||
|
"columnName": "notificationsFilter",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"autoGenerate": true
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_AccountEntity_domain_accountId",
|
||||||
|
"unique": true,
|
||||||
|
"columnNames": [
|
||||||
|
"domain",
|
||||||
|
"accountId"
|
||||||
|
],
|
||||||
|
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_AccountEntity_domain_accountId` ON `${TABLE_NAME}` (`domain`, `accountId`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "InstanceEntity",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`instance` TEXT NOT NULL, `emojiList` TEXT, `maximumTootCharacters` INTEGER, `maxPollOptions` INTEGER, `maxPollOptionLength` INTEGER, `version` TEXT, PRIMARY KEY(`instance`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "instance",
|
||||||
|
"columnName": "instance",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "emojiList",
|
||||||
|
"columnName": "emojiList",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "maximumTootCharacters",
|
||||||
|
"columnName": "maximumTootCharacters",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "maxPollOptions",
|
||||||
|
"columnName": "maxPollOptions",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "maxPollOptionLength",
|
||||||
|
"columnName": "maxPollOptionLength",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "version",
|
||||||
|
"columnName": "version",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"instance"
|
||||||
|
],
|
||||||
|
"autoGenerate": false
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "TimelineStatusEntity",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT NOT NULL, `url` TEXT, `timelineUserId` INTEGER NOT NULL, `authorServerId` TEXT, `inReplyToId` TEXT, `inReplyToAccountId` TEXT, `content` TEXT, `createdAt` INTEGER NOT NULL, `emojis` TEXT, `reblogsCount` INTEGER NOT NULL, `favouritesCount` INTEGER NOT NULL, `reblogged` INTEGER NOT NULL, `bookmarked` INTEGER NOT NULL, `favourited` INTEGER NOT NULL, `sensitive` INTEGER NOT NULL, `spoilerText` TEXT, `visibility` INTEGER, `attachments` TEXT, `mentions` TEXT, `application` TEXT, `reblogServerId` TEXT, `reblogAccountId` TEXT, `poll` TEXT, `muted` INTEGER, PRIMARY KEY(`serverId`, `timelineUserId`), FOREIGN KEY(`authorServerId`, `timelineUserId`) REFERENCES `TimelineAccountEntity`(`serverId`, `timelineUserId`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "serverId",
|
||||||
|
"columnName": "serverId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "url",
|
||||||
|
"columnName": "url",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "timelineUserId",
|
||||||
|
"columnName": "timelineUserId",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "authorServerId",
|
||||||
|
"columnName": "authorServerId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "inReplyToId",
|
||||||
|
"columnName": "inReplyToId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "inReplyToAccountId",
|
||||||
|
"columnName": "inReplyToAccountId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "content",
|
||||||
|
"columnName": "content",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "createdAt",
|
||||||
|
"columnName": "createdAt",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "emojis",
|
||||||
|
"columnName": "emojis",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "reblogsCount",
|
||||||
|
"columnName": "reblogsCount",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "favouritesCount",
|
||||||
|
"columnName": "favouritesCount",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "reblogged",
|
||||||
|
"columnName": "reblogged",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "bookmarked",
|
||||||
|
"columnName": "bookmarked",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "favourited",
|
||||||
|
"columnName": "favourited",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "sensitive",
|
||||||
|
"columnName": "sensitive",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "spoilerText",
|
||||||
|
"columnName": "spoilerText",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "visibility",
|
||||||
|
"columnName": "visibility",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "attachments",
|
||||||
|
"columnName": "attachments",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "mentions",
|
||||||
|
"columnName": "mentions",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "application",
|
||||||
|
"columnName": "application",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "reblogServerId",
|
||||||
|
"columnName": "reblogServerId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "reblogAccountId",
|
||||||
|
"columnName": "reblogAccountId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "poll",
|
||||||
|
"columnName": "poll",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "muted",
|
||||||
|
"columnName": "muted",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"serverId",
|
||||||
|
"timelineUserId"
|
||||||
|
],
|
||||||
|
"autoGenerate": false
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_TimelineStatusEntity_authorServerId_timelineUserId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"authorServerId",
|
||||||
|
"timelineUserId"
|
||||||
|
],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_TimelineStatusEntity_authorServerId_timelineUserId` ON `${TABLE_NAME}` (`authorServerId`, `timelineUserId`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": [
|
||||||
|
{
|
||||||
|
"table": "TimelineAccountEntity",
|
||||||
|
"onDelete": "NO ACTION",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"authorServerId",
|
||||||
|
"timelineUserId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"serverId",
|
||||||
|
"timelineUserId"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "TimelineAccountEntity",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT NOT NULL, `timelineUserId` INTEGER NOT NULL, `localUsername` TEXT NOT NULL, `username` TEXT NOT NULL, `displayName` TEXT NOT NULL, `url` TEXT NOT NULL, `avatar` TEXT NOT NULL, `emojis` TEXT NOT NULL, `bot` INTEGER NOT NULL, PRIMARY KEY(`serverId`, `timelineUserId`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "serverId",
|
||||||
|
"columnName": "serverId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "timelineUserId",
|
||||||
|
"columnName": "timelineUserId",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "localUsername",
|
||||||
|
"columnName": "localUsername",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "username",
|
||||||
|
"columnName": "username",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "displayName",
|
||||||
|
"columnName": "displayName",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "url",
|
||||||
|
"columnName": "url",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "avatar",
|
||||||
|
"columnName": "avatar",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "emojis",
|
||||||
|
"columnName": "emojis",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "bot",
|
||||||
|
"columnName": "bot",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"serverId",
|
||||||
|
"timelineUserId"
|
||||||
|
],
|
||||||
|
"autoGenerate": false
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "ConversationEntity",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `id` TEXT NOT NULL, `accounts` TEXT NOT NULL, `unread` INTEGER NOT NULL, `s_id` TEXT NOT NULL, `s_url` TEXT, `s_inReplyToId` TEXT, `s_inReplyToAccountId` TEXT, `s_account` TEXT NOT NULL, `s_content` TEXT NOT NULL, `s_createdAt` INTEGER NOT NULL, `s_emojis` TEXT NOT NULL, `s_favouritesCount` INTEGER NOT NULL, `s_favourited` INTEGER NOT NULL, `s_bookmarked` INTEGER NOT NULL, `s_sensitive` INTEGER NOT NULL, `s_spoilerText` TEXT NOT NULL, `s_attachments` TEXT NOT NULL, `s_mentions` TEXT NOT NULL, `s_showingHiddenContent` INTEGER NOT NULL, `s_expanded` INTEGER NOT NULL, `s_collapsible` INTEGER NOT NULL, `s_collapsed` INTEGER NOT NULL, `s_poll` TEXT, PRIMARY KEY(`id`, `accountId`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "accountId",
|
||||||
|
"columnName": "accountId",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "accounts",
|
||||||
|
"columnName": "accounts",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "unread",
|
||||||
|
"columnName": "unread",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastStatus.id",
|
||||||
|
"columnName": "s_id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastStatus.url",
|
||||||
|
"columnName": "s_url",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastStatus.inReplyToId",
|
||||||
|
"columnName": "s_inReplyToId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastStatus.inReplyToAccountId",
|
||||||
|
"columnName": "s_inReplyToAccountId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastStatus.account",
|
||||||
|
"columnName": "s_account",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastStatus.content",
|
||||||
|
"columnName": "s_content",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastStatus.createdAt",
|
||||||
|
"columnName": "s_createdAt",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastStatus.emojis",
|
||||||
|
"columnName": "s_emojis",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastStatus.favouritesCount",
|
||||||
|
"columnName": "s_favouritesCount",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastStatus.favourited",
|
||||||
|
"columnName": "s_favourited",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastStatus.bookmarked",
|
||||||
|
"columnName": "s_bookmarked",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastStatus.sensitive",
|
||||||
|
"columnName": "s_sensitive",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastStatus.spoilerText",
|
||||||
|
"columnName": "s_spoilerText",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastStatus.attachments",
|
||||||
|
"columnName": "s_attachments",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastStatus.mentions",
|
||||||
|
"columnName": "s_mentions",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastStatus.showingHiddenContent",
|
||||||
|
"columnName": "s_showingHiddenContent",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastStatus.expanded",
|
||||||
|
"columnName": "s_expanded",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastStatus.collapsible",
|
||||||
|
"columnName": "s_collapsible",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastStatus.collapsed",
|
||||||
|
"columnName": "s_collapsed",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastStatus.poll",
|
||||||
|
"columnName": "s_poll",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"columnNames": [
|
||||||
|
"id",
|
||||||
|
"accountId"
|
||||||
|
],
|
||||||
|
"autoGenerate": false
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"views": [],
|
||||||
|
"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, '03a7436643ef356198742c5f8e054f5f')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ import com.keylesspalace.tusky.entity.Status
|
||||||
data class FavoriteEvent(val statusId: String, val favourite: Boolean) : Dispatchable
|
data class FavoriteEvent(val statusId: String, val favourite: Boolean) : Dispatchable
|
||||||
data class ReblogEvent(val statusId: String, val reblog: Boolean) : Dispatchable
|
data class ReblogEvent(val statusId: String, val reblog: Boolean) : Dispatchable
|
||||||
data class BookmarkEvent(val statusId: String, val bookmark: Boolean) : Dispatchable
|
data class BookmarkEvent(val statusId: String, val bookmark: Boolean) : Dispatchable
|
||||||
|
data class MuteConversationEvent(val statusId: String, val mute: Boolean) : Dispatchable
|
||||||
data class UnfollowEvent(val accountId: String) : Dispatchable
|
data class UnfollowEvent(val accountId: String) : Dispatchable
|
||||||
data class BlockEvent(val accountId: String) : Dispatchable
|
data class BlockEvent(val accountId: String) : Dispatchable
|
||||||
data class MuteEvent(val accountId: String) : Dispatchable
|
data class MuteEvent(val accountId: String) : Dispatchable
|
||||||
|
|
|
@ -157,6 +157,7 @@ data class ConversationStatusEntity(
|
||||||
mentions = mentions,
|
mentions = mentions,
|
||||||
application = null,
|
application = null,
|
||||||
pinned = false,
|
pinned = false,
|
||||||
|
muted = false,
|
||||||
poll = poll,
|
poll = poll,
|
||||||
card = null)
|
card = null)
|
||||||
}
|
}
|
||||||
|
|
|
@ -213,6 +213,18 @@ class SearchViewModel @Inject constructor(
|
||||||
search(currentQuery)
|
search(currentQuery)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun muteConversation(status: Pair<Status, StatusViewData.Concrete>, mute: Boolean) {
|
||||||
|
val idx = loadedStatuses.indexOf(status)
|
||||||
|
if (idx >= 0) {
|
||||||
|
val newPair = Pair(status.first, StatusViewData.Builder(status.second).setMuted(mute).createStatusViewData())
|
||||||
|
loadedStatuses[idx] = newPair
|
||||||
|
repoResultStatus.value?.refresh?.invoke()
|
||||||
|
}
|
||||||
|
timelineCases.muteConversation(status.first, mute)
|
||||||
|
.onErrorReturnItem(status.first)
|
||||||
|
.subscribe()
|
||||||
|
.autoDispose()
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "SearchViewModel"
|
private const val TAG = "SearchViewModel"
|
||||||
|
|
|
@ -49,6 +49,7 @@ import com.keylesspalace.tusky.components.search.adapter.SearchStatusesAdapter
|
||||||
import com.keylesspalace.tusky.db.AccountEntity
|
import com.keylesspalace.tusky.db.AccountEntity
|
||||||
import com.keylesspalace.tusky.entity.Attachment
|
import com.keylesspalace.tusky.entity.Attachment
|
||||||
import com.keylesspalace.tusky.entity.Status
|
import com.keylesspalace.tusky.entity.Status
|
||||||
|
import com.keylesspalace.tusky.entity.Status.Mention
|
||||||
import com.keylesspalace.tusky.interfaces.AccountSelectionListener
|
import com.keylesspalace.tusky.interfaces.AccountSelectionListener
|
||||||
import com.keylesspalace.tusky.interfaces.StatusActionListener
|
import com.keylesspalace.tusky.interfaces.StatusActionListener
|
||||||
import com.keylesspalace.tusky.util.CardViewMode
|
import com.keylesspalace.tusky.util.CardViewMode
|
||||||
|
@ -228,12 +229,9 @@ class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concre
|
||||||
val loggedInAccountId = viewModel.activeAccount?.accountId
|
val loggedInAccountId = viewModel.activeAccount?.accountId
|
||||||
|
|
||||||
val popup = PopupMenu(view.context, view)
|
val popup = PopupMenu(view.context, view)
|
||||||
|
val statusIsByCurrentUser = loggedInAccountId?.equals(accountId) == true
|
||||||
// Give a different menu depending on whether this is the user's own toot or not.
|
// Give a different menu depending on whether this is the user's own toot or not.
|
||||||
if (loggedInAccountId == null || loggedInAccountId != accountId) {
|
if (statusIsByCurrentUser) {
|
||||||
popup.inflate(R.menu.status_more)
|
|
||||||
val menu = popup.menu
|
|
||||||
menu.findItem(R.id.status_download_media).isVisible = status.attachments.isNotEmpty()
|
|
||||||
} else {
|
|
||||||
popup.inflate(R.menu.status_more_for_user)
|
popup.inflate(R.menu.status_more_for_user)
|
||||||
val menu = popup.menu
|
val menu = popup.menu
|
||||||
menu.findItem(R.id.status_open_as).isVisible = !statusUrl.isNullOrBlank()
|
menu.findItem(R.id.status_open_as).isVisible = !statusUrl.isNullOrBlank()
|
||||||
|
@ -251,6 +249,10 @@ class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concre
|
||||||
Status.Visibility.UNKNOWN, Status.Visibility.DIRECT -> {
|
Status.Visibility.UNKNOWN, Status.Visibility.DIRECT -> {
|
||||||
} //Ignore
|
} //Ignore
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
popup.inflate(R.menu.status_more)
|
||||||
|
val menu = popup.menu
|
||||||
|
menu.findItem(R.id.status_download_media).isVisible = status.attachments.isNotEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
val openAsItem = popup.menu.findItem(R.id.status_open_as)
|
val openAsItem = popup.menu.findItem(R.id.status_open_as)
|
||||||
|
@ -266,6 +268,19 @@ class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concre
|
||||||
}
|
}
|
||||||
openAsItem.title = openAsTitle
|
openAsItem.title = openAsTitle
|
||||||
|
|
||||||
|
val mutable = statusIsByCurrentUser || accountIsInMentions(viewModel.activeAccount, status.mentions)
|
||||||
|
val muteConversationItem = popup.menu.findItem(R.id.status_mute_conversation).apply {
|
||||||
|
isVisible = mutable
|
||||||
|
}
|
||||||
|
if (mutable) {
|
||||||
|
muteConversationItem.setTitle(
|
||||||
|
if (status.muted == true) {
|
||||||
|
R.string.action_unmute_conversation
|
||||||
|
} else {
|
||||||
|
R.string.action_mute_conversation
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
popup.setOnMenuItemClickListener { item ->
|
popup.setOnMenuItemClickListener { item ->
|
||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
R.id.status_share_content -> {
|
R.id.status_share_content -> {
|
||||||
|
@ -303,6 +318,12 @@ class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concre
|
||||||
requestDownloadAllMedia(status)
|
requestDownloadAllMedia(status)
|
||||||
return@setOnMenuItemClickListener true
|
return@setOnMenuItemClickListener true
|
||||||
}
|
}
|
||||||
|
R.id.status_mute_conversation -> {
|
||||||
|
searchAdapter.getItem(position)?.let { foundStatus ->
|
||||||
|
viewModel.muteConversation(foundStatus, status.muted != true)
|
||||||
|
}
|
||||||
|
return@setOnMenuItemClickListener true
|
||||||
|
}
|
||||||
R.id.status_mute -> {
|
R.id.status_mute -> {
|
||||||
viewModel.muteAcount(accountId)
|
viewModel.muteAcount(accountId)
|
||||||
return@setOnMenuItemClickListener true
|
return@setOnMenuItemClickListener true
|
||||||
|
@ -341,6 +362,12 @@ class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concre
|
||||||
popup.show()
|
popup.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun accountIsInMentions(account: AccountEntity?, mentions: Array<Mention>): Boolean {
|
||||||
|
return mentions.firstOrNull {
|
||||||
|
account?.username == it.username && account.domain == Uri.parse(it.url)?.host
|
||||||
|
} != null
|
||||||
|
}
|
||||||
|
|
||||||
private fun showOpenAsDialog(statusUrl: String, dialogTitle: CharSequence) {
|
private fun showOpenAsDialog(statusUrl: String, dialogTitle: CharSequence) {
|
||||||
bottomSheetActivity?.showAccountChooserDialog(dialogTitle, false, object : AccountSelectionListener {
|
bottomSheetActivity?.showAccountChooserDialog(dialogTitle, false, object : AccountSelectionListener {
|
||||||
override fun onAccountSelected(account: AccountEntity) {
|
override fun onAccountSelected(account: AccountEntity) {
|
||||||
|
|
|
@ -30,7 +30,7 @@ import androidx.annotation.NonNull;
|
||||||
|
|
||||||
@Database(entities = {TootEntity.class, AccountEntity.class, InstanceEntity.class, TimelineStatusEntity.class,
|
@Database(entities = {TootEntity.class, AccountEntity.class, InstanceEntity.class, TimelineStatusEntity.class,
|
||||||
TimelineAccountEntity.class, ConversationEntity.class
|
TimelineAccountEntity.class, ConversationEntity.class
|
||||||
}, version = 22)
|
}, version = 23)
|
||||||
public abstract class AppDatabase extends RoomDatabase {
|
public abstract class AppDatabase extends RoomDatabase {
|
||||||
|
|
||||||
public abstract TootDao tootDao();
|
public abstract TootDao tootDao();
|
||||||
|
@ -333,4 +333,11 @@ public abstract class AppDatabase extends RoomDatabase {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static final Migration MIGRATION_22_23 = new Migration(22, 23) {
|
||||||
|
@Override
|
||||||
|
public void migrate(@NonNull SupportSQLiteDatabase database) {
|
||||||
|
database.execSQL("ALTER TABLE `TimelineStatusEntity` ADD COLUMN `muted` INTEGER");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,8 @@ data class TimelineStatusEntity(
|
||||||
val application: String?,
|
val application: String?,
|
||||||
val reblogServerId: String?, // if it has a reblogged status, it's id is stored here
|
val reblogServerId: String?, // if it has a reblogged status, it's id is stored here
|
||||||
val reblogAccountId: String?,
|
val reblogAccountId: String?,
|
||||||
val poll: String?
|
val poll: String?,
|
||||||
|
val muted: Boolean?
|
||||||
)
|
)
|
||||||
|
|
||||||
@Entity(
|
@Entity(
|
||||||
|
|
|
@ -79,7 +79,8 @@ class AppModule {
|
||||||
AppDatabase.MIGRATION_11_12, AppDatabase.MIGRATION_12_13, AppDatabase.MIGRATION_10_13,
|
AppDatabase.MIGRATION_11_12, AppDatabase.MIGRATION_12_13, AppDatabase.MIGRATION_10_13,
|
||||||
AppDatabase.MIGRATION_13_14, AppDatabase.MIGRATION_14_15, AppDatabase.MIGRATION_15_16,
|
AppDatabase.MIGRATION_13_14, AppDatabase.MIGRATION_14_15, AppDatabase.MIGRATION_15_16,
|
||||||
AppDatabase.MIGRATION_16_17, AppDatabase.MIGRATION_17_18, AppDatabase.MIGRATION_18_19,
|
AppDatabase.MIGRATION_16_17, AppDatabase.MIGRATION_17_18, AppDatabase.MIGRATION_18_19,
|
||||||
AppDatabase.MIGRATION_19_20, AppDatabase.MIGRATION_20_21, AppDatabase.MIGRATION_21_22)
|
AppDatabase.MIGRATION_19_20, AppDatabase.MIGRATION_20_21, AppDatabase.MIGRATION_21_22,
|
||||||
|
AppDatabase.MIGRATION_22_23)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@ data class Status(
|
||||||
val mentions: Array<Mention>,
|
val mentions: Array<Mention>,
|
||||||
val application: Application?,
|
val application: Application?,
|
||||||
var pinned: Boolean?,
|
var pinned: Boolean?,
|
||||||
|
var muted: Boolean?,
|
||||||
val poll: Poll?,
|
val poll: Poll?,
|
||||||
val card: Card?
|
val card: Card?
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -185,11 +185,8 @@ public abstract class SFragment extends BaseFragment implements Injectable {
|
||||||
|
|
||||||
PopupMenu popup = new PopupMenu(getContext(), view);
|
PopupMenu popup = new PopupMenu(getContext(), view);
|
||||||
// Give a different menu depending on whether this is the user's own toot or not.
|
// Give a different menu depending on whether this is the user's own toot or not.
|
||||||
if (loggedInAccountId == null || !loggedInAccountId.equals(accountId)) {
|
boolean statusIsByCurrentUser = loggedInAccountId != null && loggedInAccountId.equals(accountId);
|
||||||
popup.inflate(R.menu.status_more);
|
if (statusIsByCurrentUser) {
|
||||||
Menu menu = popup.getMenu();
|
|
||||||
menu.findItem(R.id.status_download_media).setVisible(!status.getAttachments().isEmpty());
|
|
||||||
} else {
|
|
||||||
popup.inflate(R.menu.status_more_for_user);
|
popup.inflate(R.menu.status_more_for_user);
|
||||||
Menu menu = popup.getMenu();
|
Menu menu = popup.getMenu();
|
||||||
switch (status.getVisibility()) {
|
switch (status.getVisibility()) {
|
||||||
|
@ -208,6 +205,10 @@ public abstract class SFragment extends BaseFragment implements Injectable {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
popup.inflate(R.menu.status_more);
|
||||||
|
Menu menu = popup.getMenu();
|
||||||
|
menu.findItem(R.id.status_download_media).setVisible(!status.getAttachments().isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu menu = popup.getMenu();
|
Menu menu = popup.getMenu();
|
||||||
|
@ -231,6 +232,15 @@ public abstract class SFragment extends BaseFragment implements Injectable {
|
||||||
}
|
}
|
||||||
openAsItem.setTitle(openAsTitle);
|
openAsItem.setTitle(openAsTitle);
|
||||||
|
|
||||||
|
MenuItem muteConversationItem = menu.findItem(R.id.status_mute_conversation);
|
||||||
|
boolean mutable = statusIsByCurrentUser || accountIsInMentions(activeAccount, status.getMentions());
|
||||||
|
muteConversationItem.setVisible(mutable);
|
||||||
|
if (mutable) {
|
||||||
|
muteConversationItem.setTitle((status.getMuted() == null || !status.getMuted()) ?
|
||||||
|
R.string.action_mute_conversation :
|
||||||
|
R.string.action_unmute_conversation);
|
||||||
|
}
|
||||||
|
|
||||||
popup.setOnMenuItemClickListener(item -> {
|
popup.setOnMenuItemClickListener(item -> {
|
||||||
switch (item.getItemId()) {
|
switch (item.getItemId()) {
|
||||||
case R.id.status_share_content: {
|
case R.id.status_share_content: {
|
||||||
|
@ -305,12 +315,35 @@ public abstract class SFragment extends BaseFragment implements Injectable {
|
||||||
timelineCases.pin(status, !status.isPinned());
|
timelineCases.pin(status, !status.isPinned());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case R.id.status_mute_conversation: {
|
||||||
|
timelineCases.muteConversation(status, status.getMuted() == null || !status.getMuted())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY)))
|
||||||
|
.subscribe();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
popup.show();
|
popup.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean accountIsInMentions(AccountEntity account, Status.Mention[] mentions) {
|
||||||
|
if (account == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Status.Mention mention : mentions) {
|
||||||
|
if (account.getUsername().equals(mention.getUsername())) {
|
||||||
|
Uri uri = Uri.parse(mention.getUrl());
|
||||||
|
if (uri != null && account.getDomain().equals(uri.getHost())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
protected void viewMedia(int urlIndex, Status status, @Nullable View view) {
|
protected void viewMedia(int urlIndex, Status status, @Nullable View view) {
|
||||||
final Status actionable = status.getActionableStatus();
|
final Status actionable = status.getActionableStatus();
|
||||||
final Attachment active = actionable.getAttachments().get(urlIndex);
|
final Attachment active = actionable.getAttachments().get(urlIndex);
|
||||||
|
|
|
@ -53,6 +53,7 @@ import com.keylesspalace.tusky.appstore.BookmarkEvent;
|
||||||
import com.keylesspalace.tusky.appstore.DomainMuteEvent;
|
import com.keylesspalace.tusky.appstore.DomainMuteEvent;
|
||||||
import com.keylesspalace.tusky.appstore.EventHub;
|
import com.keylesspalace.tusky.appstore.EventHub;
|
||||||
import com.keylesspalace.tusky.appstore.FavoriteEvent;
|
import com.keylesspalace.tusky.appstore.FavoriteEvent;
|
||||||
|
import com.keylesspalace.tusky.appstore.MuteConversationEvent;
|
||||||
import com.keylesspalace.tusky.appstore.MuteEvent;
|
import com.keylesspalace.tusky.appstore.MuteEvent;
|
||||||
import com.keylesspalace.tusky.appstore.PreferenceChangedEvent;
|
import com.keylesspalace.tusky.appstore.PreferenceChangedEvent;
|
||||||
import com.keylesspalace.tusky.appstore.ReblogEvent;
|
import com.keylesspalace.tusky.appstore.ReblogEvent;
|
||||||
|
@ -503,6 +504,9 @@ public class TimelineFragment extends SFragment implements
|
||||||
} else if (event instanceof BookmarkEvent) {
|
} else if (event instanceof BookmarkEvent) {
|
||||||
BookmarkEvent bookmarkEvent = (BookmarkEvent) event;
|
BookmarkEvent bookmarkEvent = (BookmarkEvent) event;
|
||||||
handleBookmarkEvent(bookmarkEvent);
|
handleBookmarkEvent(bookmarkEvent);
|
||||||
|
} else if (event instanceof MuteConversationEvent) {
|
||||||
|
MuteConversationEvent muteEvent = (MuteConversationEvent) event;
|
||||||
|
handleMuteConversationEvent(muteEvent);
|
||||||
} else if (event instanceof UnfollowEvent) {
|
} else if (event instanceof UnfollowEvent) {
|
||||||
if (kind == Kind.HOME) {
|
if (kind == Kind.HOME) {
|
||||||
String id = ((UnfollowEvent) event).getAccountId();
|
String id = ((UnfollowEvent) event).getAccountId();
|
||||||
|
@ -1313,6 +1317,10 @@ public class TimelineFragment extends SFragment implements
|
||||||
setBookmarkForStatus(pos, status, bookmarkEvent.getBookmark());
|
setBookmarkForStatus(pos, status, bookmarkEvent.getBookmark());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleMuteConversationEvent(@NonNull MuteConversationEvent event) {
|
||||||
|
fullyRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
private void handleStatusComposeEvent(@NonNull Status status) {
|
private void handleStatusComposeEvent(@NonNull Status status) {
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case HOME:
|
case HOME:
|
||||||
|
|
|
@ -200,6 +200,16 @@ interface MastodonApi {
|
||||||
@Path("id") statusId: String
|
@Path("id") statusId: String
|
||||||
): Single<Status>
|
): Single<Status>
|
||||||
|
|
||||||
|
@POST("api/v1/statuses/{id}/mute")
|
||||||
|
fun muteConversation(
|
||||||
|
@Path("id") statusId: String
|
||||||
|
): Single<Status>
|
||||||
|
|
||||||
|
@POST("api/v1/statuses/{id}/unmute")
|
||||||
|
fun unmuteConversation(
|
||||||
|
@Path("id") statusId: String
|
||||||
|
): Single<Status>
|
||||||
|
|
||||||
@GET("api/v1/scheduled_statuses")
|
@GET("api/v1/scheduled_statuses")
|
||||||
fun scheduledStatuses(
|
fun scheduledStatuses(
|
||||||
@Query("limit") limit: Int? = null,
|
@Query("limit") limit: Int? = null,
|
||||||
|
|
|
@ -41,7 +41,7 @@ interface TimelineCases {
|
||||||
fun delete(id: String): Single<DeletedStatus>
|
fun delete(id: String): Single<DeletedStatus>
|
||||||
fun pin(status: Status, pin: Boolean)
|
fun pin(status: Status, pin: Boolean)
|
||||||
fun voteInPoll(status: Status, choices: List<Int>): Single<Poll>
|
fun voteInPoll(status: Status, choices: List<Int>): Single<Poll>
|
||||||
|
fun muteConversation(status: Status, mute: Boolean): Single<Status>
|
||||||
}
|
}
|
||||||
|
|
||||||
class TimelineCasesImpl(
|
class TimelineCasesImpl(
|
||||||
|
@ -94,6 +94,19 @@ class TimelineCasesImpl(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun muteConversation(status: Status, mute: Boolean): Single<Status> {
|
||||||
|
val id = status.actionableId
|
||||||
|
|
||||||
|
val call = if (mute) {
|
||||||
|
mastodonApi.muteConversation(id)
|
||||||
|
} else {
|
||||||
|
mastodonApi.unmuteConversation(id)
|
||||||
|
}
|
||||||
|
return call.doAfterSuccess {
|
||||||
|
eventHub.dispatch(MuteConversationEvent(status.id, mute))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun mute(id: String) {
|
override fun mute(id: String) {
|
||||||
val call = mastodonApi.muteAccount(id)
|
val call = mastodonApi.muteAccount(id)
|
||||||
call.enqueue(object : Callback<Relationship> {
|
call.enqueue(object : Callback<Relationship> {
|
||||||
|
|
|
@ -229,6 +229,7 @@ class TimelineRepositoryImpl(
|
||||||
mentions = mentions,
|
mentions = mentions,
|
||||||
application = application,
|
application = application,
|
||||||
pinned = false,
|
pinned = false,
|
||||||
|
muted = status.muted,
|
||||||
poll = poll,
|
poll = poll,
|
||||||
card = null
|
card = null
|
||||||
)
|
)
|
||||||
|
@ -256,6 +257,7 @@ class TimelineRepositoryImpl(
|
||||||
mentions = arrayOf(),
|
mentions = arrayOf(),
|
||||||
application = null,
|
application = null,
|
||||||
pinned = false,
|
pinned = false,
|
||||||
|
muted = status.muted,
|
||||||
poll = null,
|
poll = null,
|
||||||
card = null
|
card = null
|
||||||
)
|
)
|
||||||
|
@ -282,6 +284,7 @@ class TimelineRepositoryImpl(
|
||||||
mentions = mentions,
|
mentions = mentions,
|
||||||
application = application,
|
application = application,
|
||||||
pinned = false,
|
pinned = false,
|
||||||
|
muted = status.muted,
|
||||||
poll = poll,
|
poll = poll,
|
||||||
card = null
|
card = null
|
||||||
)
|
)
|
||||||
|
@ -353,7 +356,8 @@ fun Placeholder.toEntity(timelineUserId: Long): TimelineStatusEntity {
|
||||||
application = null,
|
application = null,
|
||||||
reblogServerId = null,
|
reblogServerId = null,
|
||||||
reblogAccountId = null,
|
reblogAccountId = null,
|
||||||
poll = null
|
poll = null,
|
||||||
|
muted = false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -384,7 +388,8 @@ fun Status.toEntity(timelineUserId: Long,
|
||||||
application = actionable.let(gson::toJson),
|
application = actionable.let(gson::toJson),
|
||||||
reblogServerId = reblog?.id,
|
reblogServerId = reblog?.id,
|
||||||
reblogAccountId = reblog?.let { this.account.id },
|
reblogAccountId = reblog?.let { this.account.id },
|
||||||
poll = actionable.poll.let(gson::toJson)
|
poll = actionable.poll.let(gson::toJson),
|
||||||
|
muted = actionable.muted
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,7 @@ public abstract class StatusViewData {
|
||||||
final boolean reblogged;
|
final boolean reblogged;
|
||||||
final boolean favourited;
|
final boolean favourited;
|
||||||
final boolean bookmarked;
|
final boolean bookmarked;
|
||||||
|
private final boolean muted;
|
||||||
@Nullable
|
@Nullable
|
||||||
private final String spoilerText;
|
private final String spoilerText;
|
||||||
private final Status.Visibility visibility;
|
private final Status.Visibility visibility;
|
||||||
|
@ -92,7 +93,7 @@ public abstract class StatusViewData {
|
||||||
private final PollViewData poll;
|
private final PollViewData poll;
|
||||||
private final boolean isBot;
|
private final boolean isBot;
|
||||||
|
|
||||||
public Concrete(String id, Spanned content, boolean reblogged, boolean favourited, boolean bookmarked,
|
public Concrete(String id, Spanned content, boolean reblogged, boolean favourited, boolean bookmarked, boolean muted,
|
||||||
@Nullable String spoilerText, Status.Visibility visibility, List<Attachment> attachments,
|
@Nullable String spoilerText, Status.Visibility visibility, List<Attachment> attachments,
|
||||||
@Nullable String rebloggedByUsername, @Nullable String rebloggedAvatar, boolean sensitive, boolean isExpanded,
|
@Nullable String rebloggedByUsername, @Nullable String rebloggedAvatar, boolean sensitive, boolean isExpanded,
|
||||||
boolean isShowingContent, String userFullName, String nickname, String avatar,
|
boolean isShowingContent, String userFullName, String nickname, String avatar,
|
||||||
|
@ -115,6 +116,7 @@ public abstract class StatusViewData {
|
||||||
this.reblogged = reblogged;
|
this.reblogged = reblogged;
|
||||||
this.favourited = favourited;
|
this.favourited = favourited;
|
||||||
this.bookmarked = bookmarked;
|
this.bookmarked = bookmarked;
|
||||||
|
this.muted = muted;
|
||||||
this.visibility = visibility;
|
this.visibility = visibility;
|
||||||
this.attachments = attachments;
|
this.attachments = attachments;
|
||||||
this.rebloggedByUsername = rebloggedByUsername;
|
this.rebloggedByUsername = rebloggedByUsername;
|
||||||
|
@ -161,6 +163,10 @@ public abstract class StatusViewData {
|
||||||
return bookmarked;
|
return bookmarked;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isMuted() {
|
||||||
|
return muted;
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public String getSpoilerText() {
|
public String getSpoilerText() {
|
||||||
return spoilerText;
|
return spoilerText;
|
||||||
|
@ -401,6 +407,7 @@ public abstract class StatusViewData {
|
||||||
private boolean reblogged;
|
private boolean reblogged;
|
||||||
private boolean favourited;
|
private boolean favourited;
|
||||||
private boolean bookmarked;
|
private boolean bookmarked;
|
||||||
|
private boolean muted;
|
||||||
private String spoilerText;
|
private String spoilerText;
|
||||||
private Status.Visibility visibility;
|
private Status.Visibility visibility;
|
||||||
private List<Attachment> attachments;
|
private List<Attachment> attachments;
|
||||||
|
@ -437,6 +444,7 @@ public abstract class StatusViewData {
|
||||||
reblogged = viewData.reblogged;
|
reblogged = viewData.reblogged;
|
||||||
favourited = viewData.favourited;
|
favourited = viewData.favourited;
|
||||||
bookmarked = viewData.bookmarked;
|
bookmarked = viewData.bookmarked;
|
||||||
|
muted = viewData.muted;
|
||||||
spoilerText = viewData.spoilerText;
|
spoilerText = viewData.spoilerText;
|
||||||
visibility = viewData.visibility;
|
visibility = viewData.visibility;
|
||||||
attachments = viewData.attachments == null ? null : new ArrayList<>(viewData.attachments);
|
attachments = viewData.attachments == null ? null : new ArrayList<>(viewData.attachments);
|
||||||
|
@ -490,6 +498,11 @@ public abstract class StatusViewData {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder setMuted(boolean muted) {
|
||||||
|
this.muted = muted;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public Builder setSpoilerText(String spoilerText) {
|
public Builder setSpoilerText(String spoilerText) {
|
||||||
this.spoilerText = spoilerText;
|
this.spoilerText = spoilerText;
|
||||||
return this;
|
return this;
|
||||||
|
@ -639,7 +652,7 @@ public abstract class StatusViewData {
|
||||||
if (this.accountEmojis == null) accountEmojis = Collections.emptyList();
|
if (this.accountEmojis == null) accountEmojis = Collections.emptyList();
|
||||||
if (this.createdAt == null) createdAt = new Date();
|
if (this.createdAt == null) createdAt = new Date();
|
||||||
|
|
||||||
return new StatusViewData.Concrete(id, content, reblogged, favourited, bookmarked, spoilerText,
|
return new StatusViewData.Concrete(id, content, reblogged, favourited, bookmarked, muted, spoilerText,
|
||||||
visibility, attachments, rebloggedByUsername, rebloggedAvatar, isSensitive, isExpanded,
|
visibility, attachments, rebloggedByUsername, rebloggedAvatar, isSensitive, isExpanded,
|
||||||
isShowingContent, userFullName, nickname, avatar, createdAt, reblogsCount,
|
isShowingContent, userFullName, nickname, avatar, createdAt, reblogsCount,
|
||||||
favouritesCount, inReplyToId, mentions, senderId, rebloggingEnabled, application,
|
favouritesCount, inReplyToId, mentions, senderId, rebloggingEnabled, application,
|
||||||
|
|
|
@ -21,6 +21,9 @@
|
||||||
<item
|
<item
|
||||||
android:id="@+id/status_download_media"
|
android:id="@+id/status_download_media"
|
||||||
android:title="@string/download_media" />
|
android:title="@string/download_media" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/status_mute_conversation"
|
||||||
|
android:title="@string/action_mute_conversation" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/status_mute"
|
android:id="@+id/status_mute"
|
||||||
android:title="@string/action_mute" />
|
android:title="@string/action_mute" />
|
||||||
|
|
|
@ -26,6 +26,9 @@
|
||||||
android:id="@+id/status_unreblog_private"
|
android:id="@+id/status_unreblog_private"
|
||||||
android:title="@string/unreblog_private"
|
android:title="@string/unreblog_private"
|
||||||
android:visible="false" />
|
android:visible="false" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/status_mute_conversation"
|
||||||
|
android:title="@string/action_mute_conversation" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/status_delete"
|
android:id="@+id/status_delete"
|
||||||
android:title="@string/action_delete" />
|
android:title="@string/action_delete" />
|
||||||
|
|
|
@ -109,6 +109,8 @@
|
||||||
<string name="action_mute">Mute</string>
|
<string name="action_mute">Mute</string>
|
||||||
<string name="action_unmute">Unmute</string>
|
<string name="action_unmute">Unmute</string>
|
||||||
<string name="action_mute_domain">Mute %s</string>
|
<string name="action_mute_domain">Mute %s</string>
|
||||||
|
<string name="action_mute_conversation">Mute conversation</string>
|
||||||
|
<string name="action_unmute_conversation">Unmute conversation</string>
|
||||||
<string name="action_mention">Mention</string>
|
<string name="action_mention">Mention</string>
|
||||||
<string name="action_hide_media">Hide media</string>
|
<string name="action_hide_media">Hide media</string>
|
||||||
<string name="action_open_drawer">Open drawer</string>
|
<string name="action_open_drawer">Open drawer</string>
|
||||||
|
|
|
@ -88,6 +88,7 @@ class BottomSheetActivityTest {
|
||||||
arrayOf(),
|
arrayOf(),
|
||||||
null,
|
null,
|
||||||
pinned = false,
|
pinned = false,
|
||||||
|
muted = false,
|
||||||
poll = null,
|
poll = null,
|
||||||
card = null
|
card = null
|
||||||
)
|
)
|
||||||
|
|
|
@ -214,6 +214,7 @@ class FilterTest {
|
||||||
mentions = emptyArray(),
|
mentions = emptyArray(),
|
||||||
application = null,
|
application = null,
|
||||||
pinned = false,
|
pinned = false,
|
||||||
|
muted = false,
|
||||||
poll = if (pollOptions != null) {
|
poll = if (pollOptions != null) {
|
||||||
Poll(
|
Poll(
|
||||||
id = "1234",
|
id = "1234",
|
||||||
|
|
|
@ -314,6 +314,7 @@ class TimelineRepositoryTest {
|
||||||
inReplyToAccountId = null,
|
inReplyToAccountId = null,
|
||||||
inReplyToId = null,
|
inReplyToId = null,
|
||||||
pinned = false,
|
pinned = false,
|
||||||
|
muted = false,
|
||||||
reblog = null,
|
reblog = null,
|
||||||
url = "http://example.com/statuses/$id",
|
url = "http://example.com/statuses/$id",
|
||||||
poll = null,
|
poll = null,
|
||||||
|
|
Loading…
Reference in a new issue