Add notifications for follow requests (#1729)
* Add notifications for follow requests Issue #1719 * Revert item_follow_request layout, create new layout for follow request notifications * Migrate follow request interaction from notification to observable pattern * Filter follow request notifications by default * Add missing cases for system notification generation * Format code
This commit is contained in:
parent
43162789c1
commit
4a4dd4f30f
16 changed files with 1003 additions and 104 deletions
735
app/schemas/com.keylesspalace.tusky.db.AppDatabase/22.json
Normal file
735
app/schemas/com.keylesspalace.tusky.db.AppDatabase/22.json
Normal file
|
@ -0,0 +1,735 @@
|
|||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 22,
|
||||
"identityHash": "eaa3c4d012fe743948343983fe1ae493",
|
||||
"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, 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
|
||||
}
|
||||
],
|
||||
"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, 'eaa3c4d012fe743948343983fe1ae493')"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package com.keylesspalace.tusky.adapter
|
||||
|
||||
import android.view.View
|
||||
import androidx.core.text.BidiFormatter
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.entity.Account
|
||||
import com.keylesspalace.tusky.interfaces.AccountActionListener
|
||||
import com.keylesspalace.tusky.util.CustomEmojiHelper
|
||||
import com.keylesspalace.tusky.util.loadAvatar
|
||||
import com.keylesspalace.tusky.util.visible
|
||||
import kotlinx.android.synthetic.main.item_follow_request_notification.view.*
|
||||
|
||||
internal class FollowRequestViewHolder(itemView: View, private val showHeader: Boolean) : RecyclerView.ViewHolder(itemView) {
|
||||
private var id: String? = null
|
||||
private val animateAvatar: Boolean = PreferenceManager.getDefaultSharedPreferences(itemView.context)
|
||||
.getBoolean("animateGifAvatars", false)
|
||||
|
||||
fun setupWithAccount(account: Account, formatter: BidiFormatter?) {
|
||||
id = account.id
|
||||
val wrappedName = formatter?.unicodeWrap(account.name) ?: account.name
|
||||
val emojifiedName: CharSequence = CustomEmojiHelper.emojifyString(wrappedName, account.emojis, itemView)
|
||||
itemView.displayNameTextView.text = emojifiedName
|
||||
if (showHeader) {
|
||||
itemView.notificationTextView.text = itemView.context.getString(R.string.notification_follow_request_format, emojifiedName)
|
||||
}
|
||||
itemView.notificationTextView.visible(showHeader)
|
||||
val format = itemView.context.getString(R.string.status_username_format)
|
||||
val formattedUsername = String.format(format, account.username)
|
||||
itemView.usernameTextView.text = formattedUsername
|
||||
val avatarRadius = itemView.avatar.context.resources.getDimensionPixelSize(R.dimen.avatar_radius_48dp)
|
||||
loadAvatar(account.avatar, itemView.avatar, avatarRadius, animateAvatar)
|
||||
}
|
||||
|
||||
fun setupActionListener(listener: AccountActionListener) {
|
||||
itemView.acceptButton.setOnClickListener {
|
||||
val position = adapterPosition
|
||||
if (position != RecyclerView.NO_POSITION) {
|
||||
listener.onRespondToFollowRequest(true, id, position)
|
||||
}
|
||||
}
|
||||
itemView.rejectButton.setOnClickListener {
|
||||
val position = adapterPosition
|
||||
if (position != RecyclerView.NO_POSITION) {
|
||||
listener.onRespondToFollowRequest(false, id, position)
|
||||
}
|
||||
}
|
||||
itemView.avatar.setOnClickListener { listener.onViewAccount(id) }
|
||||
}
|
||||
}
|
|
@ -18,19 +18,12 @@ package com.keylesspalace.tusky.adapter;
|
|||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.keylesspalace.tusky.R;
|
||||
import com.keylesspalace.tusky.entity.Account;
|
||||
import com.keylesspalace.tusky.interfaces.AccountActionListener;
|
||||
import com.keylesspalace.tusky.util.CustomEmojiHelper;
|
||||
import com.keylesspalace.tusky.util.ImageLoadingHelper;
|
||||
|
||||
public class FollowRequestsAdapter extends AccountAdapter {
|
||||
|
||||
|
@ -46,7 +39,7 @@ public class FollowRequestsAdapter extends AccountAdapter {
|
|||
case VIEW_TYPE_ACCOUNT: {
|
||||
View view = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.item_follow_request, parent, false);
|
||||
return new FollowRequestViewHolder(view);
|
||||
return new FollowRequestViewHolder(view, false);
|
||||
}
|
||||
case VIEW_TYPE_FOOTER: {
|
||||
View view = LayoutInflater.from(parent.getContext())
|
||||
|
@ -60,57 +53,8 @@ public class FollowRequestsAdapter extends AccountAdapter {
|
|||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
|
||||
if (getItemViewType(position) == VIEW_TYPE_ACCOUNT) {
|
||||
FollowRequestViewHolder holder = (FollowRequestViewHolder) viewHolder;
|
||||
holder.setupWithAccount(accountList.get(position));
|
||||
holder.setupWithAccount(accountList.get(position), null);
|
||||
holder.setupActionListener(accountActionListener);
|
||||
}
|
||||
}
|
||||
|
||||
static class FollowRequestViewHolder extends RecyclerView.ViewHolder {
|
||||
private ImageView avatar;
|
||||
private TextView username;
|
||||
private TextView displayName;
|
||||
private ImageButton accept;
|
||||
private ImageButton reject;
|
||||
private String id;
|
||||
private boolean animateAvatar;
|
||||
|
||||
FollowRequestViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
avatar = itemView.findViewById(R.id.avatar);
|
||||
username = itemView.findViewById(R.id.usernameTextView);
|
||||
displayName = itemView.findViewById(R.id.displayNameTextView);
|
||||
accept = itemView.findViewById(R.id.acceptButton);
|
||||
reject = itemView.findViewById(R.id.rejectButton);
|
||||
animateAvatar = PreferenceManager.getDefaultSharedPreferences(itemView.getContext())
|
||||
.getBoolean("animateGifAvatars", false);
|
||||
}
|
||||
|
||||
void setupWithAccount(Account account) {
|
||||
id = account.getId();
|
||||
CharSequence emojifiedName = CustomEmojiHelper.emojifyString(account.getName(), account.getEmojis(), displayName);
|
||||
displayName.setText(emojifiedName);
|
||||
String format = username.getContext().getString(R.string.status_username_format);
|
||||
String formattedUsername = String.format(format, account.getUsername());
|
||||
username.setText(formattedUsername);
|
||||
int avatarRadius = avatar.getContext().getResources()
|
||||
.getDimensionPixelSize(R.dimen.avatar_radius_48dp);
|
||||
ImageLoadingHelper.loadAvatar(account.getAvatar(), avatar, avatarRadius, animateAvatar);
|
||||
}
|
||||
|
||||
void setupActionListener(final AccountActionListener listener) {
|
||||
accept.setOnClickListener(v -> {
|
||||
int position = getAdapterPosition();
|
||||
if (position != RecyclerView.NO_POSITION) {
|
||||
listener.onRespondToFollowRequest(true, id, position);
|
||||
}
|
||||
});
|
||||
reject.setOnClickListener(v -> {
|
||||
int position = getAdapterPosition();
|
||||
if (position != RecyclerView.NO_POSITION) {
|
||||
listener.onRespondToFollowRequest(false, id, position);
|
||||
}
|
||||
});
|
||||
avatar.setOnClickListener(v -> listener.onViewAccount(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ import com.keylesspalace.tusky.R;
|
|||
import com.keylesspalace.tusky.entity.Account;
|
||||
import com.keylesspalace.tusky.entity.Emoji;
|
||||
import com.keylesspalace.tusky.entity.Notification;
|
||||
import com.keylesspalace.tusky.interfaces.AccountActionListener;
|
||||
import com.keylesspalace.tusky.interfaces.LinkListener;
|
||||
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
||||
import com.keylesspalace.tusky.util.CardViewMode;
|
||||
|
@ -72,8 +73,9 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
|||
private static final int VIEW_TYPE_STATUS = 0;
|
||||
private static final int VIEW_TYPE_STATUS_NOTIFICATION = 1;
|
||||
private static final int VIEW_TYPE_FOLLOW = 2;
|
||||
private static final int VIEW_TYPE_PLACEHOLDER = 3;
|
||||
private static final int VIEW_TYPE_UNKNOWN = 4;
|
||||
private static final int VIEW_TYPE_FOLLOW_REQUEST = 3;
|
||||
private static final int VIEW_TYPE_PLACEHOLDER = 4;
|
||||
private static final int VIEW_TYPE_UNKNOWN = 5;
|
||||
|
||||
private static final InputFilter[] COLLAPSE_INPUT_FILTER = new InputFilter[]{SmartLengthInputFilter.INSTANCE};
|
||||
private static final InputFilter[] NO_INPUT_FILTER = new InputFilter[0];
|
||||
|
@ -82,6 +84,7 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
|||
private StatusDisplayOptions statusDisplayOptions;
|
||||
private StatusActionListener statusListener;
|
||||
private NotificationActionListener notificationActionListener;
|
||||
private AccountActionListener accountActionListener;
|
||||
private BidiFormatter bidiFormatter;
|
||||
private AdapterDataSource<NotificationViewData> dataSource;
|
||||
|
||||
|
@ -89,13 +92,15 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
|||
AdapterDataSource<NotificationViewData> dataSource,
|
||||
StatusDisplayOptions statusDisplayOptions,
|
||||
StatusActionListener statusListener,
|
||||
NotificationActionListener notificationActionListener) {
|
||||
NotificationActionListener notificationActionListener,
|
||||
AccountActionListener accountActionListener) {
|
||||
|
||||
this.accountId = accountId;
|
||||
this.dataSource = dataSource;
|
||||
this.statusDisplayOptions = statusDisplayOptions;
|
||||
this.statusListener = statusListener;
|
||||
this.notificationActionListener = notificationActionListener;
|
||||
this.accountActionListener = accountActionListener;
|
||||
bidiFormatter = BidiFormatter.getInstance();
|
||||
}
|
||||
|
||||
|
@ -119,6 +124,11 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
|||
.inflate(R.layout.item_follow, parent, false);
|
||||
return new FollowViewHolder(view, statusDisplayOptions);
|
||||
}
|
||||
case VIEW_TYPE_FOLLOW_REQUEST: {
|
||||
View view = inflater
|
||||
.inflate(R.layout.item_follow_request_notification, parent, false);
|
||||
return new FollowRequestViewHolder(view, true);
|
||||
}
|
||||
case VIEW_TYPE_PLACEHOLDER: {
|
||||
View view = inflater
|
||||
.inflate(R.layout.item_status_placeholder, parent, false);
|
||||
|
@ -215,6 +225,13 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case VIEW_TYPE_FOLLOW_REQUEST: {
|
||||
if (payloadForHolder == null) {
|
||||
FollowRequestViewHolder holder = (FollowRequestViewHolder) viewHolder;
|
||||
holder.setupWithAccount(concreteNotificaton.getAccount(), bidiFormatter);
|
||||
holder.setupActionListener(accountActionListener);
|
||||
}
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
@ -258,6 +275,9 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
|||
case FOLLOW: {
|
||||
return VIEW_TYPE_FOLLOW;
|
||||
}
|
||||
case FOLLOW_REQUEST: {
|
||||
return VIEW_TYPE_FOLLOW_REQUEST;
|
||||
}
|
||||
default: {
|
||||
return VIEW_TYPE_UNKNOWN;
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ data class AccountEntity(@field:PrimaryKey(autoGenerate = true) var id: Long,
|
|||
var notificationsEnabled: Boolean = true,
|
||||
var notificationsMentioned: Boolean = true,
|
||||
var notificationsFollowed: Boolean = true,
|
||||
var notificationsFollowRequested: Boolean = false,
|
||||
var notificationsReblogged: Boolean = true,
|
||||
var notificationsFavorited: Boolean = true,
|
||||
var notificationsPolls: Boolean = true,
|
||||
|
@ -54,7 +55,7 @@ data class AccountEntity(@field:PrimaryKey(autoGenerate = true) var id: Long,
|
|||
var activeNotifications: String = "[]",
|
||||
var emojis: List<Emoji> = emptyList(),
|
||||
var tabPreferences: List<TabData> = defaultTabs(),
|
||||
var notificationsFilter: String = "[]") {
|
||||
var notificationsFilter: String = "[\"follow_request\"]") {
|
||||
|
||||
val identifier: String
|
||||
get() = "$domain:$accountId"
|
||||
|
|
|
@ -30,7 +30,7 @@ import androidx.annotation.NonNull;
|
|||
|
||||
@Database(entities = {TootEntity.class, AccountEntity.class, InstanceEntity.class, TimelineStatusEntity.class,
|
||||
TimelineAccountEntity.class, ConversationEntity.class
|
||||
}, version = 21)
|
||||
}, version = 22)
|
||||
public abstract class AppDatabase extends RoomDatabase {
|
||||
|
||||
public abstract TootDao tootDao();
|
||||
|
@ -326,4 +326,11 @@ public abstract class AppDatabase extends RoomDatabase {
|
|||
}
|
||||
};
|
||||
|
||||
public static final Migration MIGRATION_21_22 = new Migration(21, 22) {
|
||||
@Override
|
||||
public void migrate(@NonNull SupportSQLiteDatabase database) {
|
||||
database.execSQL("ALTER TABLE `AccountEntity` ADD COLUMN `notificationsFollowRequested` INTEGER NOT NULL DEFAULT 0");
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -79,7 +79,7 @@ class AppModule {
|
|||
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_16_17, AppDatabase.MIGRATION_17_18, AppDatabase.MIGRATION_18_19,
|
||||
AppDatabase.MIGRATION_19_20, AppDatabase.MIGRATION_20_21)
|
||||
AppDatabase.MIGRATION_19_20, AppDatabase.MIGRATION_20_21, AppDatabase.MIGRATION_21_22)
|
||||
.build()
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ data class Notification(
|
|||
REBLOG("reblog"),
|
||||
FAVOURITE("favourite"),
|
||||
FOLLOW("follow"),
|
||||
FOLLOW_REQUEST("follow_request"),
|
||||
POLL("poll");
|
||||
|
||||
companion object {
|
||||
|
@ -43,7 +44,7 @@ data class Notification(
|
|||
}
|
||||
return UNKNOWN
|
||||
}
|
||||
val asList = listOf(MENTION, REBLOG, FAVOURITE, FOLLOW, POLL)
|
||||
val asList = listOf(MENTION, REBLOG, FAVOURITE, FOLLOW, FOLLOW_REQUEST, POLL)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
|
|
|
@ -65,7 +65,9 @@ import com.keylesspalace.tusky.db.AccountManager;
|
|||
import com.keylesspalace.tusky.di.Injectable;
|
||||
import com.keylesspalace.tusky.entity.Notification;
|
||||
import com.keylesspalace.tusky.entity.Poll;
|
||||
import com.keylesspalace.tusky.entity.Relationship;
|
||||
import com.keylesspalace.tusky.entity.Status;
|
||||
import com.keylesspalace.tusky.interfaces.AccountActionListener;
|
||||
import com.keylesspalace.tusky.interfaces.ActionButtonActivity;
|
||||
import com.keylesspalace.tusky.interfaces.ReselectableFragment;
|
||||
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
||||
|
@ -98,6 +100,7 @@ import javax.inject.Inject;
|
|||
|
||||
import at.connyduck.sparkbutton.helpers.Utils;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import kotlin.Unit;
|
||||
import kotlin.collections.CollectionsKt;
|
||||
|
@ -115,6 +118,7 @@ public class NotificationsFragment extends SFragment implements
|
|||
SwipeRefreshLayout.OnRefreshListener,
|
||||
StatusActionListener,
|
||||
NotificationsAdapter.NotificationActionListener,
|
||||
AccountActionListener,
|
||||
Injectable, ReselectableFragment {
|
||||
private static final String TAG = "NotificationF"; // logging tag
|
||||
|
||||
|
@ -251,7 +255,7 @@ public class NotificationsFragment extends SFragment implements
|
|||
);
|
||||
|
||||
adapter = new NotificationsAdapter(accountManager.getActiveAccount().getAccountId(),
|
||||
dataSource, statusDisplayOptions, this, this);
|
||||
dataSource, statusDisplayOptions, this, this, this);
|
||||
alwaysShowSensitiveMedia = accountManager.getActiveAccount().getAlwaysShowSensitiveMedia();
|
||||
alwaysOpenSpoiler = accountManager.getActiveAccount().getAlwaysOpenSpoiler();
|
||||
recyclerView.setAdapter(adapter);
|
||||
|
@ -765,6 +769,8 @@ public class NotificationsFragment extends SFragment implements
|
|||
return getString(R.string.notification_boost_name);
|
||||
case FOLLOW:
|
||||
return getString(R.string.notification_follow_name);
|
||||
case FOLLOW_REQUEST:
|
||||
return getString(R.string.notification_follow_request_name);
|
||||
case POLL:
|
||||
return getString(R.string.notification_poll_name);
|
||||
default:
|
||||
|
@ -817,6 +823,29 @@ public class NotificationsFragment extends SFragment implements
|
|||
super.viewAccount(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMute(boolean mute, String id, int position) {
|
||||
// No muting from notifications yet
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlock(boolean block, String id, int position) {
|
||||
// No blocking from notifications yet
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRespondToFollowRequest(boolean accept, String id, int position) {
|
||||
Single<Relationship> request = accept ?
|
||||
mastodonApi.authorizeFollowRequestObservable(id) :
|
||||
mastodonApi.rejectFollowRequestObservable(id);
|
||||
request.observeOn(AndroidSchedulers.mainThread())
|
||||
.as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY)))
|
||||
.subscribe(
|
||||
(relationship) -> fullyRefreshWithProgressBar(true),
|
||||
(error) -> Log.e(TAG, String.format("Failed to %s account id %s", accept ? "accept" : "reject", id))
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewStatusForNotificationId(String notificationId) {
|
||||
for (Either<Placeholder, Notification> either : notifications) {
|
||||
|
|
|
@ -41,42 +41,23 @@ class NotificationPreferencesFragment : PreferenceFragmentCompat(), Preference.O
|
|||
val activeAccount = accountManager.activeAccount
|
||||
|
||||
if (activeAccount != null) {
|
||||
|
||||
val notificationPref = requirePreference("notificationsEnabled") as SwitchPreferenceCompat
|
||||
notificationPref.isChecked = activeAccount.notificationsEnabled
|
||||
notificationPref.onPreferenceChangeListener = this
|
||||
|
||||
val mentionedPref = requirePreference("notificationFilterMentions") as SwitchPreferenceCompat
|
||||
mentionedPref.isChecked = activeAccount.notificationsMentioned
|
||||
mentionedPref.onPreferenceChangeListener = this
|
||||
|
||||
val followedPref = requirePreference("notificationFilterFollows") as SwitchPreferenceCompat
|
||||
followedPref.isChecked = activeAccount.notificationsFollowed
|
||||
followedPref.onPreferenceChangeListener = this
|
||||
|
||||
val boostedPref = requirePreference("notificationFilterReblogs") as SwitchPreferenceCompat
|
||||
boostedPref.isChecked = activeAccount.notificationsReblogged
|
||||
boostedPref.onPreferenceChangeListener = this
|
||||
|
||||
val favoritedPref = requirePreference("notificationFilterFavourites") as SwitchPreferenceCompat
|
||||
favoritedPref.isChecked = activeAccount.notificationsFavorited
|
||||
favoritedPref.onPreferenceChangeListener = this
|
||||
|
||||
val pollsPref = requirePreference("notificationFilterPolls") as SwitchPreferenceCompat
|
||||
pollsPref.isChecked = activeAccount.notificationsPolls
|
||||
pollsPref.onPreferenceChangeListener = this
|
||||
|
||||
val soundPref = requirePreference("notificationAlertSound") as SwitchPreferenceCompat
|
||||
soundPref.isChecked = activeAccount.notificationSound
|
||||
soundPref.onPreferenceChangeListener = this
|
||||
|
||||
val vibrationPref = requirePreference("notificationAlertVibrate") as SwitchPreferenceCompat
|
||||
vibrationPref.isChecked = activeAccount.notificationVibration
|
||||
vibrationPref.onPreferenceChangeListener = this
|
||||
|
||||
val lightPref = requirePreference("notificationAlertLight") as SwitchPreferenceCompat
|
||||
lightPref.isChecked = activeAccount.notificationLight
|
||||
lightPref.onPreferenceChangeListener = this
|
||||
for (pair in mapOf(
|
||||
"notificationsEnabled" to activeAccount.notificationsEnabled,
|
||||
"notificationFilterMentions" to activeAccount.notificationsMentioned,
|
||||
"notificationFilterFollows" to activeAccount.notificationsFollowed,
|
||||
"notificationFilterFollowRequests" to activeAccount.notificationsFollowRequested,
|
||||
"notificationFilterReblogs" to activeAccount.notificationsReblogged,
|
||||
"notificationFilterFavourites" to activeAccount.notificationsFavorited,
|
||||
"notificationFilterPolls" to activeAccount.notificationsPolls,
|
||||
"notificationAlertSound" to activeAccount.notificationSound,
|
||||
"notificationAlertVibrate" to activeAccount.notificationVibration,
|
||||
"notificationAlertLight" to activeAccount.notificationLight
|
||||
)) {
|
||||
(requirePreference(pair.key) as SwitchPreferenceCompat).apply {
|
||||
isChecked = pair.value
|
||||
onPreferenceChangeListener = this@NotificationPreferencesFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,6 +77,7 @@ class NotificationPreferencesFragment : PreferenceFragmentCompat(), Preference.O
|
|||
}
|
||||
"notificationFilterMentions" -> activeAccount.notificationsMentioned = newValue as Boolean
|
||||
"notificationFilterFollows" -> activeAccount.notificationsFollowed = newValue as Boolean
|
||||
"notificationFilterFollowRequests" -> activeAccount.notificationsFollowRequested = newValue as Boolean
|
||||
"notificationFilterReblogs" -> activeAccount.notificationsReblogged = newValue as Boolean
|
||||
"notificationFilterFavourites" -> activeAccount.notificationsFavorited = newValue as Boolean
|
||||
"notificationFilterPolls" -> activeAccount.notificationsPolls = newValue as Boolean
|
||||
|
|
|
@ -383,6 +383,16 @@ interface MastodonApi {
|
|||
@Path("id") accountId: String
|
||||
): Call<Relationship>
|
||||
|
||||
@POST("api/v1/follow_requests/{id}/authorize")
|
||||
fun authorizeFollowRequestObservable(
|
||||
@Path("id") accountId: String
|
||||
): Single<Relationship>
|
||||
|
||||
@POST("api/v1/follow_requests/{id}/reject")
|
||||
fun rejectFollowRequestObservable(
|
||||
@Path("id") accountId: String
|
||||
): Single<Relationship>
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST("api/v1/apps")
|
||||
fun authenticateApp(
|
||||
|
|
|
@ -113,6 +113,7 @@ public class NotificationHelper {
|
|||
**/
|
||||
public static final String CHANNEL_MENTION = "CHANNEL_MENTION";
|
||||
public static final String CHANNEL_FOLLOW = "CHANNEL_FOLLOW";
|
||||
public static final String CHANNEL_FOLLOW_REQUEST = "CHANNEL_FOLLOW_REQUEST";
|
||||
public static final String CHANNEL_BOOST = "CHANNEL_BOOST";
|
||||
public static final String CHANNEL_FAVOURITE = "CHANNEL_FAVOURITE";
|
||||
public static final String CHANNEL_POLL = "CHANNEL_POLL";
|
||||
|
@ -348,6 +349,7 @@ public class NotificationHelper {
|
|||
String[] channelIds = new String[]{
|
||||
CHANNEL_MENTION + account.getIdentifier(),
|
||||
CHANNEL_FOLLOW + account.getIdentifier(),
|
||||
CHANNEL_FOLLOW_REQUEST + account.getIdentifier(),
|
||||
CHANNEL_BOOST + account.getIdentifier(),
|
||||
CHANNEL_FAVOURITE + account.getIdentifier(),
|
||||
CHANNEL_POLL + account.getIdentifier(),
|
||||
|
@ -355,6 +357,7 @@ public class NotificationHelper {
|
|||
int[] channelNames = {
|
||||
R.string.notification_mention_name,
|
||||
R.string.notification_follow_name,
|
||||
R.string.notification_follow_request_name,
|
||||
R.string.notification_boost_name,
|
||||
R.string.notification_favourite_name,
|
||||
R.string.notification_poll_name
|
||||
|
@ -362,12 +365,13 @@ public class NotificationHelper {
|
|||
int[] channelDescriptions = {
|
||||
R.string.notification_mention_descriptions,
|
||||
R.string.notification_follow_description,
|
||||
R.string.notification_follow_request_description,
|
||||
R.string.notification_boost_description,
|
||||
R.string.notification_favourite_description,
|
||||
R.string.notification_poll_description
|
||||
};
|
||||
|
||||
List<NotificationChannel> channels = new ArrayList<>(5);
|
||||
List<NotificationChannel> channels = new ArrayList<>(6);
|
||||
|
||||
NotificationChannelGroup channelGroup = new NotificationChannelGroup(account.getIdentifier(), account.getFullName());
|
||||
|
||||
|
@ -508,6 +512,8 @@ public class NotificationHelper {
|
|||
return account.getNotificationsMentioned();
|
||||
case FOLLOW:
|
||||
return account.getNotificationsFollowed();
|
||||
case FOLLOW_REQUEST:
|
||||
return account.getNotificationsFollowRequested();
|
||||
case REBLOG:
|
||||
return account.getNotificationsReblogged();
|
||||
case FAVOURITE:
|
||||
|
@ -525,6 +531,8 @@ public class NotificationHelper {
|
|||
return CHANNEL_MENTION + account.getIdentifier();
|
||||
case FOLLOW:
|
||||
return CHANNEL_FOLLOW + account.getIdentifier();
|
||||
case FOLLOW_REQUEST:
|
||||
return CHANNEL_FOLLOW_REQUEST + account.getIdentifier();
|
||||
case REBLOG:
|
||||
return CHANNEL_BOOST + account.getIdentifier();
|
||||
case FAVOURITE:
|
||||
|
@ -594,6 +602,9 @@ public class NotificationHelper {
|
|||
case FOLLOW:
|
||||
return String.format(context.getString(R.string.notification_follow_format),
|
||||
accountName);
|
||||
case FOLLOW_REQUEST:
|
||||
return String.format(context.getString(R.string.notification_follow_request_format),
|
||||
accountName);
|
||||
case FAVOURITE:
|
||||
return String.format(context.getString(R.string.notification_favourite_format),
|
||||
accountName);
|
||||
|
@ -613,6 +624,7 @@ public class NotificationHelper {
|
|||
private static String bodyForType(Notification notification, Context context) {
|
||||
switch (notification.getType()) {
|
||||
case FOLLOW:
|
||||
case FOLLOW_REQUEST:
|
||||
return "@" + notification.getAccount().getUsername();
|
||||
case MENTION:
|
||||
case FAVOURITE:
|
||||
|
|
96
app/src/main/res/layout/item_follow_request_notification.xml
Normal file
96
app/src/main/res/layout/item_follow_request_notification.xml
Normal file
|
@ -0,0 +1,96 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:paddingBottom="10dp">
|
||||
|
||||
<androidx.emoji.widget.EmojiTextView
|
||||
android:id="@+id/notificationTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:drawableStart="@drawable/ic_person_add_24dp"
|
||||
android:drawablePadding="10dp"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center_vertical"
|
||||
android:maxLines="1"
|
||||
android:paddingStart="28dp"
|
||||
android:textColor="?android:textColorTertiary"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Someone requested to follow you" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/avatar"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginTop="10dp"
|
||||
android:contentDescription="@string/action_view_profile"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/notificationTextView" />
|
||||
|
||||
<androidx.emoji.widget.EmojiTextView
|
||||
android:id="@+id/displayNameTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="14dp"
|
||||
android:layout_marginTop="6dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="?attr/status_text_large"
|
||||
android:textStyle="normal|bold"
|
||||
app:layout_constraintStart_toEndOf="@id/avatar"
|
||||
app:layout_constraintTop_toBottomOf="@id/notificationTextView"
|
||||
tools:text="Display name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/usernameTextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="14dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
app:layout_constraintStart_toEndOf="@id/avatar"
|
||||
app:layout_constraintTop_toBottomOf="@id/displayNameTextView"
|
||||
tools:text="\@username" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/acceptButton"
|
||||
style="@style/TuskyImageButton"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginTop="14dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/action_accept"
|
||||
android:padding="4dp"
|
||||
app:layout_constraintEnd_toStartOf="@id/rejectButton"
|
||||
app:layout_constraintTop_toBottomOf="@id/notificationTextView"
|
||||
app:srcCompat="@drawable/ic_check_24dp" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/rejectButton"
|
||||
style="@style/TuskyImageButton"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginTop="14dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/action_reject"
|
||||
android:padding="4dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/notificationTextView"
|
||||
app:srcCompat="@drawable/ic_reject_24dp" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -60,6 +60,7 @@
|
|||
<string name="notification_reblog_format">%s boosted your toot</string>
|
||||
<string name="notification_favourite_format">%s favorited your toot</string>
|
||||
<string name="notification_follow_format">%s followed you</string>
|
||||
<string name="notification_follow_request_format">%s requested to follow you</string>
|
||||
|
||||
<string name="report_username_format">Report @%s</string>
|
||||
<string name="report_comment_hint">Additional comments?</string>
|
||||
|
@ -208,6 +209,7 @@
|
|||
<string name="pref_title_notification_filters">Notify me when</string>
|
||||
<string name="pref_title_notification_filter_mentions">mentioned</string>
|
||||
<string name="pref_title_notification_filter_follows">followed</string>
|
||||
<string name="pref_title_notification_filter_follow_requests">follow requested</string>
|
||||
<string name="pref_title_notification_filter_reblogs">my posts are boosted</string>
|
||||
<string name="pref_title_notification_filter_favourites">my posts are favorited</string>
|
||||
<string name="pref_title_notification_filter_poll">polls have ended</string>
|
||||
|
@ -262,6 +264,8 @@
|
|||
<string name="notification_mention_descriptions">Notifications about new mentions</string>
|
||||
<string name="notification_follow_name">New Followers</string>
|
||||
<string name="notification_follow_description">Notifications about new followers</string>
|
||||
<string name="notification_follow_request_name">Follow Requests</string>
|
||||
<string name="notification_follow_request_description">Notifications about follow requests</string>
|
||||
<string name="notification_boost_name">Boosts</string>
|
||||
<string name="notification_boost_description">Notifications when your toots get boosted</string>
|
||||
<string name="notification_favourite_name">Favorites</string>
|
||||
|
|
|
@ -27,6 +27,12 @@
|
|||
android:title="@string/pref_title_notification_filter_follows"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:key="notificationFilterFollowRequests"
|
||||
android:title="@string/pref_title_notification_filter_follow_requests"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="true"
|
||||
android:key="notificationFilterReblogs"
|
||||
|
|
|
@ -68,6 +68,7 @@ class ComposeActivityTest {
|
|||
notificationsEnabled = true,
|
||||
notificationsMentioned = true,
|
||||
notificationsFollowed = true,
|
||||
notificationsFollowRequested = false,
|
||||
notificationsReblogged = true,
|
||||
notificationsFavorited = true,
|
||||
notificationSound = true,
|
||||
|
|
Loading…
Reference in a new issue