Add support for custom actions on notification
This commit is contained in:
parent
08f2391acc
commit
d8bb9897a4
|
@ -6,9 +6,40 @@
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
typedef void(*dinoWinToastLibNotificationCallback)(int conv_id, void* userdata);
|
typedef enum {
|
||||||
DINOWINTOASTLIB_API int dinoWinToastLibInit();
|
Reason_Activated = 0,
|
||||||
DINOWINTOASTLIB_API int dinoWinToastLibShowMessage(dino_wintoasttemplate templ, int conv_id, dinoWinToastLibNotificationCallback click_callback, void* callback_target);
|
Reason_ApplicationHidden,
|
||||||
|
Reason_TimedOut
|
||||||
|
} dinoWinToastLib_Notification_Reason;
|
||||||
|
|
||||||
|
typedef void(*dinoWinToastLib_Notification_Callback_Simple)(void* userdata);
|
||||||
|
typedef void(*dinoWinToastLib_Notification_Callback_ActivatedWithActionIndex)(int action_id, void* userdata);
|
||||||
|
typedef void(*dinoWinToastLib_Notification_Callback_Dismissed)(dinoWinToastLib_Notification_Reason reason, void* userdata);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
dinoWinToastLib_Notification_Callback_Simple activated;
|
||||||
|
void* activated_context;
|
||||||
|
void(*activated_free)(void*);
|
||||||
|
|
||||||
|
dinoWinToastLib_Notification_Callback_ActivatedWithActionIndex activatedWithIndex;
|
||||||
|
void* activatedWithIndex_context;
|
||||||
|
void(*activatedWithIndex_free)(void*);
|
||||||
|
|
||||||
|
dinoWinToastLib_Notification_Callback_Dismissed dismissed;
|
||||||
|
void* dismissed_context;
|
||||||
|
void(*dismissed_free)(void*);
|
||||||
|
|
||||||
|
dinoWinToastLib_Notification_Callback_Simple failed;
|
||||||
|
void* failed_context;
|
||||||
|
void(*failed_free)(void*);
|
||||||
|
|
||||||
|
} dinoWinToastLib_Notification_Callbacks;
|
||||||
|
|
||||||
|
DINOWINTOASTLIB_API dinoWinToastLib_Notification_Callbacks* dinoWinToastLib_NewCallbacks();
|
||||||
|
DINOWINTOASTLIB_API void dinoWinToastLib_DestroyCallbacks(dinoWinToastLib_Notification_Callbacks* callbacks);
|
||||||
|
|
||||||
|
DINOWINTOASTLIB_API int dinoWinToastLib_Init();
|
||||||
|
DINOWINTOASTLIB_API int dinoWinToastLib_ShowMessage(dino_wintoasttemplate templ, dinoWinToastLib_Notification_Callbacks* callbacks);
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
#endif
|
#endif
|
Binary file not shown.
|
@ -9,33 +9,33 @@ extern "C" {
|
||||||
typedef void* dino_wintoasttemplate;
|
typedef void* dino_wintoasttemplate;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
System,
|
Duration_System,
|
||||||
Short,
|
Duration_Short,
|
||||||
Long
|
Duration_Long
|
||||||
} dino_wintoasttemplate_duration;
|
} dino_wintoasttemplate_duration;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
Default = 0,
|
AudioOption_Default = 0,
|
||||||
Silent = 1,
|
AudioOption_Silent = 1,
|
||||||
Loop = 2
|
AudioOption_Loop = 2
|
||||||
} dino_wintoasttemplate_audiooption;
|
} dino_wintoasttemplate_audiooption;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
FirstLine = 0,
|
TextField_FirstLine = 0,
|
||||||
SecondLine,
|
TextField_SecondLine,
|
||||||
ThirdLine
|
TextField_ThirdLine
|
||||||
} dino_wintoasttemplate_textfield;
|
} dino_wintoasttemplate_textfield;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
ImageAndText01 = 0,
|
TemplateType_ImageAndText01 = 0,
|
||||||
ImageAndText02,
|
TemplateType_ImageAndText02,
|
||||||
ImageAndText03,
|
TemplateType_ImageAndText03,
|
||||||
ImageAndText04,
|
TemplateType_ImageAndText04,
|
||||||
Text01,
|
TemplateType_Text01,
|
||||||
Text02,
|
TemplateType_Text02,
|
||||||
Text03,
|
TemplateType_Text03,
|
||||||
Text04,
|
TemplateType_Text04,
|
||||||
WinToastTemplateTypeCount
|
TemplateType_WinToastTemplateTypeCount
|
||||||
} dino_wintoasttemplate_wintoasttemplatetype;
|
} dino_wintoasttemplate_wintoasttemplatetype;
|
||||||
|
|
||||||
DINOWINTOASTLIB_API dino_wintoasttemplate dino_wintoasttemplate_new(dino_wintoasttemplate_wintoasttemplatetype templ);
|
DINOWINTOASTLIB_API dino_wintoasttemplate dino_wintoasttemplate_new(dino_wintoasttemplate_wintoasttemplatetype templ);
|
||||||
|
|
|
@ -2,6 +2,7 @@ using Dino;
|
||||||
using Dino.Entities;
|
using Dino.Entities;
|
||||||
using DinoWinToast;
|
using DinoWinToast;
|
||||||
using Xmpp;
|
using Xmpp;
|
||||||
|
using Gee;
|
||||||
|
|
||||||
namespace Dino.Plugins.WindowsNotification {
|
namespace Dino.Plugins.WindowsNotification {
|
||||||
public class WindowsNotificationProvider : NotificationProvider, Object {
|
public class WindowsNotificationProvider : NotificationProvider, Object {
|
||||||
|
@ -50,33 +51,37 @@ namespace Dino.Plugins.WindowsNotification {
|
||||||
string summary = _("Subscription request");
|
string summary = _("Subscription request");
|
||||||
string body = Markup.escape_text(conversation.counterpart.to_string());
|
string body = Markup.escape_text(conversation.counterpart.to_string());
|
||||||
|
|
||||||
if (!show_message(summary, body, get_avatar(conversation), conversation.id, stub)) { // missing actions
|
DinoWinToastTemplate template;
|
||||||
warning("Failed showing subscription request notification");
|
var image_path = get_avatar(conversation);
|
||||||
|
if (image_path != null) {
|
||||||
|
template = new DinoWinToastTemplate(TemplateType.ImageAndText02);
|
||||||
|
template.setImagePath(image_path);
|
||||||
|
} else {
|
||||||
|
template = new DinoWinToastTemplate(TemplateType.Text02);
|
||||||
}
|
}
|
||||||
|
|
||||||
// HashTable<string, Variant> hash_table = new HashTable<string, Variant>(null, null);
|
template.setTextField(summary, TextField.FirstLine);
|
||||||
// hash_table["image-data"] = yield get_conversation_icon(conversation);
|
template.setTextField(body, TextField.SecondLine);
|
||||||
// string[] actions = new string[] {"default", "Open conversation", "accept", _("Accept"), "deny", _("Deny")};
|
|
||||||
// try {
|
|
||||||
// uint32 notification_id = dbus_notifications.notify("Dino", 0, "", summary, body, actions, hash_table, 0);
|
|
||||||
|
|
||||||
// if (!conversation_notifications.has_key(conversation)) {
|
template.addAction(_("Accept"));
|
||||||
// conversation_notifications[conversation] = new ArrayList<uint32>();
|
template.addAction(_("Deny"));
|
||||||
// }
|
|
||||||
// conversation_notifications[conversation].add(notification_id);
|
|
||||||
|
|
||||||
// add_action_listener(notification_id, "default", () => {
|
var callbacks = new Callbacks();
|
||||||
// GLib.Application.get_default().activate_action("open-conversation", new Variant.int32(conversation.id));
|
callbacks.activated = () => {
|
||||||
// });
|
app.activate_action("open-conversation", conversation.id);
|
||||||
// add_action_listener(notification_id, "accept", () => {
|
};
|
||||||
// GLib.Application.get_default().activate_action("accept-subscription", new Variant.int32(conversation.id));
|
|
||||||
// });
|
callbacks.activatedWithIndex = (index) => {
|
||||||
// add_action_listener(notification_id, "deny", () => {
|
if (index == 0) {
|
||||||
// GLib.Application.get_default().activate_action("deny-subscription", new Variant.int32(conversation.id));
|
app.activate_action("accept-subscription", conversation.id);
|
||||||
// });
|
} else if (index == 1) {
|
||||||
// } catch (Error e) {
|
app.activate_action("deny-subscription", conversation.id);
|
||||||
// warning("Failed showing subscription request notification: %s", e.message);
|
}
|
||||||
// }
|
};
|
||||||
|
|
||||||
|
if (!ShowMessage(template, callbacks) == 0) {
|
||||||
|
warning("Failed showing subscription request notification");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void notify_connection_error(Account account, ConnectionManager.ConnectionError error) {
|
public async void notify_connection_error(Account account, ConnectionManager.ConnectionError error) {
|
||||||
|
@ -97,7 +102,7 @@ namespace Dino.Plugins.WindowsNotification {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!show_message(summary, body, null, 0, stub)) {
|
if (!show_message(summary, body, null, null)) {
|
||||||
warning("Failed showing connection error notification");
|
warning("Failed showing connection error notification");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,14 +114,38 @@ namespace Dino.Plugins.WindowsNotification {
|
||||||
string summary = _("Invitation to %s").printf(display_room);
|
string summary = _("Invitation to %s").printf(display_room);
|
||||||
string body = _("%s invited you to %s").printf(inviter_display_name, display_room);
|
string body = _("%s invited you to %s").printf(inviter_display_name, display_room);
|
||||||
|
|
||||||
Conversation group_conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(room_jid, account, Conversation.Type.GROUPCHAT);
|
DinoWinToastTemplate template;
|
||||||
if (!show_message(summary, body, get_avatar(direct_conversation), group_conversation.id, stub)) { // action not enabled yet
|
var image_path = get_avatar(conversation);
|
||||||
warning("Failed showing muc invite notification");
|
if (image_path != null) {
|
||||||
|
template = new DinoWinToastTemplate(TemplateType.ImageAndText02);
|
||||||
|
template.setImagePath(image_path);
|
||||||
|
} else {
|
||||||
|
template = new DinoWinToastTemplate(TemplateType.Text02);
|
||||||
}
|
}
|
||||||
|
|
||||||
// HashTable<string, Variant> hash_table = new HashTable<string, Variant>(null, null);
|
template.setTextField(summary, TextField.FirstLine);
|
||||||
// hash_table["image-data"] = yield get_conversation_icon(direct_conversation);
|
template.setTextField(body, TextField.SecondLine);
|
||||||
// string[] actions = new string[] {"default", "", "reject", _("Reject"), "accept", _("Accept")};
|
|
||||||
|
template.addAction(_("Accept"));
|
||||||
|
template.addAction(_("Deny"));
|
||||||
|
|
||||||
|
Conversation group_conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(room_jid, account, Conversation.Type.GROUPCHAT);
|
||||||
|
var callbacks = new Callbacks();
|
||||||
|
callbacks.activated = () => {
|
||||||
|
app.activate_action("open-muc-join", group_conversation.id);
|
||||||
|
};
|
||||||
|
|
||||||
|
callbacks.activatedWithIndex = (index) => {
|
||||||
|
if (index == 0) {
|
||||||
|
app.activate_action("deny-invite", group_conversation.id);
|
||||||
|
} else if (index == 1) {
|
||||||
|
app.activate_action("open-muc-join", group_conversation.id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!ShowMessage(template, callbacks)) {
|
||||||
|
warning("Failed showing muc invite notification");
|
||||||
|
}
|
||||||
|
|
||||||
// try {
|
// try {
|
||||||
// uint32 notification_id = dbus_notifications.notify("Dino", 0, "", summary, body, actions, hash_table, 0);
|
// uint32 notification_id = dbus_notifications.notify("Dino", 0, "", summary, body, actions, hash_table, 0);
|
||||||
|
@ -137,32 +166,38 @@ namespace Dino.Plugins.WindowsNotification {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void notify_voice_request(Conversation conversation, Jid from_jid) {
|
public async void notify_voice_request(Conversation conversation, Jid from_jid) {
|
||||||
|
|
||||||
string display_name = Dino.get_participant_display_name(stream_interactor, conversation, from_jid);
|
string display_name = Dino.get_participant_display_name(stream_interactor, conversation, from_jid);
|
||||||
string display_room = Dino.get_conversation_display_name(stream_interactor, conversation, _("%s from %s"));
|
string display_room = Dino.get_conversation_display_name(stream_interactor, conversation, _("%s from %s"));
|
||||||
string summary = _("Permission request");
|
string summary = _("Permission request");
|
||||||
string body = _("%s requests the permission to write in %s").printf(display_name, display_room);
|
string body = _("%s requests the permission to write in %s").printf(display_name, display_room);
|
||||||
|
|
||||||
if (!show_message(summary, body, get_avatar(conversation), conversation.id, stub)) { // missing actions
|
DinoWinToastTemplate template;
|
||||||
warning("Failed showing voice request notification");
|
var image_path = get_avatar(conversation);
|
||||||
|
if (image_path != null) {
|
||||||
|
template = new DinoWinToastTemplate(TemplateType.ImageAndText02);
|
||||||
|
template.setImagePath(image_path);
|
||||||
|
} else {
|
||||||
|
template = new DinoWinToastTemplate(TemplateType.Text02);
|
||||||
}
|
}
|
||||||
|
|
||||||
// HashTable<string, Variant> hash_table = new HashTable<string, Variant>(null, null);
|
template.setTextField(summary, TextField.FirstLine);
|
||||||
// hash_table["image-data"] = yield get_conversation_icon(conversation);
|
template.setTextField(body, TextField.SecondLine);
|
||||||
// string[] actions = new string[] {"deny", _("Deny"), "accept", _("Accept")};
|
|
||||||
|
|
||||||
// try {
|
template.addAction(_("Accept"));
|
||||||
// uint32 notification_id = dbus_notifications.notify("Dino", 0, "", summary, body, actions, hash_table, 0);
|
template.addAction(_("Deny"));
|
||||||
|
|
||||||
// add_action_listener(notification_id, "accept", () => {
|
var callbacks = new Callbacks();
|
||||||
// GLib.Application.get_default().activate_action("deny-invite", new Variant.int32(conversation.id));
|
callbacks.activatedWithIndex = (index) => {
|
||||||
// });
|
if (index == 0) {
|
||||||
// add_action_listener(notification_id, "deny", () => {
|
app.activate_action("deny-invite", conversation.id);
|
||||||
// GLib.Application.get_default().activate_action("open-muc-join", new Variant.int32(conversation.id));
|
} else if (index == 1) {
|
||||||
// });
|
app.activate_action("open-muc-join", conversation.id);
|
||||||
// } catch (Error e) {
|
}
|
||||||
// warning("Failed showing voice request notification: %s", e.message);
|
};
|
||||||
// }
|
|
||||||
|
if (!ShowMessage(template, callbacks) == 0) {
|
||||||
|
warning("Failed showing voice request notification");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void retract_content_item_notifications() {
|
public async void retract_content_item_notifications() {
|
||||||
|
@ -185,7 +220,7 @@ namespace Dino.Plugins.WindowsNotification {
|
||||||
// content_notifications.unset(conversation);
|
// content_notifications.unset(conversation);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool show_message(string sender, string message, string? image_path, int conv_id, NotificationCallback callback) {
|
private bool show_message(string sender, string message, string? image_path, Callbacks? callbacks = null) {
|
||||||
DinoWinToastTemplate template;
|
DinoWinToastTemplate template;
|
||||||
if (image_path != null) {
|
if (image_path != null) {
|
||||||
template = new DinoWinToastTemplate(TemplateType.ImageAndText02);
|
template = new DinoWinToastTemplate(TemplateType.ImageAndText02);
|
||||||
|
@ -196,7 +231,10 @@ namespace Dino.Plugins.WindowsNotification {
|
||||||
|
|
||||||
template.setTextField(sender, TextField.FirstLine);
|
template.setTextField(sender, TextField.FirstLine);
|
||||||
template.setTextField(message, TextField.SecondLine);
|
template.setTextField(message, TextField.SecondLine);
|
||||||
return ShowMessage(template, conv_id, callback) == 0;
|
if (callbacks != null) {
|
||||||
|
return ShowMessage(template, callbacks) == 0;
|
||||||
|
}
|
||||||
|
return ShowMessage(template, new Callbacks()) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void notify_content_item(Conversation conversation, string conversation_display_name, string? participant_display_name, string body_) {
|
private async void notify_content_item(Conversation conversation, string conversation_display_name, string? participant_display_name, string body_) {
|
||||||
|
@ -206,18 +244,13 @@ namespace Dino.Plugins.WindowsNotification {
|
||||||
}
|
}
|
||||||
|
|
||||||
var avatar = get_avatar(conversation);
|
var avatar = get_avatar(conversation);
|
||||||
if (!show_message(conversation_display_name, body, avatar, conversation.id, onclick_callback)) {
|
var callbacks = new Callbacks();
|
||||||
|
callbacks.activated = () => app.activate_action("open-conversation", conversation.id);
|
||||||
|
if (!show_message(conversation_display_name, body, avatar, callbacks)) {
|
||||||
warning("Failed showing content item notification");
|
warning("Failed showing content item notification");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onclick_callback(int conv_id) {
|
|
||||||
this.app.activate_action("open-conversation", conv_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void stub(int conv_id) {
|
|
||||||
}
|
|
||||||
|
|
||||||
private string? get_avatar(Conversation conversation) {
|
private string? get_avatar(Conversation conversation) {
|
||||||
var avatar_manager = app.stream_interactor.get_module(AvatarManager.IDENTITY);
|
var avatar_manager = app.stream_interactor.get_module(AvatarManager.IDENTITY);
|
||||||
return avatar_manager.get_avatar_filepath(conversation.account, conversation.counterpart);
|
return avatar_manager.get_avatar_filepath(conversation.account, conversation.counterpart);
|
||||||
|
|
|
@ -1,13 +1,45 @@
|
||||||
[CCode (cheader_filename = "DinoWinToastLib.h")]
|
[CCode (cheader_filename = "DinoWinToastLib.h")]
|
||||||
namespace DinoWinToast {
|
namespace DinoWinToast {
|
||||||
|
|
||||||
[CCode (cname = "dinoWinToastLibNotificationCallback", has_target = true)]
|
[CCode (cname = "dinoWinToastLib_Notification_Reason", cprefix = "Reason_")]
|
||||||
public delegate void NotificationCallback(int conv_id);
|
public enum Reason {
|
||||||
|
Activated,
|
||||||
|
ApplicationHidden,
|
||||||
|
TimedOut
|
||||||
|
}
|
||||||
|
|
||||||
[CCode (cname = "dinoWinToastLibInit")]
|
[CCode (cname = "dinoWinToastLib_Notification_Callback_Simple", has_target = true)]
|
||||||
|
public delegate void NotificationCallbackSimple();
|
||||||
|
|
||||||
|
[CCode (cname = "dinoWinToastLib_Notification_Callback_ActivatedWithActionIndex", has_target = true)]
|
||||||
|
public delegate void NotificationCallbackWithActionIndex(int actionId);
|
||||||
|
|
||||||
|
[CCode (cname = "dinoWinToastLib_Notification_Callback_Dismissed", has_target = true)]
|
||||||
|
public delegate void NotificationCallbackDismissed(Reason reason);
|
||||||
|
|
||||||
|
[CCode (cname = "dinoWinToastLib_Notification_Callbacks", free_function = "dinoWinToastLib_DestroyCallbacks")]
|
||||||
|
[Compact]
|
||||||
|
public class Callbacks {
|
||||||
|
[CCode (delegate_target_cname = "activated_context", destroy_notify_cname = "activated_free")]
|
||||||
|
public NotificationCallbackSimple activated;
|
||||||
|
|
||||||
|
[CCode (delegate_target_cname = "activatedWithIndex_context", destroy_notify_cname = "activatedWithIndex_free")]
|
||||||
|
public NotificationCallbackWithActionIndex activatedWithIndex;
|
||||||
|
|
||||||
|
[CCode (delegate_target_cname = "dismissed_context", destroy_notify_cname = "dismissed_free")]
|
||||||
|
public NotificationCallbackDismissed dismissed;
|
||||||
|
|
||||||
|
[CCode (delegate_target_cname = "failed_context", destroy_notify_cname = "failed_free")]
|
||||||
|
public NotificationCallbackSimple failed;
|
||||||
|
|
||||||
|
[CCode (cname = "dinoWinToastLib_NewCallbacks")]
|
||||||
|
public Callbacks();
|
||||||
|
}
|
||||||
|
|
||||||
|
[CCode (cname = "dinoWinToastLib_Init")]
|
||||||
public int Init();
|
public int Init();
|
||||||
|
|
||||||
[CCode (cname = "dinoWinToastLibShowMessage")]
|
[CCode (cname = "dinoWinToastLib_ShowMessage")]
|
||||||
public int ShowMessage(DinoWinToastTemplate templ, int conv_id, NotificationCallback callback);
|
public int ShowMessage(DinoWinToastTemplate templ, Callbacks callbacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,27 +1,27 @@
|
||||||
[CCode (cheader_filename = "DinoWinToastTemplate.h")]
|
[CCode (cheader_filename = "DinoWinToastTemplate.h")]
|
||||||
namespace DinoWinToast {
|
namespace DinoWinToast {
|
||||||
[CCode (cname = "dino_wintoasttemplate_duration", cprefix = "")]
|
[CCode (cname = "dino_wintoasttemplate_duration", cprefix = "Duration_")]
|
||||||
public enum Duration {
|
public enum Duration {
|
||||||
System,
|
System,
|
||||||
Short,
|
Short,
|
||||||
Long
|
Long
|
||||||
}
|
}
|
||||||
|
|
||||||
[CCode (cname = "dino_wintoasttemplate_audiooption", cprefix = "")]
|
[CCode (cname = "dino_wintoasttemplate_audiooption", cprefix = "AudioOption_")]
|
||||||
public enum AudioOption {
|
public enum AudioOption {
|
||||||
Default,
|
Default,
|
||||||
Silent,
|
Silent,
|
||||||
Loop
|
Loop
|
||||||
}
|
}
|
||||||
|
|
||||||
[CCode (cname = "dino_wintoasttemplate_textfield", cprefix = "")]
|
[CCode (cname = "dino_wintoasttemplate_textfield", cprefix = "TextField_")]
|
||||||
public enum TextField {
|
public enum TextField {
|
||||||
FirstLine,
|
FirstLine,
|
||||||
SecondLine,
|
SecondLine,
|
||||||
ThirdLine
|
ThirdLine
|
||||||
}
|
}
|
||||||
|
|
||||||
[CCode (cname = "dino_wintoasttemplate_wintoasttemplatetype", cprefix = "")]
|
[CCode (cname = "dino_wintoasttemplate_wintoasttemplatetype", cprefix = "TemplateType_")]
|
||||||
public enum TemplateType {
|
public enum TemplateType {
|
||||||
ImageAndText01,
|
ImageAndText01,
|
||||||
ImageAndText02,
|
ImageAndText02,
|
||||||
|
|
Loading…
Reference in a new issue