diff --git a/api/src/main/java/telegram/files/AutoDownloadVerticle.java b/api/src/main/java/telegram/files/AutoDownloadVerticle.java index 0483bee..b6571d7 100644 --- a/api/src/main/java/telegram/files/AutoDownloadVerticle.java +++ b/api/src/main/java/telegram/files/AutoDownloadVerticle.java @@ -11,6 +11,7 @@ import io.vertx.core.Promise; import io.vertx.core.json.Json; import io.vertx.core.json.JsonObject; import org.drinkless.tdlib.TdApi; +import org.jooq.lambda.tuple.Tuple2; import telegram.files.repository.FileRecord; import telegram.files.repository.SettingAutoRecords; import telegram.files.repository.SettingKey; @@ -34,7 +35,7 @@ public class AutoDownloadVerticle extends AbstractVerticle { private static final int DOWNLOAD_INTERVAL = 10 * 1000; - private static final List FILE_TYPE_ORDER = List.of("photo", "video", "audio", "file"); + private static final List DEFAULT_FILE_TYPE_ORDER = List.of("photo", "video", "audio", "file"); // telegramId -> messages private final Map> waitingDownloadMessages = new ConcurrentHashMap<>(); @@ -137,11 +138,11 @@ public class AutoDownloadVerticle extends AbstractVerticle { log.debug("Scan history end! TelegramId: %d ChatId: %d".formatted(auto.telegramId, auto.chatId)); return; } - if (StrUtil.isBlank(auto.nextFileType)) { - auto.nextFileType = FILE_TYPE_ORDER.getFirst(); - } + Tuple2> rule = handleRule(auto); + TelegramVerticle telegramVerticle = this.getTelegramVerticle(auto.telegramId); TdApi.SearchChatMessages searchChatMessages = new TdApi.SearchChatMessages(); + searchChatMessages.query = rule.v1; searchChatMessages.chatId = auto.chatId; searchChatMessages.fromMessageId = auto.nextFromMessageId; searchChatMessages.limit = Math.min(MAX_WAITING_LENGTH, 100); @@ -153,10 +154,11 @@ public class AutoDownloadVerticle extends AbstractVerticle { return; } if (foundChatMessages.messages.length == 0) { - int nextTypeIndex = FILE_TYPE_ORDER.indexOf(auto.nextFileType) + 1; - if (nextTypeIndex < FILE_TYPE_ORDER.size()) { + List fileTypes = rule.v2; + int nextTypeIndex = fileTypes.indexOf(auto.nextFileType) + 1; + if (nextTypeIndex < fileTypes.size()) { String originalType = auto.nextFileType; - auto.nextFileType = FILE_TYPE_ORDER.get(nextTypeIndex); + auto.nextFileType = fileTypes.get(nextTypeIndex); auto.nextFromMessageId = 0; log.debug("%s No more %s files found! Switch to %s".formatted(auto.uniqueKey(), originalType, auto.nextFileType)); addHistoryMessage(auto, currentTimeMillis); @@ -181,6 +183,25 @@ public class AutoDownloadVerticle extends AbstractVerticle { } } + private Tuple2> handleRule(SettingAutoRecords.Item auto) { + SettingAutoRecords.Rule rule = auto.rule; + String query = null; + List fileTypes = DEFAULT_FILE_TYPE_ORDER; + if (rule != null) { + if (StrUtil.isNotBlank(rule.query)) { + query = rule.query; + } + if (CollUtil.isNotEmpty(rule.fileTypes)) { + fileTypes = rule.fileTypes; + } + } + if (StrUtil.isBlank(auto.nextFileType)) { + auto.nextFileType = fileTypes.getFirst(); + } + + return new Tuple2<>(query, fileTypes); + } + private boolean isExceedLimit(long telegramId) { List waitingMessages = this.waitingDownloadMessages.get(telegramId); return getSurplusSize(telegramId) <= 0 || (waitingMessages != null && waitingMessages.size() > limit); diff --git a/api/src/main/java/telegram/files/HttpVerticle.java b/api/src/main/java/telegram/files/HttpVerticle.java index 8fd8865..3bc35ee 100644 --- a/api/src/main/java/telegram/files/HttpVerticle.java +++ b/api/src/main/java/telegram/files/HttpVerticle.java @@ -605,8 +605,8 @@ public class HttpVerticle extends AbstractVerticle { ctx.fail(400); return; } - - telegramVerticle.toggleAutoDownload(Convert.toLong(chatId)) + JsonObject params = ctx.body().asJsonObject(); + telegramVerticle.toggleAutoDownload(Convert.toLong(chatId), params) .onSuccess(r -> ctx.end()) .onFailure(ctx::fail); } diff --git a/api/src/main/java/telegram/files/TelegramVerticle.java b/api/src/main/java/telegram/files/TelegramVerticle.java index b37097a..822b485 100644 --- a/api/src/main/java/telegram/files/TelegramVerticle.java +++ b/api/src/main/java/telegram/files/TelegramVerticle.java @@ -3,6 +3,7 @@ package telegram.files; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.codec.Base64; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.date.DateField; import cn.hutool.core.date.DatePattern; @@ -439,7 +440,7 @@ public class TelegramVerticle extends AbstractVerticle { .mapEmpty(); } - public Future toggleAutoDownload(Long chatId) { + public Future toggleAutoDownload(Long chatId, JsonObject params) { return DataVerticle.settingRepository.getByKey(SettingKey.autoDownload) .compose(settingAutoRecords -> { if (settingAutoRecords == null) { @@ -448,7 +449,14 @@ public class TelegramVerticle extends AbstractVerticle { if (settingAutoRecords.exists(this.telegramRecord.id(), chatId)) { settingAutoRecords.remove(this.telegramRecord.id(), chatId); } else { - settingAutoRecords.add(this.telegramRecord.id(), chatId); + SettingAutoRecords.Rule rule = null; + if (params != null && params.containsKey("rule")) { + rule = params.getJsonObject("rule").mapTo(SettingAutoRecords.Rule.class); + if (StrUtil.isBlank(rule.query) && CollUtil.isEmpty(rule.fileTypes)) { + rule = null; + } + } + settingAutoRecords.add(this.telegramRecord.id(), chatId, rule); } return DataVerticle.settingRepository.createOrUpdate(SettingKey.autoDownload.name(), Json.encode(settingAutoRecords)); }) @@ -824,19 +832,22 @@ public class TelegramVerticle extends AbstractVerticle { private Future convertChat(List chats) { return DataVerticle.settingRepository.getByKey(SettingKey.autoDownload) - .map(settingAutoRecords -> settingAutoRecords == null ? Collections.emptySet() - : settingAutoRecords.getChatIds(this.telegramRecord.id())) - .map(enableAutoChatIds -> new JsonArray(chats.stream() - .map(chat -> new JsonObject() - .put("id", Convert.toStr(chat.id)) - .put("name", chat.id == this.telegramRecord.id() ? "Saved Messages" : chat.title) - .put("type", TdApiHelp.getChatType(chat.type)) - .put("avatar", Base64.encode((byte[]) BeanUtil.getProperty(chat, "photo.minithumbnail.data"))) - .put("unreadCount", chat.unreadCount) - .put("lastMessage", "") - .put("lastMessageTime", "") - .put("autoEnabled", enableAutoChatIds.contains(chat.id)) - ) + .map(settingAutoRecords -> settingAutoRecords == null ? new HashMap() + : settingAutoRecords.getItems(this.telegramRecord.id())) + .map(enableAutoChats -> new JsonArray(chats.stream() + .map(chat -> { + SettingAutoRecords.Item autoItem = enableAutoChats.get(chat.id); + return new JsonObject() + .put("id", Convert.toStr(chat.id)) + .put("name", chat.id == this.telegramRecord.id() ? "Saved Messages" : chat.title) + .put("type", TdApiHelp.getChatType(chat.type)) + .put("avatar", Base64.encode((byte[]) BeanUtil.getProperty(chat, "photo.minithumbnail.data"))) + .put("unreadCount", chat.unreadCount) + .put("lastMessage", "") + .put("lastMessageTime", "") + .put("autoEnabled", autoItem != null) + .put("autoRule", autoItem == null ? null : autoItem.rule); + }) .toList() )); } diff --git a/api/src/main/java/telegram/files/repository/SettingAutoRecords.java b/api/src/main/java/telegram/files/repository/SettingAutoRecords.java index 7e327f5..2c3f83e 100644 --- a/api/src/main/java/telegram/files/repository/SettingAutoRecords.java +++ b/api/src/main/java/telegram/files/repository/SettingAutoRecords.java @@ -2,7 +2,8 @@ package telegram.files.repository; import java.util.ArrayList; import java.util.List; -import java.util.Set; +import java.util.Map; +import java.util.function.Function; import java.util.stream.Collectors; public class SettingAutoRecords { @@ -18,12 +19,15 @@ public class SettingAutoRecords { public long nextFromMessageId; + public Rule rule; + public Item() { } - public Item(long telegramId, long chatId) { + public Item(long telegramId, long chatId, Rule rule) { this.telegramId = telegramId; this.chatId = chatId; + this.rule = rule; } public String uniqueKey() { @@ -31,6 +35,20 @@ public class SettingAutoRecords { } } + public static class Rule { + public String query; + + public List fileTypes; + + public Rule() { + } + + public Rule(String query, List fileTypes) { + this.query = query; + this.fileTypes = fileTypes; + } + } + public SettingAutoRecords() { this.items = new ArrayList<>(); } @@ -48,19 +66,18 @@ public class SettingAutoRecords { items.add(item); } - public void add(long telegramId, long chatId) { - items.add(new Item(telegramId, chatId)); + public void add(long telegramId, long chatId, Rule rule) { + items.add(new Item(telegramId, chatId, rule)); } public void remove(long telegramId, long chatId) { items.removeIf(item -> item.telegramId == telegramId && item.chatId == chatId); } - public Set getChatIds(long telegramId) { + public Map getItems(long telegramId) { return items.stream() .filter(item -> item.telegramId == telegramId) - .map(item -> item.chatId) - .collect(Collectors.toSet()); + .collect(Collectors.toMap(i -> i.chatId, Function.identity())); } } diff --git a/web/package-lock.json b/web/package-lock.json index 95aa2f5..2c8517f 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -12,6 +12,7 @@ "@dnd-kit/modifiers": "^8.0.0", "@dnd-kit/sortable": "^9.0.0", "@hookform/resolvers": "^3.9.1", + "@radix-ui/react-accordion": "^1.2.2", "@radix-ui/react-avatar": "^1.1.1", "@radix-ui/react-checkbox": "^1.1.2", "@radix-ui/react-dialog": "^1.1.4", @@ -970,6 +971,119 @@ "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz", "integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==" }, + "node_modules/@radix-ui/react-accordion": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.2.tgz", + "integrity": "sha512-b1oh54x4DMCdGsB4/7ahiSrViXxaBwRPotiZNnYXjLha9vfuURSAZErki6qjDoSIV0eXx5v57XnTGVtGwnfp2g==", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-collapsible": "1.1.2", + "@radix-ui/react-collection": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-use-controllable-state": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz", + "integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==" + }, + "node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/react-collection": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.1.tgz", + "integrity": "sha512-LwT3pSho9Dljg+wY2KN2mrrh6y3qELfftINERIzBUO9e0N+t0oMTyn3k9iv+ZqgrwGkRnLpNJrsMv9BZlt2yuA==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-slot": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", + "integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/react-primitive": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz", + "integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==", + "dependencies": { + "@radix-ui/react-slot": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/react-slot": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz", + "integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-arrow": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz", @@ -1046,6 +1160,116 @@ } } }, + "node_modules/@radix-ui/react-collapsible": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.2.tgz", + "integrity": "sha512-PliMB63vxz7vggcyq0IxNYk8vGDrLXVWw4+W4B8YnwI1s18x7YZYqlG9PLX7XxAJUi0g2DxP4XKJMFHh/iVh9A==", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz", + "integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==" + }, + "node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", + "integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-presence": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz", + "integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-primitive": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz", + "integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==", + "dependencies": { + "@radix-ui/react-slot": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-slot": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz", + "integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-collection": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.0.tgz", @@ -8322,6 +8546,62 @@ "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz", "integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==" }, + "@radix-ui/react-accordion": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.2.tgz", + "integrity": "sha512-b1oh54x4DMCdGsB4/7ahiSrViXxaBwRPotiZNnYXjLha9vfuURSAZErki6qjDoSIV0eXx5v57XnTGVtGwnfp2g==", + "requires": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-collapsible": "1.1.2", + "@radix-ui/react-collection": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-use-controllable-state": "1.1.0" + }, + "dependencies": { + "@radix-ui/primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz", + "integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==" + }, + "@radix-ui/react-collection": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.1.tgz", + "integrity": "sha512-LwT3pSho9Dljg+wY2KN2mrrh6y3qELfftINERIzBUO9e0N+t0oMTyn3k9iv+ZqgrwGkRnLpNJrsMv9BZlt2yuA==", + "requires": { + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-slot": "1.1.1" + } + }, + "@radix-ui/react-compose-refs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", + "integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==", + "requires": {} + }, + "@radix-ui/react-primitive": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz", + "integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==", + "requires": { + "@radix-ui/react-slot": "1.1.1" + } + }, + "@radix-ui/react-slot": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz", + "integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==", + "requires": { + "@radix-ui/react-compose-refs": "1.1.1" + } + } + } + }, "@radix-ui/react-arrow": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz", @@ -8356,6 +8636,59 @@ "@radix-ui/react-use-size": "1.1.0" } }, + "@radix-ui/react-collapsible": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.2.tgz", + "integrity": "sha512-PliMB63vxz7vggcyq0IxNYk8vGDrLXVWw4+W4B8YnwI1s18x7YZYqlG9PLX7XxAJUi0g2DxP4XKJMFHh/iVh9A==", + "requires": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "dependencies": { + "@radix-ui/primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz", + "integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==" + }, + "@radix-ui/react-compose-refs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", + "integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==", + "requires": {} + }, + "@radix-ui/react-presence": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz", + "integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==", + "requires": { + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.0" + } + }, + "@radix-ui/react-primitive": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz", + "integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==", + "requires": { + "@radix-ui/react-slot": "1.1.1" + } + }, + "@radix-ui/react-slot": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz", + "integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==", + "requires": { + "@radix-ui/react-compose-refs": "1.1.1" + } + } + } + }, "@radix-ui/react-collection": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.0.tgz", diff --git a/web/package.json b/web/package.json index 92395bd..52551ff 100644 --- a/web/package.json +++ b/web/package.json @@ -20,6 +20,7 @@ "@dnd-kit/modifiers": "^8.0.0", "@dnd-kit/sortable": "^9.0.0", "@hookform/resolvers": "^3.9.1", + "@radix-ui/react-accordion": "^1.2.2", "@radix-ui/react-avatar": "^1.1.1", "@radix-ui/react-checkbox": "^1.1.2", "@radix-ui/react-dialog": "^1.1.4", diff --git a/web/src/components/auto-download-dialog.tsx b/web/src/components/auto-download-dialog.tsx index ee430b2..98d617f 100644 --- a/web/src/components/auto-download-dialog.tsx +++ b/web/src/components/auto-download-dialog.tsx @@ -7,7 +7,7 @@ import { DialogTrigger, } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; -import { useState } from "react"; +import React, { useState } from "react"; import useSWRMutation from "swr/mutation"; import { POST } from "@/lib/api"; import { useDebounce } from "use-debounce"; @@ -15,17 +15,41 @@ import { useToast } from "@/hooks/use-toast"; import { AutoDownloadButton } from "@/components/auto-download-button"; import { useTelegramChat } from "@/hooks/use-telegram-chat"; import { useTelegramAccount } from "@/hooks/use-telegram-account"; +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from "./ui/accordion"; +import { Label } from "@/components/ui/label"; +import { Input } from "@/components/ui/input"; +import { type AutoDownloadRule, type FileType } from "@/lib/types"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Badge } from "./ui/badge"; +import { X } from "lucide-react"; export default function AutoDownloadDialog() { const { accountId } = useTelegramAccount(); const { isLoading, chat, reload } = useTelegramChat(); const { toast } = useToast(); const [open, setOpen] = useState(false); + const [rule, setRule] = useState({ + query: "", + fileTypes: [], + }); const { trigger: triggerAuto, isMutating: isAutoMutating } = useSWRMutation( !accountId || !chat ? undefined : `/file/auto-download?telegramId=${accountId}&chatId=${chat?.id}`, - POST, + (key, { arg }: { arg: { rule: AutoDownloadRule } }) => { + return POST(key, arg); + }, { onSuccess: () => { toast({ @@ -74,43 +98,109 @@ export default function AutoDownloadDialog() { {chat?.autoEnabled ? ( -
-
- -

- This will disable auto download for this chat. You can always - enable it later. -

-
-
- -

- Files that are being downloaded will be paused and you can - enable automatic downloading again later. -

+
+ {chat.autoRule && ( +
+
+ + + Active + +
+ +
+ {/* Filter Keyword Section */} +
+
+ + Filter Keyword + + + {chat.autoRule.query || "No keyword specified"} + +
+
+ +
+ + File Types + +
+ {chat.autoRule.fileTypes.length > 0 ? ( + chat.autoRule.fileTypes.map((type) => ( + + {type} + + )) + ) : ( + + No file types selected + + )} +
+
+
+
+ )} +
+
+ +

+ This will disable auto download for this chat. You can always + enable it later. +

+
+
+ +

+ Files that are being downloaded will be paused and you can + enable automatic downloading again later. +

+
) : ( -
-
- -

- This will enable auto download for this chat. Files will be - downloaded automatically. -

-
-
- -

- Files in historical messages will be downloaded first, and then - files in new messages will be downloaded automatically. -

+
+
+
+ +

+ This will enable auto download for this chat. Files will be + downloaded automatically. +

+
+
+ +

+ Files in historical messages will be downloaded first, and + then files in new messages will be downloaded automatically. +

+
+
+ +

+ Download Order: + + {"Photo -> Video -> Audio -> File"} + +

+
+
)}