mirror of
https://gitlab.gnome.org/World/tootle
synced 2025-02-01 16:36:59 +01:00
Manager revamp
This commit is contained in:
parent
3d49981c67
commit
ece9abb1b2
@ -19,8 +19,8 @@ executable(
|
||||
meson.project_name(),
|
||||
asresources,
|
||||
'src/Application.vala',
|
||||
'src/Settings.vala',
|
||||
'src/MainWindow.vala',
|
||||
'src/SettingsManager.vala',
|
||||
'src/AccountManager.vala',
|
||||
'src/NetManager.vala',
|
||||
'src/CacheManager.vala',
|
||||
|
@ -7,33 +7,20 @@ public class Tootle.AccountManager : Object{
|
||||
public abstract signal void removed(Account account);
|
||||
|
||||
private static Account current;
|
||||
private static Settings settings;
|
||||
private static AccountManager _instance;
|
||||
public static AccountManager instance{
|
||||
get{
|
||||
if(_instance == null)
|
||||
_instance = new AccountManager();
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
construct{
|
||||
settings = Settings.instance;
|
||||
}
|
||||
|
||||
public AccountManager(){
|
||||
Object();
|
||||
}
|
||||
|
||||
public bool has_client_tokens(){
|
||||
var client_id = settings.client_id;
|
||||
var client_secret = settings.client_secret;
|
||||
var client_id = Tootle.settings.client_id;
|
||||
var client_secret = Tootle.settings.client_secret;
|
||||
|
||||
return !(client_id == "null" || client_secret == "null");
|
||||
}
|
||||
|
||||
public bool has_access_token (){
|
||||
return settings.access_token != "null";
|
||||
return Tootle.settings.access_token != "null";
|
||||
}
|
||||
|
||||
public void request_auth_code (string client_id){
|
||||
@ -43,7 +30,7 @@ public class Tootle.AccountManager : Object{
|
||||
pars += "&client_id=" +client_id;
|
||||
|
||||
try {
|
||||
AppInfo.launch_default_for_uri (settings.instance_url + "/oauth/authorize" + pars, null);
|
||||
AppInfo.launch_default_for_uri (Tootle.settings.instance_url + "/oauth/authorize" + pars, null);
|
||||
}
|
||||
catch (GLib.Error e){
|
||||
warning (e.message);
|
||||
@ -56,14 +43,14 @@ public class Tootle.AccountManager : Object{
|
||||
pars += "&website=https://github.com/bleakgrey/tootle";
|
||||
pars += "&scopes=read%20write%20follow";
|
||||
|
||||
var msg = new Soup.Message("POST", settings.instance_url + "/api/v1/apps" + pars);
|
||||
NetManager.instance.queue(msg, (sess, mess) => {
|
||||
var msg = new Soup.Message("POST", Tootle.settings.instance_url + "/api/v1/apps" + pars);
|
||||
Tootle.network.queue(msg, (sess, mess) => {
|
||||
try{
|
||||
var root = NetManager.parse (mess);
|
||||
var root = Tootle.network.parse (mess);
|
||||
var client_id = root.get_string_member ("client_id");
|
||||
var client_secret = root.get_string_member ("client_secret");
|
||||
settings.client_id = client_id;
|
||||
settings.client_secret = client_secret;
|
||||
Tootle.settings.client_id = client_id;
|
||||
Tootle.settings.client_secret = client_secret;
|
||||
debug ("Received tokens");
|
||||
|
||||
request_auth_code (client_id);
|
||||
@ -77,18 +64,18 @@ public class Tootle.AccountManager : Object{
|
||||
}
|
||||
|
||||
public Soup.Message try_auth (string code){
|
||||
var pars = "?client_id=" + settings.client_id;
|
||||
pars += "&client_secret=" + settings.client_secret;
|
||||
var pars = "?client_id=" + Tootle.settings.client_id;
|
||||
pars += "&client_secret=" + Tootle.settings.client_secret;
|
||||
pars += "&redirect_uri=urn:ietf:wg:oauth:2.0:oob";
|
||||
pars += "&grant_type=authorization_code";
|
||||
pars += "&code=" + code;
|
||||
|
||||
var msg = new Soup.Message("POST", settings.instance_url + "/oauth/token" + pars);
|
||||
NetManager.instance.queue(msg, (sess, mess) => {
|
||||
var msg = new Soup.Message("POST", Tootle.settings.instance_url + "/oauth/token" + pars);
|
||||
Tootle.network.queue(msg, (sess, mess) => {
|
||||
try{
|
||||
var root = NetManager.parse (mess);
|
||||
var root = Tootle.network.parse (mess);
|
||||
var access_token = root.get_string_member ("access_token");
|
||||
settings.access_token = access_token;
|
||||
Tootle.settings.access_token = access_token;
|
||||
debug ("Got access token");
|
||||
update_current ();
|
||||
}
|
||||
@ -101,10 +88,10 @@ public class Tootle.AccountManager : Object{
|
||||
}
|
||||
|
||||
public Soup.Message update_current (){
|
||||
var msg = new Soup.Message("GET", settings.instance_url + "/api/v1/accounts/verify_credentials");
|
||||
NetManager.instance.queue(msg, (sess, mess) => {
|
||||
var msg = new Soup.Message("GET", Tootle.settings.instance_url + "/api/v1/accounts/verify_credentials");
|
||||
Tootle.network.queue(msg, (sess, mess) => {
|
||||
try{
|
||||
var root = NetManager.parse (mess);
|
||||
var root = Tootle.network.parse (mess);
|
||||
current = Account.parse(root);
|
||||
switched (current);
|
||||
}
|
||||
|
@ -5,6 +5,11 @@ namespace Tootle{
|
||||
|
||||
public static Application app;
|
||||
public static MainWindow window;
|
||||
|
||||
public static SettingsManager settings;
|
||||
public static AccountManager accounts;
|
||||
public static NetManager network;
|
||||
public static CacheManager cache;
|
||||
|
||||
public class Application : Granite.Application {
|
||||
|
||||
@ -16,6 +21,10 @@ namespace Tootle{
|
||||
flags = ApplicationFlags.FLAGS_NONE;
|
||||
program_name = "Toot";
|
||||
build_version = "0.1.0";
|
||||
settings = new SettingsManager ();
|
||||
accounts = new AccountManager ();
|
||||
network = new NetManager ();
|
||||
cache = new CacheManager ();
|
||||
}
|
||||
|
||||
public static int main (string[] args) {
|
||||
@ -31,11 +40,11 @@ namespace Tootle{
|
||||
|
||||
protected override void activate () {
|
||||
window.present ();
|
||||
var has_token = AccountManager.instance.has_access_token();
|
||||
var has_token = Tootle.accounts.has_access_token();
|
||||
if(has_token)
|
||||
AccountManager.instance.update_current ();
|
||||
Tootle.accounts.update_current ();
|
||||
else
|
||||
AccountManager.instance.switched (null);
|
||||
Tootle.accounts.switched (null);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,15 +2,6 @@ using Gdk;
|
||||
using GLib;
|
||||
|
||||
public class Tootle.CacheManager : GLib.Object{
|
||||
|
||||
private static CacheManager _instance;
|
||||
public static CacheManager instance{
|
||||
get{
|
||||
if(_instance == null)
|
||||
_instance = new CacheManager();
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
private static string path_images;
|
||||
|
||||
@ -35,7 +26,7 @@ public class Tootle.CacheManager : GLib.Object{
|
||||
|
||||
avatar.pixbuf = pixbuf;
|
||||
});
|
||||
NetManager.instance.queue(msg, (sess, mess) => {});
|
||||
Tootle.network.queue(msg, (sess, mess) => {});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -122,10 +122,10 @@ public class Tootle.PostDialog : Gtk.Dialog {
|
||||
pars += "&in_reply_to_id=" + in_reply_to_id.to_string ();
|
||||
pars += "&visibility=" + visibility_opt.to_string ();
|
||||
|
||||
var msg = new Soup.Message("POST", Settings.instance.instance_url + "/api/v1/statuses" + pars);
|
||||
NetManager.instance.queue(msg, (sess, mess) => {
|
||||
var msg = new Soup.Message("POST", Tootle.settings.instance_url + "/api/v1/statuses" + pars);
|
||||
Tootle.network.queue(msg, (sess, mess) => {
|
||||
try{
|
||||
var root = NetManager.parse (mess);
|
||||
var root = Tootle.network.parse (mess);
|
||||
var status = Status.parse (root);
|
||||
Tootle.window.home.prepend (status);
|
||||
this.destroy ();
|
||||
|
@ -40,7 +40,7 @@ public class Tootle.MainWindow: Gtk.Window {
|
||||
set_titlebar (header);
|
||||
window_position = WindowPosition.CENTER;
|
||||
|
||||
AccountManager.instance.switched.connect(on_account_switched);
|
||||
Tootle.accounts.switched.connect(on_account_switched);
|
||||
}
|
||||
|
||||
private void on_account_switched(Account? account){
|
||||
|
@ -6,15 +6,6 @@ public class Tootle.NetManager : GLib.Object{
|
||||
|
||||
public abstract signal void started();
|
||||
public abstract signal void finished();
|
||||
|
||||
private static NetManager _instance;
|
||||
public static NetManager instance{
|
||||
get{
|
||||
if(_instance == null)
|
||||
_instance = new NetManager();
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
private int requests_processing = 0;
|
||||
private Soup.Session session;
|
||||
@ -36,7 +27,7 @@ public class Tootle.NetManager : GLib.Object{
|
||||
requests_processing++;
|
||||
started ();
|
||||
|
||||
var token = Settings.instance.access_token;
|
||||
var token = Tootle.settings.access_token;
|
||||
if(token != "null")
|
||||
msg.request_headers.append ("Authorization", "Bearer " + token);
|
||||
|
||||
@ -44,7 +35,7 @@ public class Tootle.NetManager : GLib.Object{
|
||||
return msg;
|
||||
}
|
||||
|
||||
public static Json.Object parse(Soup.Message msg) throws GLib.Error{
|
||||
public Json.Object parse(Soup.Message msg) throws GLib.Error{
|
||||
// stdout.printf ("Status Code: %u\n", msg.status_code);
|
||||
// stdout.printf ("Message length: %lld\n", msg.response_body.length);
|
||||
// stdout.printf ("Object: \n%s\n", (string) msg.response_body.data);
|
||||
@ -54,7 +45,7 @@ public class Tootle.NetManager : GLib.Object{
|
||||
return parser.get_root ().get_object ();
|
||||
}
|
||||
|
||||
public static Json.Array parse_array(Soup.Message msg) throws GLib.Error{
|
||||
public Json.Array parse_array(Soup.Message msg) throws GLib.Error{
|
||||
// stdout.printf ("Status Code: %u\n", msg.status_code);
|
||||
// stdout.printf ("Message length: %lld\n", msg.response_body.length);
|
||||
// stdout.printf ("Array: \n%s\n", (string) msg.response_body.data);
|
||||
|
@ -1,29 +0,0 @@
|
||||
public class Tootle.Settings : Granite.Services.Settings {
|
||||
|
||||
private static Settings? _instance;
|
||||
public static Settings instance {
|
||||
get{
|
||||
if (_instance == null)
|
||||
_instance = new Settings ();
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
public string client_id { get; set; }
|
||||
public string client_secret { get; set; }
|
||||
public string access_token { get; set; }
|
||||
public string refresh_token { get; set; }
|
||||
public string instance_url { get; set; }
|
||||
|
||||
public void clear_account (){
|
||||
access_token = "null";
|
||||
refresh_token = "null";
|
||||
instance_url = "null";
|
||||
debug ("Removed current account");
|
||||
}
|
||||
|
||||
private Settings () {
|
||||
base ("com.github.bleakgrey.tootle");
|
||||
}
|
||||
|
||||
}
|
20
src/SettingsManager.vala
Normal file
20
src/SettingsManager.vala
Normal file
@ -0,0 +1,20 @@
|
||||
public class Tootle.SettingsManager : Granite.Services.Settings {
|
||||
|
||||
public string client_id { get; set; }
|
||||
public string client_secret { get; set; }
|
||||
public string access_token { get; set; }
|
||||
public string refresh_token { get; set; }
|
||||
public string instance_url { get; set; }
|
||||
|
||||
public void clear_account (){
|
||||
access_token = "null";
|
||||
refresh_token = "null";
|
||||
instance_url = "null";
|
||||
debug ("Removed current account");
|
||||
}
|
||||
|
||||
public SettingsManager () {
|
||||
base ("com.github.bleakgrey.tootle");
|
||||
}
|
||||
|
||||
}
|
@ -58,7 +58,7 @@ public class Tootle.AccountView : Tootle.HomeView {
|
||||
display_name.label = account.display_name;
|
||||
username.label = "@" + account.acct;
|
||||
note.label = Utils.escape_html (account.note);
|
||||
CacheManager.instance.load_avatar (account.avatar, avatar, 128);
|
||||
Tootle.cache.load_avatar (account.avatar, avatar, 128);
|
||||
|
||||
add_counter (_("Toots"), 1, account.statuses_count);
|
||||
add_counter (_("Follows"), 2, account.following_count);
|
||||
@ -84,7 +84,7 @@ public class Tootle.AccountView : Tootle.HomeView {
|
||||
}
|
||||
|
||||
public override string get_url (){
|
||||
var url = "%s/api/v1/accounts/%lld/statuses".printf (Settings.instance.instance_url, account.id);
|
||||
var url = "%s/api/v1/accounts/%lld/statuses".printf (Tootle.settings.instance_url, account.id);
|
||||
url += "?limit=25";
|
||||
|
||||
if (max_id > 0)
|
||||
|
@ -3,7 +3,6 @@ using Granite;
|
||||
|
||||
public class Tootle.AddAccountView : Tootle.AbstractView {
|
||||
|
||||
AccountManager manager;
|
||||
public Stack stack;
|
||||
GridInstance grid_instance;
|
||||
GridCode grid_code;
|
||||
@ -86,8 +85,6 @@ public class Tootle.AddAccountView : Tootle.AbstractView {
|
||||
|
||||
|
||||
construct {
|
||||
manager = AccountManager.instance;
|
||||
|
||||
stack = new Stack ();
|
||||
stack.valign = Gtk.Align.CENTER;
|
||||
stack.transition_type = StackTransitionType.SLIDE_LEFT_RIGHT;
|
||||
@ -120,12 +117,12 @@ public class Tootle.AddAccountView : Tootle.AbstractView {
|
||||
}
|
||||
|
||||
private void on_next_click(){
|
||||
Settings.instance.clear_account ();
|
||||
Settings.instance.instance_url = grid_instance.entry.text;
|
||||
Tootle.settings.clear_account ();
|
||||
Tootle.settings.instance_url = grid_instance.entry.text;
|
||||
grid_instance.sensitive = false;
|
||||
|
||||
if(!manager.has_client_tokens ()){
|
||||
var msg = manager.request_client_tokens ();
|
||||
if(!Tootle.accounts.has_client_tokens ()){
|
||||
var msg = Tootle.accounts.request_client_tokens ();
|
||||
msg.finished.connect(() => {
|
||||
grid_instance.sensitive = true;
|
||||
stack.set_visible_child_name ("code");
|
||||
@ -134,13 +131,13 @@ public class Tootle.AddAccountView : Tootle.AbstractView {
|
||||
else{
|
||||
grid_instance.sensitive = true;
|
||||
stack.set_visible_child_name ("code");
|
||||
manager.request_auth_code (Settings.instance.client_id);
|
||||
Tootle.accounts.request_auth_code (Tootle.settings.client_id);
|
||||
}
|
||||
}
|
||||
|
||||
private void on_add_click (){
|
||||
var code = grid_code.code.text;
|
||||
manager.try_auth (code);
|
||||
Tootle.accounts.try_auth (code);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ public class Tootle.HomeView : Tootle.AbstractView {
|
||||
this.timeline = timeline;
|
||||
|
||||
view.remove.connect (on_remove);
|
||||
AccountManager.instance.switched.connect(on_account_changed);
|
||||
Tootle.accounts.switched.connect(on_account_changed);
|
||||
|
||||
// var s = new Status(1);
|
||||
// s.content = "Test content, wow!";
|
||||
@ -51,7 +51,7 @@ public class Tootle.HomeView : Tootle.AbstractView {
|
||||
}
|
||||
|
||||
public virtual string get_url (){
|
||||
var url = Settings.instance.instance_url;
|
||||
var url = Tootle.settings.instance_url;
|
||||
url += "api/v1/timelines/";
|
||||
url += this.timeline;
|
||||
url += "?limit=25";
|
||||
@ -64,9 +64,9 @@ public class Tootle.HomeView : Tootle.AbstractView {
|
||||
|
||||
public void request (){
|
||||
var msg = new Soup.Message("GET", get_url ());
|
||||
NetManager.instance.queue(msg, (sess, mess) => {
|
||||
Tootle.network.queue(msg, (sess, mess) => {
|
||||
try{
|
||||
NetManager.parse_array (mess).foreach_element ((array, i, node) => {
|
||||
Tootle.network.parse_array (mess).foreach_element ((array, i, node) => {
|
||||
var object = node.get_object ();
|
||||
if (object != null){
|
||||
var status = Status.parse(object);
|
||||
|
@ -7,7 +7,7 @@ public class Tootle.NotificationsView : Tootle.AbstractView {
|
||||
base (true);
|
||||
|
||||
view.remove.connect (on_remove);
|
||||
AccountManager.instance.switched.connect(on_account_changed);
|
||||
Tootle.accounts.switched.connect(on_account_changed);
|
||||
}
|
||||
|
||||
public override string get_icon () {
|
||||
@ -41,13 +41,13 @@ public class Tootle.NotificationsView : Tootle.AbstractView {
|
||||
if(account == null)
|
||||
return;
|
||||
|
||||
var url = Settings.instance.instance_url;
|
||||
var url = Tootle.settings.instance_url;
|
||||
url += "/api/v1/notifications";
|
||||
|
||||
var msg = new Soup.Message("GET", url);
|
||||
NetManager.instance.queue(msg, (sess, mess) => {
|
||||
Tootle.network.queue(msg, (sess, mess) => {
|
||||
try{
|
||||
NetManager.parse_array (mess).foreach_element ((array, i, node) => {
|
||||
Tootle.network.parse_array (mess).foreach_element ((array, i, node) => {
|
||||
var object = node.get_object ();
|
||||
if (object != null){
|
||||
var notification = Notification.parse(object);
|
||||
|
@ -27,11 +27,11 @@ public class Tootle.StatusView : Tootle.AbstractView {
|
||||
}
|
||||
|
||||
public Soup.Message request_context (){
|
||||
var url = "%s/api/v1/statuses/%lld/context".printf (Settings.instance.instance_url, root_status.id);
|
||||
var url = "%s/api/v1/statuses/%lld/context".printf (Tootle.settings.instance_url, root_status.id);
|
||||
var msg = new Soup.Message("GET", url);
|
||||
NetManager.instance.queue(msg, (sess, mess) => {
|
||||
Tootle.network.queue(msg, (sess, mess) => {
|
||||
try{
|
||||
var root = NetManager.parse (mess);
|
||||
var root = Tootle.network.parse (mess);
|
||||
|
||||
var ancestors = root.get_array_member ("ancestors");
|
||||
ancestors.foreach_element ((array, i, node) => {
|
||||
|
@ -26,7 +26,7 @@ public class Tootle.AccountsButton : Gtk.MenuButton{
|
||||
logout = new Gtk.Button.from_icon_name ("pane-hide-symbolic", Gtk.IconSize.SMALL_TOOLBAR);
|
||||
logout.receives_default = false;
|
||||
logout.tooltip_text = _("Log out");
|
||||
logout.clicked.connect (() => AccountManager.instance.logout ());
|
||||
logout.clicked.connect (() => Tootle.accounts.logout ());
|
||||
show_all ();
|
||||
|
||||
attach(display_name, 1, 0, 1, 1);
|
||||
@ -68,9 +68,9 @@ public class Tootle.AccountsButton : Gtk.MenuButton{
|
||||
add(avatar);
|
||||
show_all ();
|
||||
|
||||
AccountManager.instance.switched.connect (account => {
|
||||
Tootle.accounts.switched.connect (account => {
|
||||
if (account != null){
|
||||
CacheManager.instance.load_avatar (account.avatar, avatar, 24);
|
||||
Tootle.cache.load_avatar (account.avatar, avatar, 24);
|
||||
default_account.display_name.label = "<b>"+account.display_name+"</b>";
|
||||
default_account.user.label = "@"+account.username;
|
||||
}
|
||||
|
@ -43,8 +43,8 @@ public class Tootle.HeaderBar : Gtk.HeaderBar{
|
||||
});
|
||||
button_mode.show ();
|
||||
|
||||
NetManager.instance.started.connect (() => spinner.show ());
|
||||
NetManager.instance.finished.connect (() => spinner.hide ());
|
||||
Tootle.network.started.connect (() => spinner.show ());
|
||||
Tootle.network.finished.connect (() => spinner.hide ());
|
||||
|
||||
pack_start (button_back);
|
||||
pack_start (button_toot);
|
||||
|
@ -57,12 +57,12 @@ public class Tootle.NotificationWidget : Gtk.Grid {
|
||||
}
|
||||
|
||||
public static Soup.Message dismiss (Notification notification){
|
||||
var url = Settings.instance.instance_url;
|
||||
var url = Tootle.settings.instance_url;
|
||||
url += "api/v1/notifications/dismiss";
|
||||
url += "?id=" + notification.id.to_string ();
|
||||
|
||||
var msg = new Soup.Message("POST", url);
|
||||
NetManager.instance.queue(msg, (sess, mess) => {});
|
||||
Tootle.network.queue(msg, (sess, mess) => {});
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
@ -145,7 +145,7 @@ public class Tootle.StatusWidget : Gtk.EventBox {
|
||||
favorite.sensitive = true;
|
||||
|
||||
var avatar_url = status.reblog != null ? status.reblog.account.avatar : status.account.avatar;
|
||||
CacheManager.instance.load_avatar (avatar_url, this.avatar, this.avatar_size);
|
||||
Tootle.cache.load_avatar (avatar_url, this.avatar, this.avatar_size);
|
||||
}
|
||||
|
||||
public bool on_avatar_clicked (){
|
||||
@ -177,7 +177,7 @@ public class Tootle.StatusWidget : Gtk.EventBox {
|
||||
if (!state)
|
||||
action = "unreblog";
|
||||
|
||||
var msg = new Soup.Message("POST", Settings.instance.instance_url + "/api/v1/statuses/" + status.id.to_string () + "/" + action);
|
||||
var msg = new Soup.Message("POST", Tootle.settings.instance_url + "/api/v1/statuses/" + status.id.to_string () + "/" + action);
|
||||
msg.finished.connect (() => {
|
||||
status.reblogged = state;
|
||||
reblog.sensitive = false;
|
||||
@ -188,8 +188,8 @@ public class Tootle.StatusWidget : Gtk.EventBox {
|
||||
status.reblogs_count -= 1;
|
||||
rebind ();
|
||||
});
|
||||
NetManager.instance.queue (msg, (sess, mess) => {
|
||||
//NetManager.parse (msg);
|
||||
Tootle.network.queue (msg, (sess, mess) => {
|
||||
//Tootle.network.parse (msg);
|
||||
});
|
||||
}
|
||||
|
||||
@ -199,7 +199,7 @@ public class Tootle.StatusWidget : Gtk.EventBox {
|
||||
if (!state)
|
||||
action = "unfavourite";
|
||||
|
||||
var msg = new Soup.Message ("POST", Settings.instance.instance_url + "/api/v1/statuses/" + status.id.to_string () + "/" + action);
|
||||
var msg = new Soup.Message ("POST", Tootle.settings.instance_url + "/api/v1/statuses/" + status.id.to_string () + "/" + action);
|
||||
msg.finished.connect (() => {
|
||||
status.favorited = state;
|
||||
reblog.sensitive = false;
|
||||
@ -210,8 +210,8 @@ public class Tootle.StatusWidget : Gtk.EventBox {
|
||||
status.favourites_count -= 1;
|
||||
rebind ();
|
||||
});
|
||||
NetManager.instance.queue (msg, (sess, mess) => {
|
||||
//NetManager.parse (msg);
|
||||
Tootle.network.queue (msg, (sess, mess) => {
|
||||
//Tootle.network.parse (msg);
|
||||
});
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user