Merge remote-tracking branch 'upstream/master' into master-windows-changes

This commit is contained in:
LAGonauta 2023-01-30 08:53:45 -03:00
commit 3d61f175a6
172 changed files with 19166 additions and 17433 deletions

View file

@ -7,7 +7,7 @@ jobs:
- uses: actions/checkout@v2
- run: sudo apt-get update
- run: sudo apt-get remove libunwind-14-dev
- run: sudo apt-get install -y build-essential gettext cmake valac libgee-0.8-dev libsqlite3-dev libgtk-4-dev libnotify-dev libgpgme-dev libsoup2.4-dev libgcrypt20-dev libqrencode-dev libgspell-1-dev libnice-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libsrtp2-dev libwebrtc-audio-processing-dev
- run: sudo apt-get install -y build-essential gettext cmake valac libgee-0.8-dev libsqlite3-dev libgtk-4-dev libnotify-dev libgpgme-dev libsoup2.4-dev libgcrypt20-dev libqrencode-dev libgspell-1-dev libnice-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libsrtp2-dev libwebrtc-audio-processing-dev libadwaita-1-dev
- run: ./configure --with-tests --with-libsignal-in-tree
- run: make
- run: build/xmpp-vala-test

11
cmake/FindAdwaita.cmake Normal file
View file

@ -0,0 +1,11 @@
include(PkgConfigWithFallback)
find_pkg_config_with_fallback(Adwaita
PKG_CONFIG_NAME libadwaita-1
LIB_NAMES libadwaita-1
INCLUDE_NAMES adwaita.h
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Adwaita
REQUIRED_VARS Adwaita_LIBRARY
VERSION_VAR Adwaita_VERSION)

View file

@ -42,9 +42,10 @@ public class Conversation : Object {
public enum Setting { DEFAULT, ON, OFF }
public Setting send_typing { get; set; default = Setting.DEFAULT; }
public Setting send_marker { get; set; default = Setting.DEFAULT; }
public int pinned { get; set; default = 0; }
private Database? db;
public Conversation(Jid jid, Account account, Type type) {
@ -74,6 +75,7 @@ public class Conversation : Object {
notify_setting = (NotifySetting) row[db.conversation.notification];
send_typing = (Setting) row[db.conversation.send_typing];
send_marker = (Setting) row[db.conversation.send_marker];
pinned = row[db.conversation.pinned];
notify.connect(on_update);
}
@ -91,7 +93,8 @@ public class Conversation : Object {
.value(db.conversation.active_last_changed, (long) active_last_changed.to_unix())
.value(db.conversation.notification, notify_setting)
.value(db.conversation.send_typing, send_typing)
.value(db.conversation.send_marker, send_marker);
.value(db.conversation.send_marker, send_marker)
.value(db.conversation.pinned, pinned);
if (read_up_to != null) {
insert.value(db.conversation.read_up_to, read_up_to.id);
}
@ -197,6 +200,8 @@ public class Conversation : Object {
update.set(db.conversation.send_typing, send_typing); break;
case "send-marker":
update.set(db.conversation.send_marker, send_marker); break;
case "pinned":
update.set(db.conversation.pinned, pinned); break;
}
update.perform();
}

View file

@ -154,6 +154,7 @@ public interface ConversationItemWidgetInterface: Object {
public delegate void MessageActionEvoked(Object button, Plugins.MetaConversationItem evoked_on, Object widget);
public class MessageAction : Object {
public string icon_name;
public string? tooltip;
public Object? popover;
public MessageActionEvoked? callback;
}

View file

@ -44,11 +44,8 @@ public class ContentItemStore : StreamInteractionModule, Object {
Gee.TreeSet<ContentItem> items = new Gee.TreeSet<ContentItem>(ContentItem.compare_func);
foreach (var row in select) {
int id = row[db.content_item.id];
int content_type = row[db.content_item.content_type];
int foreign_id = row[db.content_item.foreign_id];
DateTime time = new DateTime.from_unix_utc(row[db.content_item.time]);
items.add(get_item(conversation, id, content_type, foreign_id, time));
ContentItem content_item = get_item_from_row(row, conversation);
items.add(content_item);
}
Gee.List<ContentItem> ret = new ArrayList<ContentItem>();
@ -58,6 +55,14 @@ public class ContentItemStore : StreamInteractionModule, Object {
return ret;
}
private ContentItem get_item_from_row(Row row, Conversation conversation) throws Error {
int id = row[db.content_item.id];
int content_type = row[db.content_item.content_type];
int foreign_id = row[db.content_item.foreign_id];
DateTime time = new DateTime.from_unix_utc(row[db.content_item.time]);
return get_item(conversation, id, content_type, foreign_id, time);
}
private ContentItem get_item(Conversation conversation, int id, int content_type, int foreign_id, DateTime time) throws Error {
switch (content_type) {
case 1:
@ -112,6 +117,91 @@ public class ContentItemStore : StreamInteractionModule, Object {
return item.size > 0 ? item[0] : null;
}
public string? get_message_id_for_content_item(Conversation conversation, ContentItem content_item) {
Message? message = get_message_for_content_item(conversation, content_item);
if (message == null) return null;
if (message.edit_to != null) return message.edit_to;
if (conversation.type_ == Conversation.Type.CHAT) {
return message.stanza_id;
} else {
return message.server_id;
}
}
public Jid? get_message_sender_for_content_item(Conversation conversation, ContentItem content_item) {
Message? message = get_message_for_content_item(conversation, content_item);
if (message == null) return null;
// No need to look at edit_to, because it's the same sender JID.
return message.from;
}
public Message? get_message_for_content_item(Conversation conversation, ContentItem content_item) {
FileItem? file_item = content_item as FileItem;
if (file_item != null) {
if (file_item.file_transfer.provider != 0 || file_item.file_transfer.info == null) return null;
int message_db_id = int.parse(file_item.file_transfer.info);
return stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_id(message_db_id, conversation);
}
MessageItem? message_item = content_item as MessageItem;
if (message_item != null) {
return message_item.message;
}
return null;
}
public ContentItem? get_content_item_for_message_id(Conversation conversation, string message_id) {
Row? row = get_content_item_row_for_message_id(conversation, message_id);
if (row != null) {
return get_item_from_row(row, conversation);
}
return null;
}
public int get_content_item_id_for_message_id(Conversation conversation, string message_id) {
Row? row = get_content_item_row_for_message_id(conversation, message_id);
if (row != null) {
return row[db.content_item.id];
}
return -1;
}
private Row? get_content_item_row_for_message_id(Conversation conversation, string message_id) {
var content_item_row = db.content_item.select();
Message? message = null;
if (conversation.type_ == Conversation.Type.CHAT) {
message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_stanza_id(message_id, conversation);
} else {
message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_server_id(message_id, conversation);
}
if (message == null) return null;
RowOption file_transfer_row = db.file_transfer.select()
.with(db.file_transfer.account_id, "=", conversation.account.id)
.with(db.file_transfer.counterpart_id, "=", db.get_jid_id(conversation.counterpart))
.with(db.file_transfer.info, "=", message.id.to_string())
.order_by(db.file_transfer.time, "DESC")
.single().row();
if (file_transfer_row.is_present()) {
content_item_row.with(db.content_item.foreign_id, "=", file_transfer_row[db.file_transfer.id])
.with(db.content_item.content_type, "=", 2);
} else {
content_item_row.with(db.content_item.foreign_id, "=", message.id)
.with(db.content_item.content_type, "=", 1);
}
RowOption content_item_row_option = content_item_row.single().row();
if (content_item_row_option.is_present()) {
return content_item_row_option.inner;
}
return null;
}
public ContentItem? get_latest(Conversation conversation) {
Gee.List<ContentItem> items = get_n_latest(conversation, 1);
if (items.size > 0) {

View file

@ -7,7 +7,7 @@ using Dino.Entities;
namespace Dino {
public class Database : Qlite.Database {
private const int VERSION = 24;
private const int VERSION = 25;
public class AccountTable : Table {
public Column<int> id = new Column.Integer("id") { primary_key = true, auto_increment = true };
@ -244,10 +244,11 @@ public class Database : Qlite.Database {
public Column<int> notification = new Column.Integer("notification") { min_version=3 };
public Column<int> send_typing = new Column.Integer("send_typing") { min_version=3 };
public Column<int> send_marker = new Column.Integer("send_marker") { min_version=3 };
public Column<int> pinned = new Column.Integer("pinned") { default="0", min_version=25 };
internal ConversationTable(Database db) {
base(db, "conversation");
init({id, account_id, jid_id, resource, active, active_last_changed, last_active, type_, encryption, read_up_to, read_up_to_item, notification, send_typing, send_marker});
init({id, account_id, jid_id, resource, active, active_last_changed, last_active, type_, encryption, read_up_to, read_up_to_item, notification, send_typing, send_marker, pinned});
}
}

View file

@ -204,19 +204,18 @@ public class Dino.HistorySync {
var query_params = new Xmpp.MessageArchiveManagement.V2.MamQueryParams.query_latest(mam_server, latest_message_time, latest_message_id);
PageRequestResult page_result = yield get_mam_page(account, query_params, null);
if (page_result.page_result == PageResult.Duplicate) {
// No new messages
return null;
}
debug("[%s | %s] Latest page result: %s", account.bare_jid.to_string(), mam_server.to_string(), page_result.page_result.to_string());
if (page_result.page_result == PageResult.Error) {
debug("[%s | %s] Failed fetching latest page %s", mam_server.to_string(), mam_server.to_string(), page_result.page_result.to_string());
return null;
}
// If we get PageResult.Duplicate, we still want to update the db row to the latest message.
// Catchup finished within first page. Update latest db entry.
if (page_result.page_result in new PageResult[] { PageResult.TargetReached, PageResult.NoMoreMessages } && latest_row_id != -1) {
if (latest_row_id != -1 &&
page_result.page_result in new PageResult[] { PageResult.TargetReached, PageResult.NoMoreMessages, PageResult.Duplicate }) {
if (page_result.stanzas == null || page_result.stanzas.is_empty) return null;
string latest_mam_id = page_result.query_result.last;
@ -258,7 +257,7 @@ public class Dino.HistorySync {
.value(db.mam_catchup.server_jid, mam_server.to_string())
.value(db.mam_catchup.from_id, from_id)
.value(db.mam_catchup.from_time, from_time)
.value(db.mam_catchup.from_end, false)
.value(db.mam_catchup.from_end, page_result.page_result == PageResult.NoMoreMessages)
.value(db.mam_catchup.to_id, to_id)
.value(db.mam_catchup.to_time, to_time)
.perform();

View file

@ -425,24 +425,8 @@ public class MessageProcessor : StreamInteractionModule, Object {
new_message.type_ = MessageStanza.TYPE_CHAT;
}
if (message.quoted_item_id > 0) {
ContentItem? content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_id(conversation, message.quoted_item_id);
if (content_item != null && content_item.type_ == MessageItem.TYPE) {
Message? quoted_message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_id(((MessageItem) content_item).message.id, conversation);
if (quoted_message != null) {
Xep.Replies.set_reply_to(new_message, new Xep.Replies.ReplyTo(quoted_message.from, quoted_message.stanza_id));
string body_with_fallback = "> " + Dino.message_body_without_reply_fallback(quoted_message);
body_with_fallback = body_with_fallback.replace("\n", "\n> ");
body_with_fallback += "\n";
long fallback_length = body_with_fallback.length;
body_with_fallback += message.body;
new_message.body = body_with_fallback;
var fallback_location = new Xep.FallbackIndication.FallbackLocation(0, (int)fallback_length);
Xep.FallbackIndication.set_fallback(new_message, new Xep.FallbackIndication.Fallback(Xep.Replies.NS_URI, new Xep.FallbackIndication.FallbackLocation[] { fallback_location }));
}
}
}
string? fallback = get_fallback_body_set_infos(message, new_message, conversation);
new_message.body = fallback == null ? message.body : fallback + message.body;
build_message_stanza(message, new_message, conversation);
pre_message_send(message, new_message, conversation);
@ -487,6 +471,37 @@ public class MessageProcessor : StreamInteractionModule, Object {
}
});
}
public string? get_fallback_body_set_infos(Entities.Message message, MessageStanza new_stanza, Conversation conversation) {
if (message.quoted_item_id == 0) return null;
ContentItem? content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_id(conversation, message.quoted_item_id);
if (content_item == null) return null;
Jid? quoted_sender = stream_interactor.get_module(ContentItemStore.IDENTITY).get_message_sender_for_content_item(conversation, content_item);
string? quoted_stanza_id = stream_interactor.get_module(ContentItemStore.IDENTITY).get_message_id_for_content_item(conversation, content_item);
if (quoted_sender != null && quoted_stanza_id != null) {
Xep.Replies.set_reply_to(new_stanza, new Xep.Replies.ReplyTo(quoted_sender, quoted_stanza_id));
}
string fallback = "> ";
if (content_item.type_ == MessageItem.TYPE) {
Message? quoted_message = ((MessageItem) content_item).message;
fallback += Dino.message_body_without_reply_fallback(quoted_message);
fallback = fallback.replace("\n", "\n> ");
} else if (content_item.type_ == FileItem.TYPE) {
FileTransfer? quoted_file = ((FileItem) content_item).file_transfer;
fallback += quoted_file.file_name;
}
fallback += "\n";
long fallback_length = fallback.length;
var fallback_location = new Xep.FallbackIndication.FallbackLocation(0, (int)fallback_length);
Xep.FallbackIndication.set_fallback(new_stanza, new Xep.FallbackIndication.Fallback(Xep.Replies.NS_URI, new Xep.FallbackIndication.FallbackLocation[] { fallback_location }));
return fallback;
}
}
public abstract class MessageListener : Xmpp.OrderedListener {

View file

@ -116,9 +116,7 @@ public class MessageStorage : StreamInteractionModule, Object {
.outer_join_with(db.message_correction, db.message_correction.message_id, db.message.id)
.outer_join_with(db.reply, db.reply.message_id, db.message.id);
if (conversation.counterpart.resourcepart == null) {
query.with_null(db.message.counterpart_resource);
} else {
if (conversation.counterpart.resourcepart != null) {
query.with(db.message.counterpart_resource, "=", conversation.counterpart.resourcepart);
}

View file

@ -10,7 +10,6 @@ public class Dino.Reactions : StreamInteractionModule, Object {
public string id { get { return IDENTITY.id; } }
public signal void reaction_added(Account account, int content_item_id, Jid jid, string reaction);
// [Signal(detailed=true)]
public signal void reaction_removed(Account account, int content_item_id, Jid jid, string reaction);
private StreamInteractor stream_interactor;
@ -27,7 +26,7 @@ public class Dino.Reactions : StreamInteractionModule, Object {
this.db = database;
stream_interactor.account_added.connect(on_account_added);
stream_interactor.get_module(MessageProcessor.IDENTITY).message_sent_or_received.connect(on_new_message);
stream_interactor.get_module(ContentItemStore.IDENTITY).new_item.connect(on_new_item);
}
public void add_reaction(Conversation conversation, ContentItem content_item, string reaction) {
@ -35,15 +34,19 @@ public class Dino.Reactions : StreamInteractionModule, Object {
if (!reactions.contains(reaction)) {
reactions.add(reaction);
}
send_reactions(conversation, content_item, reactions);
reaction_added(conversation.account, content_item.id, conversation.account.bare_jid, reaction);
try {
send_reactions(conversation, content_item, reactions);
reaction_added(conversation.account, content_item.id, conversation.account.bare_jid, reaction);
} catch (SendError e) {}
}
public void remove_reaction(Conversation conversation, ContentItem content_item, string reaction) {
Gee.List<string> reactions = get_own_reactions(conversation, content_item);
reactions.remove(reaction);
send_reactions(conversation, content_item, reactions);
reaction_removed(conversation.account, content_item.id, conversation.account.bare_jid, reaction);
try {
send_reactions(conversation, content_item, reactions);
reaction_removed(conversation.account, content_item.id, conversation.account.bare_jid, reaction);
} catch (SendError e) {}
}
public Gee.List<ReactionUsers> get_item_reactions(Conversation conversation, ContentItem content_item) {
@ -80,35 +83,28 @@ public class Dino.Reactions : StreamInteractionModule, Object {
return false;
}
private void send_reactions(Conversation conversation, ContentItem content_item, Gee.List<string> reactions) {
Message? message = null;
private void send_reactions(Conversation conversation, ContentItem content_item, Gee.List<string> reactions) throws SendError {
string? message_id = stream_interactor.get_module(ContentItemStore.IDENTITY).get_message_id_for_content_item(conversation, content_item);
if (message_id == null) throw new SendError.Misc("No message for content_item");
FileItem? file_item = content_item as FileItem;
if (file_item != null) {
int message_id = int.parse(file_item.file_transfer.info);
message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_id(message_id, conversation);
}
MessageItem? message_item = content_item as MessageItem;
if (message_item != null) {
message = message_item.message;
}
XmppStream? stream = stream_interactor.get_stream(conversation.account);
if (stream == null) throw new SendError.NoStream("");
if (message == null) {
return;
}
var reactions_module = stream.get_module(Xmpp.Xep.Reactions.Module.IDENTITY);
XmppStream stream = stream_interactor.get_stream(conversation.account);
if (conversation.type_ == Conversation.Type.GROUPCHAT || conversation.type_ == Conversation.Type.GROUPCHAT_PM) {
if (conversation.type_ == Conversation.Type.GROUPCHAT) {
stream.get_module(Xmpp.Xep.Reactions.Module.IDENTITY).send_reaction(stream, conversation.counterpart, "groupchat", message.server_id ?? message.stanza_id, reactions);
} else if (conversation.type_ == Conversation.Type.GROUPCHAT_PM) {
stream.get_module(Xmpp.Xep.Reactions.Module.IDENTITY).send_reaction(stream, conversation.counterpart, "chat", message.server_id ?? message.stanza_id, reactions);
}
if (conversation.type_ == Conversation.Type.GROUPCHAT) {
reactions_module.send_reaction.begin(stream, conversation.counterpart, "groupchat", message_id, reactions);
// We save the reaction when it gets reflected back to us
} else if (conversation.type_ == Conversation.Type.GROUPCHAT_PM) {
reactions_module.send_reaction(stream, conversation.counterpart, "chat", message_id, reactions);
} else if (conversation.type_ == Conversation.Type.CHAT) {
stream.get_module(Xmpp.Xep.Reactions.Module.IDENTITY).send_reaction(stream, conversation.counterpart, "chat", message.stanza_id, reactions);
int64 now_millis = GLib.get_real_time () / 1000;
save_chat_reactions(conversation.account, conversation.account.bare_jid, content_item.id, now_millis, reactions);
reactions_module.send_reaction.begin(stream, conversation.counterpart, "chat", message_id, reactions, (_, res) => {
try {
reactions_module.send_reaction.end(res);
save_chat_reactions(conversation.account, conversation.account.bare_jid, content_item.id, now_millis, reactions);
} catch (SendError e) {}
});
}
}
@ -251,11 +247,11 @@ public class Dino.Reactions : StreamInteractionModule, Object {
Message reaction_message = yield stream_interactor.get_module(MessageProcessor.IDENTITY).parse_message_stanza(account, stanza);
Conversation conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation_for_message(reaction_message);
Message? message = get_message_for_reaction(conversation, message_id);
var reaction_info = new ReactionInfo() { account=account, from_jid=from_jid, reactions=reactions, stanza=stanza, received_time=new DateTime.now() };
int content_item_id = stream_interactor.get_module(ContentItemStore.IDENTITY).get_content_item_id_for_message_id(conversation, message_id);
var reaction_info = new ReactionInfo() { conversation=conversation, from_jid=from_jid, reactions=reactions, stanza=stanza, received_time=new DateTime.now() };
if (message != null) {
process_reaction_for_message(message.id, reaction_info);
if (content_item_id != -1) {
process_reaction_for_message(content_item_id, reaction_info);
return;
}
@ -267,45 +263,33 @@ public class Dino.Reactions : StreamInteractionModule, Object {
reaction_infos[message_id].add(reaction_info);
}
private void on_new_message(Message message, Conversation conversation) {
Gee.List<ReactionInfo>? reaction_info_list = null;
if (conversation.type_ == Conversation.Type.CHAT) {
reaction_info_list = reaction_infos[message.stanza_id];
} else {
reaction_info_list = reaction_infos[message.server_id];
}
/*
* When we get a new ContentItem, check if we have any reactions cached that apply to it.
* If so, process the reactions, map and store them.
*/
private void on_new_item(ContentItem item, Conversation conversation) {
string? stanza_id = stream_interactor.get_module(ContentItemStore.IDENTITY).get_message_id_for_content_item(conversation, item);
if (stanza_id == null) return;
Gee.List<ReactionInfo>? reaction_info_list = reaction_infos[stanza_id];
if (reaction_info_list == null) return;
Message? message = stream_interactor.get_module(ContentItemStore.IDENTITY).get_message_for_content_item(conversation, item);
if (message == null) return;
// Check if the (or potentially which) reaction fits the message
ReactionInfo? reaction_info = null;
foreach (ReactionInfo info in reaction_info_list) {
if (!info.account.equals(conversation.account)) return;
switch (info.stanza.type_) {
case MessageStanza.TYPE_CHAT:
Jid counterpart = message.from.equals_bare(conversation.account.bare_jid) ? info.stanza.from: info.stanza.to;
if (message.type_ != Message.Type.CHAT || !counterpart.equals_bare(conversation.counterpart)) continue;
break;
case MessageStanza.TYPE_GROUPCHAT:
if (message.type_ != Message.Type.GROUPCHAT || !message.from.equals_bare(conversation.counterpart)) continue;
break;
default:
break;
var applicable_reactions = new ArrayList<ReactionInfo>();
applicable_reactions.add_all_iterator(reaction_info_list.filter(info => info.conversation.equals(conversation)));
foreach (ReactionInfo applicable_reaction in applicable_reactions) {
reaction_info_list.remove(applicable_reaction);
if (reaction_info_list.is_empty) {
reaction_infos.unset(stanza_id);
}
reaction_info = info;
debug("Got ContentItem for reaction %s", stanza_id);
process_reaction_for_message(item.id, applicable_reaction);
}
if (reaction_info == null) return;
reaction_info_list.remove(reaction_info);
if (reaction_info_list.is_empty) {
if (conversation.type_ == Conversation.Type.GROUPCHAT) {
reaction_infos.unset(message.server_id);
} else {
reaction_infos.unset(message.stanza_id);
}
}
debug("Got message for reaction %s", message.stanza_id);
process_reaction_for_message(message.id, reaction_info);
}
private Message? get_message_for_reaction(Conversation conversation, string message_id) {
@ -317,30 +301,12 @@ public class Dino.Reactions : StreamInteractionModule, Object {
}
}
private void process_reaction_for_message(int message_db_id, ReactionInfo reaction_info) {
Account account = reaction_info.account;
private void process_reaction_for_message(int content_item_id, ReactionInfo reaction_info) {
Account account = reaction_info.conversation.account;
MessageStanza stanza = reaction_info.stanza;
Jid from_jid = reaction_info.from_jid;
Gee.List<string> reactions = reaction_info.reactions;
RowOption file_transfer_row = db.file_transfer.select()
.with(db.file_transfer.account_id, "=", account.id)
.with(db.file_transfer.info, "=", message_db_id.to_string())
.single().row(); // TODO better
var content_item_row = db.content_item.select();
if (file_transfer_row.is_present()) {
content_item_row.with(db.content_item.foreign_id, "=", file_transfer_row[db.file_transfer.id])
.with(db.content_item.content_type, "=", 2);
} else {
content_item_row.with(db.content_item.foreign_id, "=", message_db_id)
.with(db.content_item.content_type, "=", 1);
}
var content_item_row_opt = content_item_row.single().row();
if (!content_item_row_opt.is_present()) return;
int content_item_id = content_item_row_opt[db.content_item.id];
// Get reaction time
DateTime? reaction_time = null;
DelayedDelivery.MessageFlag? delayed_message_flag = DelayedDelivery.MessageFlag.get_flag(stanza);
@ -485,7 +451,7 @@ public class Dino.ReactionUsers {
}
public class Dino.ReactionInfo {
public Account account { get; set; }
public Conversation conversation { get; set; }
public Jid from_jid { get; set; }
public Gee.List<string> reactions { get; set; }
public MessageStanza stanza { get; set; }

View file

@ -77,22 +77,7 @@ public class Dino.Replies : StreamInteractionModule, Object {
Xep.Replies.ReplyTo? reply_to = Xep.Replies.get_reply_to(stanza);
if (reply_to == null) return;
Message? quoted_message = null;
if (conversation.type_ == Conversation.Type.GROUPCHAT) {
quoted_message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_server_id(reply_to.to_message_id, conversation);
} else {
quoted_message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_stanza_id(reply_to.to_message_id, conversation);
}
if (quoted_message == null) {
db.reply.upsert()
.value(db.reply.message_id, message.id, true)
.value(db.reply.quoted_message_stanza_id, reply_to.to_message_id)
.value(db.reply.quoted_message_from, reply_to.to_jid.to_string())
.perform();
return;
}
ContentItem? quoted_content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_foreign(conversation, 1, quoted_message.id);
ContentItem? quoted_content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_content_item_for_message_id(conversation, reply_to.to_message_id);
if (quoted_content_item == null) return;
set_message_is_reply_to(message, quoted_content_item);

View file

@ -1,4 +1,5 @@
using Dino.Entities;
using Qlite;
namespace Dino {

View file

@ -11,6 +11,7 @@ find_packages(MAIN_PACKAGES REQUIRED
GObject
GTK4
ICU
Adwaita
)
set(RESOURCE_LIST
@ -89,7 +90,8 @@ set(RESOURCE_LIST
unified_main_content.ui
unified_window_placeholder.ui
theme.css
style.css
style-dark.css
)
compile_gresources(
@ -110,6 +112,12 @@ set(MAIN_DEFINITIONS)
if(GTK4_VERSION VERSION_GREATER_EQUAL "4.6")
set(MAIN_DEFINITIONS ${MAIN_DEFINITIONS} GTK_4_6)
endif()
if(GTK4_VERSION VERSION_GREATER_EQUAL "4.8")
set(MAIN_DEFINITIONS ${MAIN_DEFINITIONS} GTK_4_8)
endif()
if(Adwaita_VERSION VERSION_GREATER_EQUAL "1.2")
set(MAIN_DEFINITIONS ${MAIN_DEFINITIONS} Adw_1_2)
endif()
vala_precompile(MAIN_VALA_C
SOURCES
@ -199,7 +207,9 @@ SOURCES
src/ui/util/label_hybrid.vala
src/ui/util/sizing_bin.vala
src/ui/util/size_request_box.vala
src/ui/util/scaling_image.vala
src/ui/widgets/fixed_ratio_picture.vala
src/ui/widgets/natural_size_increase.vala
CUSTOM_VAPIS
${CMAKE_BINARY_DIR}/exports/xmpp-vala.vapi
${CMAKE_BINARY_DIR}/exports/qlite.vapi

View file

@ -31,7 +31,9 @@
<property name="halign">end</property>
<property name="valign">start</property>
<style>
<class name="linked"/>
<class name="card"/>
<class name="toolbar"/>
<class name="overlay-toolbar"/>
</style>
</object>
</child>

View file

@ -1,13 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk" version="4.0"/>
<object class="GtkHeaderBar" id="header_bar">
<requires lib="adw" version="1.0"/>
<object class="AdwHeaderBar" id="header_bar">
<property name="hexpand">False</property>
<style>
<class name="dino-left"/>
</style>
<child>
<object class="GtkMenuButton" id="add_button">
<property name="has-frame">False</property>
<child>
<object class="GtkImage">
<property name="icon-name">list-add-symbolic</property>
@ -18,6 +20,7 @@
</child>
<child type="end">
<object class="GtkMenuButton" id="menu_button">
<property name="has-frame">False</property>
<child>
<object class="GtkImage">
<property name="icon-name">open-menu-symbolic</property>

View file

@ -88,20 +88,33 @@
</object>
</child>
<child>
<object class="GtkRevealer" id="unread_count_revealer">
<object class="GtkRevealer" id="top_row_revealer">
<property name="transition-type">slide-right</property>
<property name="transition-duration">50</property>
<property name="reveal-child">True</property>
<property name="margin-start">15</property>
<child>
<object class="GtkLabel" id="unread_count_label">
<property name="vexpand">False</property>
<property name="visible">False</property>
<property name="margin-start">15</property>
<property name="xalign">0.5</property>
<attributes>
<attribute name="scale" value="0.6"/>
<attribute name="weight" value="PANGO_WEIGHT_BOLD"/>
</attributes>
<object class="GtkBox">
<property name="orientation">horizontal</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="unread_count_label">
<property name="vexpand">False</property>
<property name="visible">False</property>
<property name="xalign">0.5</property>
<attributes>
<attribute name="scale" value="0.6"/>
<attribute name="weight" value="PANGO_WEIGHT_BOLD"/>
</attributes>
</object>
</child>
<child>
<object class="GtkImage" id="pinned_image">
<property name="icon-name">view-pin-symbolic</property>
<property name="pixel-size">12</property>
<property name="visible">False</property>
</object>
</child>
</object>
</child>
</object>
@ -131,7 +144,7 @@
<property name="margin-start">5</property>
<style>
<class name="conversation_list_row_xbutton"/>
<class name="circular-button"/>
<class name="circular"/>
<class name="flat"/>
</style>
<child>

View file

@ -31,7 +31,8 @@
<property name="halign">end</property>
<property name="valign">end</property>
<style>
<class name="circular-button"/>
<class name="circular"/>
<class name="opaque"/>
</style>
<child>
<object class="GtkImage">

View file

@ -19,40 +19,13 @@
<object class="GtkStackPage">
<property name="name">empty</property>
<property name="child">
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="spacing">10</property>
<property name="valign">center</property>
<child>
<object class="GtkImage">
<property name="icon-name">system-search-symbolic</property>
<property name="icon-size">large</property>
<property name="pixel-size">72</property>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label" translatable="1">No active search</property>
<attributes>
<attribute name="weight" value="PANGO_WEIGHT_BOLD"></attribute>
<attribute name="scale" value="1.3"></attribute>
</attributes>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label" translatable="1">Type to start a search</property>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
<object class="AdwStatusPage">
<style>
<class name="compact"/>
</style>
<property name="icon-name">system-search-symbolic</property>
<property name="title" translatable="1">No active search</property>
<property name="description" translatable="1">Type to start a search</property>
</object>
</property>
</object>
@ -61,40 +34,13 @@
<object class="GtkStackPage">
<property name="name">no-result</property>
<property name="child">
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="spacing">10</property>
<property name="valign">center</property>
<child>
<object class="GtkImage">
<property name="icon-name">face-uncertain-symbolic</property>
<property name="icon-size">large</property>
<property name="pixel-size">72</property>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label" translatable="1">No matching messages</property>
<attributes>
<attribute name="weight" value="PANGO_WEIGHT_BOLD"></attribute>
<attribute name="scale" value="1.3"></attribute>
</attributes>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label" translatable="1">Check the spelling or try to remove filters</property>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
<object class="AdwStatusPage">
<style>
<class name="compact"/>
</style>
<property name="icon-name">face-uncertain-symbolic</property>
<property name="title" translatable="1">No matching messages</property>
<property name="description" translatable="1">Check the spelling or try to remove filters</property>
</object>
</property>
</object>

View file

@ -17,13 +17,14 @@
<summary xml:lang="pt">Moderno cliente de chat XMPP</summary>
<summary xml:lang="pl">Nowoczesny komunikator XMPP</summary>
<summary xml:lang="oc">Client XMPP modèrn</summary>
<summary xml:lang="nl_BE">Modernen XMPP-chatcliënt</summary>
<summary xml:lang="nl">Een moderne XMPP-chatclient</summary>
<summary xml:lang="nb">Moderne XMPP-sludreklient</summary>
<summary xml:lang="lt">Šiuolaikinė XMPP pokalbių kliento programa</summary>
<summary xml:lang="lb">Modernen XMPP Chat Client</summary>
<summary xml:lang="ko">현대 XMPP 채팅 클라이언트</summary>
<summary xml:lang="ja">現代的な XMPP チャット クライアント</summary>
<summary xml:lang="it">Client di chat moderno per XMPP</summary>
<summary xml:lang="is">Nútímalegt XMPP-spjallforrit</summary>
<summary xml:lang="ie">Un modern client de conversationes XMPP</summary>
<summary xml:lang="id">Aplikasi chat XMPP modern</summary>
<summary xml:lang="hu">Modern XMPP csevegőprogram</summary>
@ -33,6 +34,7 @@
<summary xml:lang="eu">XMPP txat bezero modernoa</summary>
<summary xml:lang="es">Un cliente de XMPP moderno</summary>
<summary xml:lang="eo">Moderna XMPP-Retebabililo</summary>
<summary xml:lang="el">Σύγχρονος XMPP Chat Client</summary>
<summary xml:lang="de">Modernes XMPP-Chat-Programm</summary>
<summary xml:lang="cs">Moderní XMPP klient</summary>
<summary xml:lang="ca">Client de xat XMPP modern</summary>
@ -50,13 +52,14 @@
<p xml:lang="pt">Dino é um moderno chat de código aberto para desktop. Ele é focado em prover uma transparente e confiável experiência Jabber/XMPP, tendo em mente a sua privacidade.</p>
<p xml:lang="pl">Dino jest nowoczesnym, otwartym komunikatorem. Skupia się na prostej obsłudze sieci Jabber/XMPP dbając o twoją prywatność.</p>
<p xml:lang="oc">Dino es un client de chat liure e modèrn per lordenador de burèu. Ensaja de provesir una experiéncia neta e fisabla de Jabber/XMPP en téner en compte vòstra confidencialitat.</p>
<p xml:lang="nl_BE">Dino is een moderne, vrije chattoepassing voor uw bureaublad. Ze biedt een eenvoudige en betrouwbare Jabber/XMPP-ervaring, met uw privacy in het achterhoofd.</p>
<p xml:lang="nl">Dino is een moderne, vrije chattoepassing voor je computer. Dino biedt een eenvoudige en betrouwbare Jabber-/XMPP-ervaring, met privacy in het achterhoofd.</p>
<p xml:lang="nb">Dino er en moderne friporg-sludringsklient for skrivebordet. Det fokuserer på rask og pålitelig XMPP-opplevelse, samtidig som det hegner om personvernet.</p>
<p xml:lang="lt">Dino yra šiuolaikinė atvirojo kodo kliento programa skirta darbalaukiui. Jos pagrindinis dėmesys yra pateikti tvarkingą ir patikimą Jabber/XMPP patyrimą nepamirštant apie jūsų privatumą.</p>
<p xml:lang="lb">Dino ass e modernen, quell-offene Chat Client fir den Desktop. Hien biet eng opgeraumt a robust Jabber/XMPP Erfarung a leet ee Schwéierpunkt op Privatsphär.</p>
<p xml:lang="ko">Dino는 데스크탑을 위한 현대 오픈소스 채팅 클라이언트입니다. 깔끔하고 신뢰 할 수 있는 Jabber/XMPP 경험을 개인정보 보호 중시와 함께 제공할 수 있도록 주력하고 있습니다.</p>
<p xml:lang="ja">Dino はオープンソースの現代的なデスクトップ向けチャットクライアントです。プライバシーを考慮しつつ、シンプルで信頼できる Jabber/XMPP エクスペリエンスの提供を第一に考えて開発されています。</p>
<p xml:lang="it">Dino è un client di chat per il desktop, moderno e open-source. Si concentra nel fornire un'esperienza Jabber/XMPP pulita e affidabile tenendo presente la tua privacy.</p>
<p xml:lang="is">Dino er nútímalegt spjallforrit og frjáls hugbúnaður fyrir skjáborðið. Það leggur áherslu á að veita hreina og áreiðanlega Jabber/XMPP upplifun með friðhelgi þína í huga.</p>
<p xml:lang="ie">Dino es un modern cliente de conversationes con fonte apert. It foca se sur provider un nett e fidibil experientie de Jabber/XMPP con un attention a confidentialitá.</p>
<p xml:lang="id">Dino adalah aplikasi chat open source modern untuk PC. Menyediakan pengalaman Jabber / XMPP yang handal dengan tetap menjunjung privasi Anda.</p>
<p xml:lang="hu">A Dino egy modern, nyílt forráskódú csevegőprogram az asztali gépekre. Arra összpontosít, hogy tiszta és megbízható Jabber/XMPP-élményt nyújtson, miközben a magánszféra megőrzését is fontosnak tartja.</p>
@ -67,6 +70,7 @@
<p xml:lang="eu">Dino mahaigainerako iturburu irekiko txat bezero moderno bat da. Jabber/XMPP esperientzia garbi eta fidagarri bat ematen du zure pribatutasuna kontuan hartzeaz gain.</p>
<p xml:lang="es">Dino es un cliente de mensajería moderno y libre para escritorio y móvil. Está enfocado en proveer una experiencia Jabber/XMPP limpia y confiable teniendo la privacidad en mente.</p>
<p xml:lang="eo">Dino estas moderna malfermfonta retbabililo por la tabla komputilo. Ĝi celas provizi puran kaj fidindan sperton de Jabber/XMPP, protektante vian privatecon.</p>
<p xml:lang="el">Το Dino είναι ένας σύγχρονος πελάτης συνομιλίας ανοιχτού κώδικα για desktop υπολογιστές. Επικεντρώνεται στην παροχή μιας καθαρής και αξιόπιστης εμπειρίας Jabber/XMPP έχοντας παράλληλα υπόψη την προστασία των προσωπικών δεδομένων σας.</p>
<p xml:lang="de">Dino ist ein modernes, quelloffenes Chat-Programm für den Desktop. Es bietet ein aufgeräumtes und robustes Jabber-/XMPP-Erlebnis und legt einen Schwerpunkt auf Privatsphäre.</p>
<p xml:lang="cs">Dino je moderní open-source chatovací klient pro stolní počítače. Jeho cílem je poskytování čistého a spolehlivého prostředí Jabber/XMPP s důrazem na zachování vašeho soukromí.</p>
<p xml:lang="ca">Dino és un client de xat lliure i modern per a l'escriptori. Està centrat en proveir una experiència neta i fiable de Jabber/XMPP, sempre tenint en compte la vostra privacitat.</p>
@ -83,13 +87,14 @@
<p xml:lang="pt">Suporte criptografia ponta a ponta com OMEMO e OpenPGP e permite configurar privacidade—características relacionadas às notificações de leitura, recebimento e escrita.</p>
<p xml:lang="pl">Obsługuje szyfrowanie od końca do końca za pomocą OMEMO i OpenPGP, a także daje kontrolę nad funkcjami wpływającymi na prywatność, jak powiadomienia o pisaniu czy odczytaniu wiadomości.</p>
<p xml:lang="oc">Compatible amb lo chiframent OMEMO e OpenPGP del cap a la fin e permet de configurar de foncionalitats ligadas amb la confidencialitat coma los acusats de lectura e las notificacions descritura.</p>
<p xml:lang="nl_BE">Ze ondersteunt eind-tot-eind-versleuteling met OMEMO en OpenPGP, en laat u toe privacygerelateerde functies, gelijk leesbevestigingen en typmeldingen, in te stellen.</p>
<p xml:lang="nl">Dino ondersteunt end-to-endversleuteling met OMEMO en OpenPGP en staat je toe privacy-gerelateerde functies, zoals leesbevestigingen en aan-het-typenmeldingen, in te stellen.</p>
<p xml:lang="nb">Det støtter ende-til-ende -kryptering med OMEMO og OpenPGP, og tillater oppsett av personvernsrelaterte funksjoner som meldingskvitteringer og skrivevarsling.</p>
<p xml:lang="lt">Ji palaiko ištisinį šifravimą naudojant OMEMO ir OpenPGP bei leidžia konfigūruoti su privatumu susijusias ypatybes, tokias kaip pranešimus apie žinučių skaitymą ir rašymą.</p>
<p xml:lang="lb">Hien ënnerstëtz Enn-zu-Enn Verschlësselung mat OMEMO an OpenPGP an enthält Privatsphäre-Astellungen zu Liesbestätegungen an Tipp-Benoriichtegungen.</p>
<p xml:lang="ko">OMEMO와 OpenPGP를 통한 종단간 암호화를 지원하며 프라이버시와 관련된 읽음 확인이나 입력 알림기능을 설정할 수 있습니다.</p>
<p xml:lang="ja">OMEMO と OpenPGP を利用したエンドツーエンド暗号化に対応しており、既読状態の送信や入力通知などのプライバシー関連の設定も可能です。</p>
<p xml:lang="it">Support la crittografia end-to-end tramite OMEMO e OpenPGP e permette di configurare le funzioni relative alla privacy come le ricevute di lettura e le notifiche di digitazione.</p>
<p xml:lang="is">Það styður dulkóðun frá enda til enda með OMEMO og OpenPGP og gerir kleift að stilla persónuverndartengda eiginleika eins og lestrarkvittanir og innsláttartilkynningar.</p>
<p xml:lang="ie">It supporta ciffration terminal per OMEMO e OpenPGP e permisse configurar sensitiv functiones quam confirmation de lectada e notificationes de tippada.</p>
<p xml:lang="id">Mendukung enkripsi end-to-end dengan OMEMO dan OpenPGP, dan memungkinkan pengaturan fitur terkait privasi seperti tanda pesan dibaca dan pemberitahuan pengetikan.</p>
<p xml:lang="hu">Támogatja az OMEMO és az OpenPGP használatával történő végpontok közötti titkosítást, és lehetővé teszi a magánszférához kapcsolódó funkciókat, mint például az olvasási visszaigazolást és a gépelési értesítéseket.</p>
@ -98,8 +103,9 @@
<p xml:lang="fi">Se tukee päästä päähän -salausta OMEMO:n ja OpenPGP:n avulla ja mahdollistaa yksityisyyteen liittyvien ominaisuuksien, kuten lukukuittausten ja kirjoitusilmoitusten asetusten määrittämisen.</p>
<p xml:lang="fa">از رمزگذاری سرتاسر با اُمیمو و اُپن‌پی‌جی‌پی پشتیبانی می‌کند و اجازه تنظیم قابلیت‌های مربوط به حریم خصوصی را می‌دهد، از جمله: رسید خوانده‌شدن پیام‌ها و اعلان در حال نوشتن بودن.</p>
<p xml:lang="eu">Amaieratik amaierarako enkriptazioa onartzen du OMEMO eta OpenPGPrekin eta pribatutasun ezaugarriak konfiguratzea baimentzen du irakurtze markak eta idazketa jakinarazpenak bezala.</p>
<p xml:lang="es">Soporta encriptación fin-a-fin a través de OMEMO y OpenPGP y permite configurar las características relacionadas con la privacidad, como confirmaciones de lectura y notificaciones de escritura.</p>
<p xml:lang="es">Soporta cifrado fin-a-fin a través de OMEMO y OpenPGP y permite configurar características relacionadas con la privacidad, como confirmaciones de lectura y notificaciones de escritura.</p>
<p xml:lang="eo">Ĝi subtenas fin-al-finan ĉifradon per OMEMO kaj OpenPGP kaj permesas agordi funkciojn pri privateco kiel kvitancojn de legiteco kaj sciigojn pri tajpado.</p>
<p xml:lang="el">Υποστηρίζει κρυπτογράφηση από άκρο σε άκρο με OMEMO και OpenPGP και επιτρέπει την ρύθμιση λειτουργιών που σχετίζονται με το απόρρητο, όπως αποδείξεις ανάγνωσης και ειδοποιήσεις πληκτρολόγησης.</p>
<p xml:lang="de">Er unterstützt Ende-zu-Ende Verschlüsselung mit OMEMO und OpenPGP und enthält Privatsphäre-Einstellungen zu Lesebestätigungen und Tippbenachrichtigungen.</p>
<p xml:lang="cs">Podporuje šifrování end-to-end pomocí OMEMO a OpenPGP a umožňuje konfigurovat funkce související se soukromím, jako jsou potvrzení o přečtení a oznámení o psaní.</p>
<p xml:lang="ca">Implementa xifratge punt a punt amb OMEMO i OpenPGP, i permet configurar funcionalitats relacionades amb la privacitat com per exemple rebuts de lectura i notificacions d'escriptura.</p>
@ -116,13 +122,14 @@
<p xml:lang="pt">Dino obtém o histórico do servidor e sincroniza mensagens com outros aparelhos.</p>
<p xml:lang="pl">Dino pobiera historię rozmów z serwera i synchronizuje wiadomości z innymi urządzeniami.</p>
<p xml:lang="oc">Dino recupèra listoric del servidor e sincroniza los messatges amb dautres periferics.</p>
<p xml:lang="nl_BE">Dino haalt de geschiedenis op van de server en synchroniseert berichten met andere apparaten.</p>
<p xml:lang="nl">Dino haalt de geschiedenis op van de server en synchroniseert berichten met andere apparaten.</p>
<p xml:lang="nb">Dino henter historikk fra tjeneren og synkroniserer meldinger med andre enheter.</p>
<p xml:lang="lt">Dino gauna istoriją iš serverio ir sinchronizuoja žinutes su kitais įrenginiais.</p>
<p xml:lang="lb">Dino rifft  Gespréichverläf vum Server of a synchroniséiert Noriichte mat anere Geräter.</p>
<p xml:lang="ko">Dino는 서버와 타 장치와의 메세지 동기화로부터 기록을 불러옵니다.</p>
<p xml:lang="ja">Dino はサーバーから履歴を取得し、ほかのデバイスとメッセージを同期します。</p>
<p xml:lang="it">Dino recupera la cronologia dal server e sincronizza i messaggi con gli altri dispositivi.</p>
<p xml:lang="is">Dino sækir feril af netþjóni og samstillir skilaboð með öðrum tækjum.</p>
<p xml:lang="ie">Dino obtene li diarium del servitore e sincronisa missages inter altri apparates.</p>
<p xml:lang="id">Dino mengambil riwayat pesan dari server dan menyinkronkan pesan dengan perangkat lain.</p>
<p xml:lang="hu">A Dino lekéri az előzményeket a kiszolgálóról, és szinkronizálja az üzeneteket a többi eszközzel.</p>
@ -133,6 +140,7 @@
<p xml:lang="eu">Dinok zerbitzaritik hartzen du historia eta beste gailuekin mezuak sinkronizatzen ditu.</p>
<p xml:lang="es">Dino recupera el historial de mensajes desde el servidor y sincroniza los mensajes con otros dispositivos.</p>
<p xml:lang="eo">Dino prenas historion el la servilo kaj sinkronigas mesaĝojn kun aliaj aparatoj.</p>
<p xml:lang="el">Το Dino ανακτά το ιστορικό από τον διακομιστή και συγχρονίζει τα μηνύματα με άλλες συσκευές.</p>
<p xml:lang="de">Dino ruft Gesprächsverläufe vom Server ab und synchronisiert Nachrichten mit anderen Geräten.</p>
<p xml:lang="cs">Dino načítá historii ze serveru a synchronizuje zprávy s ostatními zařízeními.</p>
<p xml:lang="ca">Dino recupera l'historial del servidor i sincronitza els missatges amb altres dispositius.</p>
@ -140,13 +148,13 @@
</description>
<screenshots>
<screenshot type="default">
<image height="952" width="1601">https://dino.im/img/appdata/2022-02_screenshot-main.png</image>
<image>https://dino.im/img/appdata/2022-02_screenshot-main.png</image>
</screenshot>
<screenshot>
<image height="753" width="1199">https://dino.im/img/appdata/2022-02_screenshot-call.png</image>
<image>https://dino.im/img/appdata/2022-02_screenshot-call.png</image>
</screenshot>
<screenshot>
<image height="942" width="1600">https://dino.im/img/appdata/start_chat.png</image>
<image>https://dino.im/img/appdata/start_chat.png</image>
</screenshot>
</screenshots>
<translation type="gettext">dino</translation>
@ -155,21 +163,26 @@
<url type="bugtracker">https://github.com/dino/dino/issues</url>
<url type="donation">https://dino.im/#donate</url>
<url type="translate">https://hosted.weblate.org/projects/dino/</url>
<!-- TODO: Point this to a webpage with documentation when there is one
<url type="help">http://dino.im/help/</url>
-->
<update_contact>appstream@dino.im</update_contact>
<!-- TODO: Write this when Dino is released
<releases>
<release date="XXXX-XX-XX" version="X.X">
<release date="2022-02-12" version="0.3">
<description>
<p>
This is our first release. It comes with a clean UI, notifications,
end-to-end encryption support (OMEMO and GnuPG), multi-user chats,
and more!
</p>
<p>The 0.3 release is all about calls. Dino now supports calls between two or more people!</p>
</description>
</release>
<release date="2020-11-12" version="0.2">
<description>
<p>The 0.2 release adds message correction, improves the file upload functionality and provides more information about message encryption.</p>
</description>
</release>
<release date="2020-01-29" version="0.1">
<description>
<p>The first Dino release! Dino is a secure and open-source application for decentralized messaging.</p>
</description>
</release>
</releases>
-->
<content_rating type="oars-1.1">
<content_attribute id="social-chat">intense</content_attribute>
<content_attribute id="social-audio">intense</content_attribute>
</content_rating>
</component>

View file

@ -13,13 +13,13 @@
</description>
<screenshots>
<screenshot type="default">
<image height="952" width="1601">https://dino.im/img/appdata/2022-02_screenshot-main.png</image>
<image>https://dino.im/img/appdata/2022-02_screenshot-main.png</image>
</screenshot>
<screenshot>
<image height="753" width="1199">https://dino.im/img/appdata/2022-02_screenshot-call.png</image>
<image>https://dino.im/img/appdata/2022-02_screenshot-call.png</image>
</screenshot>
<screenshot>
<image height="942" width="1600">https://dino.im/img/appdata/start_chat.png</image>
<image>https://dino.im/img/appdata/start_chat.png</image>
</screenshot>
</screenshots>
<translation type="gettext">dino</translation>
@ -28,22 +28,26 @@
<url type="bugtracker">https://github.com/dino/dino/issues</url>
<url type="donation">https://dino.im/#donate</url>
<url type="translate">https://hosted.weblate.org/projects/dino/</url>
<!-- TODO: Point this to a webpage with documentation when there is one
<url type="help">http://dino.im/help/</url>
-->
<update_contact>appstream@dino.im</update_contact>
<!-- TODO: Write this when Dino is released
<releases>
<release date="XXXX-XX-XX" version="X.X">
<release date="2022-02-12" version="0.3">
<description>
<p>
This is our first release. It comes with a clean UI, notifications,
end-to-end encryption support (OMEMO and GnuPG), multi-user chats,
and more!
</p>
<p>The 0.3 release is all about calls. Dino now supports calls between two or more people!</p>
</description>
</release>
<release date="2020-11-12" version="0.2">
<description>
<p>The 0.2 release adds message correction, improves the file upload functionality and provides more information about message encryption.</p>
</description>
</release>
<release date="2020-01-29" version="0.1">
<description>
<p>The first Dino release! Dino is a secure and open-source application for decentralized messaging.</p>
</description>
</release>
</releases>
-->
<content_rating type="oars-1.1">
<content_attribute id="social-chat">intense</content_attribute>
<content_attribute id="social-audio">intense</content_attribute>
</content_rating>
</component>

3
main/data/style-dark.css Normal file
View file

@ -0,0 +1,3 @@
.dino-main .overlay-toolbar {
background-color: shade(@view_bg_color, 1.5);
}

View file

@ -3,6 +3,10 @@
* It provides sane defaults for things that are very Dino-specific.
*/
statuspage {
opacity: 0.5;
}
window.dino-main .dino-header-right {
background: @theme_base_color;
}
@ -39,7 +43,7 @@ window.dino-main .dino-conversation .highlight-once {
}
window.dino-main .dino-conversation .message-box.highlight {
background: alpha(@theme_fg_color, 0.04);
background: @window_bg_color;
}
window.dino-main .dino-conversation .message-box {
@ -88,11 +92,6 @@ window.dino-main .dino-sidebar > frame {
transition: background .05s ease;
}
window.dino-main .circular-button {
padding: 0;
border-radius: 1000px;
}
window.dino-main .dino-conversation .message-box.edit-mode {
background: alpha(@theme_selected_bg_color, 0.1);
}
@ -120,20 +119,20 @@ window.dino-main .dino-quote:hover {
background: alpha(@theme_fg_color, 0.08);
}
/* Message Menu */
/* Overlay Toolbar */
.message-menu-box {
background-color: @theme_base_color;
border: 1px solid alpha(@theme_fg_color, 0.15);
border-radius: 5px;
.dino-main .overlay-toolbar {
padding: 2px;
border-radius: 6px;
border-spacing: 0;
}
.message-menu-button {
padding: 6px;
border: none;
.dino-main .overlay-toolbar > * {
margin-top: 0;
margin-bottom: 0;
}
/* Fie Widget */
/* File Widget */
window.dino-main .file-box-outer,
window.dino-main .call-box-outer {
@ -149,7 +148,7 @@ window.dino-main .call-box {
window.dino-main .file-image-widget {
border: 1px solid alpha(@theme_fg_color, 0.1);
border-radius: 3px;
border-radius: 6px;
}
window.dino-main .file-image-widget .file-box-outer {
@ -168,6 +167,10 @@ window.dino-main .file-image-widget .file-box-outer button:hover {
background: rgba(100, 100, 100, 0.5);
}
.dino-main .file-image-widget picture {
border-radius: 6px;
}
/* Call widget */
window.dino-main .call-box-outer.incoming {
@ -185,38 +188,23 @@ window.dino-main .multiparty-participants {
/* Reactions */
window.dino-main menubutton.reaction-box image {
margin-left: 5px;
margin-right: 5px;
.dino-main .reaction-grid button {
min-height: 16px;
min-width: 30px;
padding: 4px;
}
window.dino-main button.reaction-box,
window.dino-main menubutton.reaction-box > button {
border: 1px solid transparent;
padding: 3px 5px ;
border-radius: 10px;
background-color: alpha(@theme_fg_color, 0.07);
background-image: none;
box-shadow: none;
min-height: 0;
min-width: 0;
.dino-main .reaction-grid button.own-reaction,
.dino-main .reaction-grid .own-reaction button {
background-color: alpha(@accent_bg_color, 0.1);
border: 1px solid @accent_bg_color;
padding: 3px;
color: @accent_color;
}
window.dino-main button.reaction-box.own-reaction,
window.dino-main menubutton.reaction-box.own-reaction > button {
color: mix(@theme_selected_bg_color, @theme_fg_color, 0.4);
border-color: @theme_selected_bg_color;
background-color: alpha(@theme_selected_bg_color, 0.05);
}
window.dino-main button.reaction-box:hover,
window.dino-main menubutton.reaction-box:hover > button {
background-color: alpha(@theme_fg_color, 0.1);
}
window.dino-main button.reaction-box.own-reaction:hover,
window.dino-main menubutton.reaction-box.own-reaction > button {
background-color: alpha(@theme_selected_bg_color, 0.2);
.dino-main .reaction-grid button.own-reaction:hover,
.dino-main .reaction-grid .own-reaction button:hover {
background-color: alpha(@accent_bg_color, 0.2);
}
/* Sidebar */

View file

@ -1,74 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk" version="4.0"/>
<object class="GtkPaned" id="paned">
<property name="shrink-start-child">False</property>
<property name="shrink-end-child">False</property>
<property name="resize-start-child">False</property>
<property name="position">300</property>
<object class="AdwLeaflet" id="leaflet">
<property name="transition-type">slide</property>
<property name="can-navigate-back">true</property>
<property name="can-navigate-forward">true</property>
<child>
<object class="GtkStack" id="left_stack">
<object class="GtkBox" id="left_box">
<property name="orientation">vertical</property>
<child>
<object class="GtkStackPage">
<property name="name">content</property>
<property name="child">
<object class="GtkScrolledWindow">
<property name="hscrollbar_policy">never</property>
<child>
<object class="DinoUiConversationSelector" id="conversation_list">
</object>
</child>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">placeholder</property>
<property name="child">
<object class="GtkBox">
<property name="margin-start">20</property>
<property name="margin-end">20</property>
<property name="margin-top">20</property>
<property name="margin-bottom">20</property>
<property name="spacing">10</property>
<property name="valign">start</property>
<property name="halign">start</property>
<child>
<object class="GtkImage" id="conversation_list_placeholder_image">
<property name="valign">start</property>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="wrap">1</property>
<property name="margin-top">70</property>
<property name="margin-end">50</property>
<property name="valign">end</property>
<property name="label" translatable="1">Click here to start a conversation or join a channel.</property>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
</object>
</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkOverlay">
<property name="child">
<object class="GtkStack" id="right_stack">
<object class="GtkStack" id="left_stack">
<property name="hexpand">False</property>
<child>
<object class="GtkStackPage">
<property name="name">content</property>
<property name="child">
<object class="DinoUiConversationView" id="conversation_view">
<object class="GtkScrolledWindow">
<property name="hscrollbar_policy">never</property>
<property name="vexpand">1</property>
<child>
<object class="DinoUiConversationSelector" id="conversation_list">
</object>
</child>
</object>
</property>
</object>
@ -77,54 +30,89 @@
<object class="GtkStackPage">
<property name="name">placeholder</property>
<property name="child">
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="hexpand">1</property>
<property name="vexpand">1</property>
<property name="halign">center</property>
<property name="valign">center</property>
<child>
<object class="GtkImage">
<property name="icon-name">im.dino.Dino-symbolic</property>
<property name="pixel-size">144</property>
<property name="margin-bottom">30</property>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label" translatable="1">You have no open chats</property>
<style>
<class name="dim-label"/>
</style>
<attributes>
<attribute name="scale" value="1.2"></attribute>
</attributes>
</object>
</child>
<object class="AdwStatusPage">
<style>
<class name="compact"/>
</style>
<property name="margin-start">20</property>
<property name="margin-end">20</property>
<property name="margin-top">20</property>
<property name="margin-bottom">20</property>
<property name="width_request">260</property>
<property name="title" translatable="yes">You have no open chats</property>
<property name="description" translatable="yes">Click + to start a chat or join a channel</property>
</object>
</property>
</object>
</child>
</object>
</property>
<child type="overlay">
<object class="GtkRevealer" id="search_revealer">
<property name="halign">end</property>
<property name="transition-type">slide-left</property>
<style>
<class name="dino-sidebar"/>
</style>
<property name="child">
<object class="GtkFrame" id="search_frame">
<property name="width-request">400</property>
</child>
</object>
</child>
<child>
<object class="GtkBox" id="right_box">
<property name="orientation">vertical</property>
<child>
<object class="AdwFlap" id="search_flap">
<property name="flap-position">end</property>
<property name="modal">true</property>
<property name="locked">true</property>
<property name="swipe-to-open">false</property>
<property name="fold-threshold-policy">natural</property>
<property name="hexpand">true</property>
<child type="content">
<object class="DinoUiNaturalSizeIncrease">
<property name="min-natural-width">600</property>
<child>
<object class="GtkStack" id="right_stack">
<property name="hexpand">false</property>
<child>
<object class="GtkStackPage">
<property name="name">content</property>
<property name="child">
<object class="DinoUiConversationView" id="conversation_view">
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">placeholder</property>
<property name="child">
<object class="AdwStatusPage">
<property name="icon-name">im.dino.Dino-symbolic</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
</object>
</property>
</object>
</child>
</object>
</child>
</object>
</property>
</child>
<child type="separator">
<object class="GtkSeparator" />
</child>
<child type="flap">
<object class="AdwClamp">
<property name="hexpand">false</property>
<property name="maximum-size">400</property>
<property name="tightening-threshold">400</property>
<child>
<object class="AdwBin" id="search_frame">
<property name="hexpand">true</property>
<style>
<class name="background"/>
</style>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</interface>
</interface>

View file

@ -2,63 +2,47 @@
<interface>
<requires lib="gtk" version="4.0"/>
<template class="DinoUiMainWindowPlaceholder">
<property name="valign">center</property>
<child>
<object class="GtkBox" id="box">
<property name="orientation">vertical</property>
<property name="valign">center</property>
<property name="halign">center</property>
<property name="hexpand">1</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<child>
<object class="GtkImage">
<property name="icon-name">im.dino.Dino-symbolic</property>
<property name="pixel-size">144</property>
<property name="margin-start">30</property>
<property name="margin-end">30</property>
<property name="margin-top">30</property>
<property name="margin-bottom">30</property>
<style>
<class name="dim-label"/>
</style>
<object class="GtkHeaderBar">
<property name="show-title-buttons">true</property>
</object>
</child>
<child>
<object class="GtkLabel" id="title_label">
<property name="visible">0</property>
<style>
<class name="dim-label"/>
</style>
<attributes>
<attribute name="scale" value="1.3"></attribute>
</attributes>
</object>
</child>
<child>
<object class="GtkLabel" id="label">
<property name="margin-top">5</property>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
<child>
<object class="GtkButton" id="primary_button">
<property name="margin-top">15</property>
<property name="halign">center</property>
<style>
<class name="text-button"/>
<class name="suggested-action"/>
</style>
</object>
</child>
<child>
<object class="GtkButton" id="secondary_button">
<property name="visible">0</property>
<property name="halign">center</property>
<!-- <property name="no_show_all">True</property>-->
<style>
<class name="text-button"/>
</style>
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="valign">center</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<child>
<object class="AdwStatusPage" id="status_page">
<property name="icon-name">im.dino.Dino-symbolic</property>
</object>
</child>
<child>
<object class="GtkButton" id="primary_button">
<property name="halign">center</property>
<property name="visible">False</property>
<style>
<class name="text-button"/>
<class name="suggested-action"/>
</style>
</object>
</child>
<child>
<object class="GtkButton" id="secondary_button">
<property name="visible">0</property>
<property name="halign">center</property>
<property name="visible">False</property>
<style>
<class name="text-button"/>
</style>
</object>
</child>
</object>
</child>
</object>

View file

@ -15,6 +15,7 @@ gl
hu
id
ie
is
it
ja
kab
@ -23,7 +24,6 @@ lb
lt
nb
nl
nl_BE
oc
pl
pt

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

1149
main/po/is.po Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -14,7 +14,7 @@ protected class RosterList {
private ulong[] handler_ids = new ulong[0];
private ListBox list_box = new ListBox();
private HashMap<Account, HashMap<Jid, ListRow>> rows = new HashMap<Account, HashMap<Jid, ListRow>>(Account.hash_func, Account.equals_func);
private HashMap<Account, HashMap<Jid, ListBoxRow>> rows = new HashMap<Account, HashMap<Jid, ListBoxRow>>(Account.hash_func, Account.equals_func);
public RosterList(StreamInteractor stream_interactor, Gee.List<Account> accounts) {
this.stream_interactor = stream_interactor;
@ -47,14 +47,15 @@ protected class RosterList {
private void on_updated_roster_item(Account account, Jid jid, Roster.Item roster_item) {
on_removed_roster_item(account, jid, roster_item);
ListRow row = new ListRow.from_jid(stream_interactor, roster_item.jid, account, accounts.size > 1);
rows[account][jid] = row;
list_box.append(row);
ListBoxRow list_box_row = new ListBoxRow() { child=row };
rows[account][jid] = list_box_row;
list_box.append(list_box_row);
list_box.invalidate_sort();
list_box.invalidate_filter();
}
private void fetch_roster_items(Account account) {
rows[account] = new HashMap<Jid, ListRow>(Jid.hash_func, Jid.equals_func);
rows[account] = new HashMap<Jid, ListBoxRow>(Jid.hash_func, Jid.equals_func);
foreach (Roster.Item roster_item in stream_interactor.get_module(RosterManager.IDENTITY).get_roster(account)) {
on_updated_roster_item(account, roster_item.jid, roster_item);
}

View file

@ -80,7 +80,7 @@ public class SelectContactDialog : Gtk.Dialog {
add_contact_dialog.present();
});
select_jid_fragment.remove_jid.connect((row) => {
ListRow list_row = roster_list_box.get_selected_row() as ListRow;
ListRow list_row = roster_list_box.get_selected_row().child as ListRow;
stream_interactor.get_module(RosterManager.IDENTITY).remove_jid(list_row.account, list_row.jid);
});
select_jid_fragment.notify["done"].connect(() => {

View file

@ -4,7 +4,7 @@ using Dino.Entities;
using Dino.Ui;
using Xmpp;
public class Dino.Ui.Application : Gtk.Application, Dino.Application {
public class Dino.Ui.Application : Adw.Application, Dino.Application {
private const string[] KEY_COMBINATION_QUIT = {"<Ctrl>Q", null};
private const string[] KEY_COMBINATION_ADD_CHAT = {"<Ctrl>T", null};
private const string[] KEY_COMBINATION_ADD_CONFERENCE = {"<Ctrl>G", null};
@ -33,10 +33,6 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application {
Environment.set_application_name("Dino");
Window.set_default_icon_name("im.dino.Dino");
CssProvider provider = new CssProvider();
provider.load_from_resource("/im/dino/Dino/theme.css");
StyleContext.add_provider_for_display(Gdk.Display.get_default(), provider, STYLE_PROVIDER_PRIORITY_APPLICATION);
create_actions();
add_main_option_entries(options);
@ -272,25 +268,30 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application {
case "0.3": version = @"$version - <span font_style='italic'>Theikenmeer</span>"; break;
}
}
Gtk.AboutDialog dialog = new Gtk.AboutDialog();
dialog.destroy_with_parent = true;
dialog.transient_for = window;
dialog.modal = true;
dialog.title = _("About Dino");
dialog.logo_icon_name = "im.dino.Dino";
dialog.program_name = "Dino";
dialog.version = version;
dialog.comments = "Dino. Communicating happiness.";
dialog.website = "https://dino.im/";
dialog.website_label = "dino.im";
dialog.copyright = "Copyright © 2016-2022 - Dino Team";
dialog.license_type = License.GPL_3_0;
#if Adw_1_2
Adw.AboutWindow about_window = new Adw.AboutWindow();
about_window.application_icon = "im.dino.Dino";
about_window.application_name = "Dino";
#else
Gtk.AboutDialog about_window = new Gtk.AboutDialog();
about_window.logo_icon_name = "im.dino.Dino";
about_window.program_name = "Dino";
about_window.website_label = "dino.im";
#endif
about_window.destroy_with_parent = true;
about_window.transient_for = window;
about_window.modal = true;
about_window.title = _("About Dino");
about_window.version = version;
about_window.comments = "Dino. Communicating happiness.";
about_window.website = "https://dino.im/";
about_window.copyright = "Copyright © 2016-2022 - Dino Team";
about_window.license_type = License.GPL_3_0;
if (!use_csd()) {
dialog.set_titlebar(null);
about_window.set_titlebar(null);
}
dialog.present();
about_window.present();
}
private void show_join_muc_dialog(Account? account, string jid) {

View file

@ -74,7 +74,7 @@ public class ChatInputController : Object {
quoted_content_item = content_item;
var quote_model = new Quote.Model.from_content_item(content_item, conversation, stream_interactor) { can_abort = true };
quote_model.aborted.connect(() => {
content_item = null;
quoted_content_item = null;
chat_input.unset_quoted_message();
});
chat_input.set_quoted_message(Quote.get_widget(quote_model));
@ -83,8 +83,8 @@ public class ChatInputController : Object {
}
public void set_conversation(Conversation conversation) {
this.quoted_content_item = null;
reset_input_field_status();
this.quoted_content_item = null;
chat_input.unset_quoted_message();
this.conversation = conversation;

View file

@ -49,6 +49,12 @@ public class SettingsProvider : Plugins.ContactDetailsProvider, Object {
combobox.active_id = get_notify_setting_id(conversation.notify_setting);
combobox.changed.connect(() => { conversation.notify_setting = get_notify_setting(combobox.active_id); } );
}
Switch pinned_switch = new Switch() { valign=Align.CENTER };
string category = conversation.type_ == Conversation.Type.GROUPCHAT ? DETAILS_HEADLINE_ROOM : DETAILS_HEADLINE_CHAT;
contact_details.add(category, _("Pin conversation"), _("Pins the conversation to the top of the conversation list"), pinned_switch);
pinned_switch.state = conversation.pinned != 0;
pinned_switch.state_set.connect((state) => { conversation.pinned = state ? 1 : 0; return false; });
}
private Conversation.Setting get_setting(string id) {

View file

@ -37,7 +37,7 @@ public class ConversationItemSkeleton : Plugins.ConversationItemWidgetInterface,
private uint time_update_timeout = 0;
private ulong updated_roster_handler_id = 0;
public ConversationItemSkeleton(StreamInteractor stream_interactor, Conversation conversation, Plugins.MetaConversationItem item, bool initial_item) {
public ConversationItemSkeleton(StreamInteractor stream_interactor, Conversation conversation, Plugins.MetaConversationItem item) {
this.stream_interactor = stream_interactor;
this.conversation = conversation;
this.item = item;

View file

@ -9,6 +9,7 @@ namespace Dino.Ui.ConversationSummary {
[GtkTemplate (ui = "/im/dino/Dino/conversation_content_view/view.ui")]
public class ConversationView : Widget, Plugins.ConversationItemCollection, Plugins.NotificationCollection {
private const int MESSAGE_MENU_BOX_OFFSET = -20;
public Conversation? conversation { get; private set; }
@ -23,7 +24,7 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
private Gee.List<Dino.Plugins.MessageAction>? message_actions = null;
private StreamInteractor stream_interactor;
private Gee.TreeSet<Plugins.MetaConversationItem> content_items = new Gee.TreeSet<Plugins.MetaConversationItem>(compare_meta_items);
private Gee.TreeSet<ContentMetaItem> content_items = new Gee.TreeSet<ContentMetaItem>(compare_content_meta_items);
private Gee.TreeSet<Plugins.MetaConversationItem> meta_items = new TreeSet<Plugins.MetaConversationItem>(compare_meta_items);
private Gee.HashMap<Plugins.MetaConversationItem, ConversationItemSkeleton> item_item_skeletons = new Gee.HashMap<Plugins.MetaConversationItem, ConversationItemSkeleton>();
private Gee.HashMap<Plugins.MetaConversationItem, Widget> widgets = new Gee.HashMap<Plugins.MetaConversationItem, Widget>();
@ -36,7 +37,6 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
private double? was_page_size;
private Mutex reloading_mutex = Mutex();
private bool animate = false;
private bool firstLoad = true;
private bool at_current_content = true;
private bool reload_messages = true;
@ -81,6 +81,15 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
main.add_controller(main_motion_events);
main_motion_events.motion.connect(update_highlight);
// Process touch events and capture phase to allow highlighting a message without cursor
GestureClick click_controller = new GestureClick();
click_controller.touch_only = true;
click_controller.propagation_phase = Gtk.PropagationPhase.CAPTURE;
main_wrap_box.add_controller(click_controller);
click_controller.pressed.connect_after((n, x, y) => {
update_highlight(x, y);
});
return this;
}
@ -173,7 +182,7 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
currently_highlighted.add_css_class("highlight");
// Move message menu
message_menu_box.margin_top = (int)(widget_y - 10);
message_menu_box.margin_top = (int)(widget_y + MESSAGE_MENU_BOX_OFFSET);
}
}
@ -199,6 +208,7 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
MenuButton button = new MenuButton();
button.icon_name = message_actions[i].icon_name;
button.set_popover(message_actions[i].popover as Popover);
button.tooltip_text = Util.string_if_tooltips_active(message_actions[i].tooltip);
action_buttons.add(button);
}
@ -209,6 +219,7 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
button.clicked.connect(() => {
message_action.callback(button, current_meta_item, currently_highlighted);
});
button.tooltip_text = Util.string_if_tooltips_active(message_actions[i].tooltip);
action_buttons.add(button);
}
}
@ -231,12 +242,71 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
});
firstLoad = false;
}
if (conversation == this.conversation && at_current_content) {
// Just make sure we are scrolled down
if (scrolled.vadjustment.value != scrolled.vadjustment.upper) {
scroll_animation(scrolled.vadjustment.upper).play();
}
return;
}
clear();
initialize_for_conversation_(conversation);
display_latest();
at_current_content = true;
// Scroll to end
scrolled.vadjustment.value = scrolled.vadjustment.upper;
}
private void scroll_and_highlight_item(Plugins.MetaConversationItem target, uint duration = 500) {
Widget widget = null;
int h = 0;
foreach (Plugins.MetaConversationItem item in meta_items) {
widget = widgets[item];
if (target == item) {
break;
}
h += widget.get_allocated_height();
}
if (widget != widgets[target]) {
warning("Target item widget not reached");
return;
}
double target_height = h - scrolled.vadjustment.page_size * 1/3;
Adw.Animation animation = scroll_animation(target_height);
animation.done.connect(() => {
widget.remove_css_class("highlight-once");
widget.add_css_class("highlight-once");
Timeout.add(5000, () => {
widget.remove_css_class("highlight-once");
return false;
});
});
animation.play();
}
private Adw.Animation scroll_animation(double target) {
#if ADW_1_2
return new Adw.TimedAnimation(scrolled, scrolled.vadjustment.value, target, 500,
new Adw.PropertyAnimationTarget(scrolled.vadjustment, "value")
);
#else
return new Adw.TimedAnimation(scrolled, scrolled.vadjustment.value, target, 500,
new Adw.CallbackAnimationTarget(value => {
scrolled.vadjustment.value = value;
})
);
#endif
}
public void initialize_around_message(Conversation conversation, ContentItem content_item) {
if (conversation == this.conversation) {
ContentMetaItem? matching_item = content_items.first_match(it => it.content_item.id == content_item.id);
if (matching_item != null) {
scroll_and_highlight_item(matching_item);
return;
}
}
clear();
initialize_for_conversation_(conversation);
Gee.List<ContentMetaItem> before_items = content_populator.populate_before(conversation, content_item, 40);
@ -244,7 +314,6 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
do_insert_item(item);
}
ContentMetaItem meta_item = content_populator.get_content_meta_item(content_item);
meta_item.can_merge = false;
Widget w = insert_new(meta_item);
content_items.add(meta_item);
meta_items.add(meta_item);
@ -260,23 +329,16 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
// Compute where to jump to for centered message, jump, highlight.
reload_messages = false;
Timeout.add(700, () => {
int h = 0, i = 0;
foreach (Plugins.MetaConversationItem item in meta_items) {
Widget widget = widgets[item];
if (widget == w) {
break;
}
h += widget.get_allocated_height();
i++;
}
scrolled.vadjustment.value = h - scrolled.vadjustment.page_size * 1/3;
w.add_css_class("highlight-once");
scroll_and_highlight_item(meta_item, 300);
reload_messages = true;
return false;
});
}
private void initialize_for_conversation_(Conversation? conversation) {
if (this.conversation == conversation) {
print("Re-initialized for %s\n", conversation.counterpart.bare_jid.to_string());
}
// Deinitialize old conversation
Dino.Application app = Dino.Application.get_default();
if (this.conversation != null) {
@ -298,9 +360,6 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
}
content_populator.init(this, conversation, Plugins.WidgetType.GTK4);
subscription_notification.init(conversation, this);
animate = false;
Timeout.add(20, () => { animate = true; return false; });
}
private void display_latest() {
@ -330,8 +389,8 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
public void do_insert_item(Plugins.MetaConversationItem item) {
lock (meta_items) {
insert_new(item);
if (item as ContentMetaItem != null) {
content_items.add(item);
if (item is ContentMetaItem) {
content_items.add((ContentMetaItem)item);
}
meta_items.add(item);
}
@ -347,7 +406,9 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
widget_order.remove(skeleton.get_widget());
item_item_skeletons.unset(item);
content_items.remove(item);
if (item is ContentMetaItem) {
content_items.remove((ContentMetaItem)item);
}
meta_items.remove(item);
}
@ -386,7 +447,7 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
Plugins.MetaConversationItem? lower_item = meta_items.lower(item);
// Fill datastructure
ConversationItemSkeleton item_skeleton = new ConversationItemSkeleton(stream_interactor, conversation, item, !animate);
ConversationItemSkeleton item_skeleton = new ConversationItemSkeleton(stream_interactor, conversation, item);
item_item_skeletons[item] = item_skeleton;
int index = lower_item != null ? widget_order.index_of(item_item_skeletons[lower_item].get_widget()) + 1 : 0;
widget_order.insert(index, item_skeleton.get_widget());
@ -502,6 +563,10 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
}
}
private static int compare_content_meta_items(ContentMetaItem a, ContentMetaItem b) {
return compare_meta_items(a, b);
}
private static int compare_meta_items(Plugins.MetaConversationItem a, Plugins.MetaConversationItem b) {
int cmp1 = a.time.compare(b.time);
if (cmp1 != 0) return cmp1;

View file

@ -10,9 +10,6 @@ namespace Dino.Ui {
public class FileDefaultWidget : Box {
public signal void clicked();
public signal void open_file();
public signal void save_file_as();
public signal void cancel_download();
[GtkChild] public unowned Stack image_stack;
[GtkChild] public unowned Label name_label;
@ -23,12 +20,6 @@ public class FileDefaultWidget : Box {
private FileTransfer.State state;
class construct {
install_action("file.open", null, (widget, action_name) => { ((FileDefaultWidget) widget).open_file(); });
install_action("file.save_as", null, (widget, action_name) => { ((FileDefaultWidget) widget).save_file_as(); });
install_action("file.cancel", null, (widget, action_name) => { ((FileDefaultWidget) widget).cancel_download(); });
}
public FileDefaultWidget() {
EventControllerMotion this_motion_events = new EventControllerMotion();
this.add_controller(this_motion_events);

View file

@ -8,64 +8,70 @@ namespace Dino.Ui {
public class FileImageWidget : Box {
private ScalingImage image;
FileDefaultWidget file_default_widget;
FileDefaultWidgetController file_default_widget_controller;
public FileImageWidget() {
this.halign = Align.START;
this.add_css_class("file-image-widget");
this.set_cursor_from_name("zoom-in");
}
public async void load_from_file(File file, string file_name, int MAX_WIDTH=600, int MAX_HEIGHT=300) throws GLib.Error {
// Load and prepare image in tread
Thread<ScalingImage?> thread = new Thread<ScalingImage?> (null, () => {
ScalingImage image = new ScalingImage() { halign=Align.START, visible = true, max_width = MAX_WIDTH, max_height = MAX_HEIGHT };
Gtk.Box image_overlay_toolbar = new Gtk.Box(Orientation.HORIZONTAL, 0) { halign=Gtk.Align.END, valign=Gtk.Align.START, margin_top=10, margin_start=10, margin_end=10, margin_bottom=10, vexpand=false, visible=false };
image_overlay_toolbar.add_css_class("card");
image_overlay_toolbar.add_css_class("toolbar");
image_overlay_toolbar.add_css_class("overlay-toolbar");
image_overlay_toolbar.set_cursor_from_name("default");
Gdk.Pixbuf pixbuf;
try {
pixbuf = new Gdk.Pixbuf.from_file(file.get_path());
} catch (Error error) {
warning("Can't load picture %s - %s", file.get_path(), error.message);
Idle.add(load_from_file.callback);
return null;
FixedRatioPicture image = new FixedRatioPicture() { min_width=100, min_height=100, max_width=MAX_WIDTH, max_height=MAX_HEIGHT, file=file };
GestureClick gesture_click_controller = new GestureClick();
gesture_click_controller.button = 1; // listen for left clicks
gesture_click_controller.released.connect((n_press, x, y) => {
switch (gesture_click_controller.get_device().source) {
case Gdk.InputSource.TOUCHSCREEN:
case Gdk.InputSource.PEN:
if (n_press == 1) {
image_overlay_toolbar.visible = !image_overlay_toolbar.visible;
} else if (n_press == 2) {
this.activate_action("file.open", null);
image_overlay_toolbar.visible = false;
}
break;
default:
this.activate_action("file.open", null);
image_overlay_toolbar.visible = false;
break;
}
pixbuf = pixbuf.apply_embedded_orientation();
image.load(pixbuf);
Idle.add(load_from_file.callback);
return image;
});
yield;
image = thread.join();
if (image == null) throw new Error(-1, 0, "Error loading image");
image.add_controller(gesture_click_controller);
FileInfo file_info = file.query_info("*", FileQueryInfoFlags.NONE);
string? mime_type = Dino.Util.get_content_type(file_info);
file_default_widget = new FileDefaultWidget() { valign=Align.END, vexpand=false, visible=false };
file_default_widget.image_stack.visible = false;
file_default_widget_controller = new FileDefaultWidgetController(file_default_widget);
file_default_widget_controller.set_file(file, file_name, mime_type);
MenuButton button = new MenuButton();
button.icon_name = "open-menu";
Menu menu_model = new Menu();
menu_model.append(_("Open"), "file.open");
menu_model.append(_("Save as…"), "file.save_as");
Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu.from_model(menu_model);
button.popover = popover_menu;
image_overlay_toolbar.append(button);
Overlay overlay = new Overlay();
overlay.set_child(image);
overlay.add_overlay(file_default_widget);
overlay.add_overlay(image_overlay_toolbar);
overlay.set_measure_overlay(image, true);
overlay.set_clip_overlay(file_default_widget, true);
overlay.set_clip_overlay(image_overlay_toolbar, true);
EventControllerMotion this_motion_events = new EventControllerMotion();
this.add_controller(this_motion_events);
this_motion_events.enter.connect(() => {
file_default_widget.visible = true;
image_overlay_toolbar.visible = true;
});
this_motion_events.leave.connect(() => {
if (file_default_widget.file_menu.popover != null && file_default_widget.file_menu.popover.visible) return;
if (button.popover != null && button.popover.visible) return;
file_default_widget.visible = false;
image_overlay_toolbar.visible = false;
});
this.append(overlay);

View file

@ -10,19 +10,46 @@ namespace Dino.Ui {
public class FileMetaItem : ConversationSummary.ContentMetaItem {
private StreamInteractor stream_interactor;
private FileItem file_item;
private FileTransfer file_transfer;
public FileMetaItem(ContentItem content_item, StreamInteractor stream_interactor) {
base(content_item);
this.stream_interactor = stream_interactor;
this.file_item = content_item as FileItem;
this.file_transfer = file_item.file_transfer;
}
public override Object? get_widget(Plugins.ConversationItemWidgetInterface outer, Plugins.WidgetType type) {
FileItem file_item = content_item as FileItem;
FileTransfer transfer = file_item.file_transfer;
return new FileWidget(stream_interactor, transfer);
FileWidget widget = new FileWidget(file_transfer);
FileWidgetController widget_controller = new FileWidgetController(widget, file_transfer, stream_interactor);
return widget;
}
public override Gee.List<Plugins.MessageAction>? get_item_actions(Plugins.WidgetType type) { return null; }
public override Gee.List<Plugins.MessageAction>? get_item_actions(Plugins.WidgetType type) {
if (file_transfer.provider != 0 || file_transfer.info == null) return null;
Gee.List<Plugins.MessageAction> actions = new ArrayList<Plugins.MessageAction>();
if (stream_interactor.get_module(ContentItemStore.IDENTITY).get_message_id_for_content_item(file_item.conversation, content_item) != null) {
Plugins.MessageAction reply_action = new Plugins.MessageAction();
reply_action.icon_name = "mail-reply-sender-symbolic";
reply_action.callback = (button, content_meta_item_activated, widget) => {
GLib.Application.get_default().activate_action("quote", new GLib.Variant.tuple(new GLib.Variant[] { new GLib.Variant.int32(file_item.conversation.id), new GLib.Variant.int32(content_item.id) }));
};
actions.add(reply_action);
Plugins.MessageAction action2 = new Plugins.MessageAction();
action2.icon_name = "dino-emoticon-add-symbolic";
EmojiChooser chooser = new EmojiChooser();
chooser.emoji_picked.connect((emoji) => {
stream_interactor.get_module(Reactions.IDENTITY).add_reaction(file_item.conversation, content_item, emoji);
});
action2.popover = chooser;
actions.add(action2);
}
return actions;
}
}
public class FileWidget : SizeRequestBox {
@ -32,7 +59,6 @@ public class FileWidget : SizeRequestBox {
DEFAULT
}
private StreamInteractor stream_interactor;
private FileTransfer file_transfer;
public FileTransfer.State file_transfer_state { get; set; }
public string file_transfer_mime_type { get; set; }
@ -41,13 +67,24 @@ public class FileWidget : SizeRequestBox {
private FileDefaultWidgetController default_widget_controller;
private Widget? content = null;
public signal void open_file();
public signal void save_file_as();
public signal void start_download();
public signal void cancel_download();
class construct {
install_action("file.open", null, (widget, action_name) => { ((FileWidget) widget).open_file(); });
install_action("file.save_as", null, (widget, action_name) => { ((FileWidget) widget).save_file_as(); });
install_action("file.download", null, (widget, action_name) => { ((FileWidget) widget).start_download(); });
install_action("file.cancel", null, (widget, action_name) => { ((FileWidget) widget).cancel_download(); });
}
construct {
margin_top = 4;
size_request_mode = SizeRequestMode.HEIGHT_FOR_WIDTH;
}
public FileWidget(StreamInteractor stream_interactor, FileTransfer file_transfer) {
this.stream_interactor = stream_interactor;
public FileWidget(FileTransfer file_transfer) {
this.file_transfer = file_transfer;
update_widget.begin();
@ -88,7 +125,7 @@ public class FileWidget : SizeRequestBox {
if (content != null) this.remove(content);
FileDefaultWidget default_file_widget = new FileDefaultWidget();
default_widget_controller = new FileDefaultWidgetController(default_file_widget);
default_widget_controller.set_file_transfer(file_transfer, stream_interactor);
default_widget_controller.set_file_transfer(file_transfer);
content = default_file_widget;
this.state = State.DEFAULT;
this.append(content);
@ -113,94 +150,104 @@ public class FileWidget : SizeRequestBox {
}
}
public class FileDefaultWidgetController : Object {
private FileDefaultWidget widget;
private FileTransfer? file_transfer;
public string file_transfer_path { get; set; }
public string file_transfer_state { get; set; }
public string file_transfer_mime_type { get; set; }
public class FileWidgetController : Object {
private weak Widget widget;
private FileTransfer file_transfer;
private StreamInteractor? stream_interactor;
private string file_uri;
private string file_name;
private FileTransfer.State state;
public FileDefaultWidgetController(FileDefaultWidget widget) {
public FileWidgetController(FileWidget widget, FileTransfer file_transfer, StreamInteractor? stream_interactor = null) {
this.widget = widget;
widget.clicked.connect(on_clicked);
widget.open_file.connect(open_file);
widget.save_file_as.connect(save_file);
widget.cancel_download.connect(cancel_download);
}
public void set_file_transfer(FileTransfer file_transfer, StreamInteractor stream_interactor) {
this.ref();
this.widget.weak_ref(() => {
this.widget = null;
this.unref();
});
this.file_transfer = file_transfer;
this.stream_interactor = stream_interactor;
widget.name_label.label = file_name = file_transfer.file_name;
file_transfer.bind_property("path", this, "file-transfer-path");
file_transfer.bind_property("state", this, "file-transfer-state");
file_transfer.bind_property("mime-type", this, "file-transfer-mime-type");
this.notify["file-transfer-path"].connect(update_file_info);
this.notify["file-transfer-state"].connect(update_file_info);
this.notify["file-transfer-mime-type"].connect(update_file_info);
update_file_info();
}
public void set_file(File file, string file_name, string? mime_type) {
file_uri = file.get_uri();
state = FileTransfer.State.COMPLETE;
widget.name_label.label = this.file_name = file_name;
widget.update_file_info(mime_type, state, -1);
}
private void update_file_info() {
file_uri = file_transfer.get_file().get_uri();
state = file_transfer.state;
widget.update_file_info(file_transfer.mime_type, file_transfer.state, file_transfer.size);
widget.open_file.connect(open_file);
widget.save_file_as.connect(save_file);
widget.start_download.connect(start_download);
widget.cancel_download.connect(cancel_download);
}
private void open_file() {
try{
Dino.Util.launch_default_for_uri(file_uri);
Dino.Util.launch_default_for_uri(file_transfer.get_file().get_uri());
} catch (Error err) {
warning("Failed to open %s - %s", file_uri, err.message);
warning("Failed to open %s - %s", file_transfer.get_file().get_uri(), err.message);
}
}
private void save_file() {
var save_dialog = new FileChooserNative(_("Save as…"), widget.get_root() as Gtk.Window, FileChooserAction.SAVE, null, null);
save_dialog.set_modal(true);
save_dialog.set_current_name(file_name);
save_dialog.set_current_name(file_transfer.file_name);
save_dialog.response.connect(() => {
try{
GLib.File.new_for_uri(file_uri).copy(save_dialog.get_file(), GLib.FileCopyFlags.OVERWRITE, null);
GLib.File.new_for_uri(file_transfer.get_file().get_uri()).copy(save_dialog.get_file(), GLib.FileCopyFlags.OVERWRITE, null);
} catch (Error err) {
warning("Failed copy file %s - %s", file_uri, err.message);
warning("Failed copy file %s - %s", file_transfer.get_file().get_uri(), err.message);
}
});
save_dialog.show();
}
private void start_download() {
if (stream_interactor != null) {
stream_interactor.get_module(FileManager.IDENTITY).download_file.begin(file_transfer);
}
}
private void cancel_download() {
file_transfer.cancellable.cancel();
}
}
public class FileDefaultWidgetController : Object {
private FileDefaultWidget widget;
private FileTransfer? file_transfer;
public string file_transfer_state { get; set; }
public string file_transfer_mime_type { get; set; }
private FileTransfer.State state;
public FileDefaultWidgetController(FileDefaultWidget widget) {
this.widget = widget;
widget.clicked.connect(on_clicked);
this.notify["file-transfer-state"].connect(update_file_info);
this.notify["file-transfer-mime-type"].connect(update_file_info);
}
public void set_file_transfer(FileTransfer file_transfer) {
this.file_transfer = file_transfer;
widget.name_label.label = file_transfer.file_name;
file_transfer.bind_property("state", this, "file-transfer-state");
file_transfer.bind_property("mime-type", this, "file-transfer-mime-type");
update_file_info();
}
private void update_file_info() {
state = file_transfer.state;
widget.update_file_info(file_transfer.mime_type, file_transfer.state, file_transfer.size);
}
private void on_clicked() {
switch (state) {
case FileTransfer.State.COMPLETE:
open_file();
widget.activate_action("file.open", null);
break;
case FileTransfer.State.NOT_STARTED:
assert(stream_interactor != null && file_transfer != null);
stream_interactor.get_module(FileManager.IDENTITY).download_file.begin(file_transfer);
widget.activate_action("file.download", null);
break;
default:
// Clicking doesn't do anything in FAILED and IN_PROGRESS states

View file

@ -198,7 +198,7 @@ public class MessageMetaItem : ContentMetaItem {
if (quoted_content_item != null) {
var quote_model = new Quote.Model.from_content_item(quoted_content_item, message_item.conversation, stream_interactor);
quote_model.jump_to.connect(() => {
GLib.Application.get_default().activate_action("jump-to-conversation-message", new GLib.Variant.tuple(new GLib.Variant[] { new GLib.Variant.int32(message_item.conversation.id), new GLib.Variant.int32(message_item.id) }));
GLib.Application.get_default().activate_action("jump-to-conversation-message", new GLib.Variant.tuple(new GLib.Variant[] { new GLib.Variant.int32(message_item.conversation.id), new GLib.Variant.int32(quoted_content_item.id) }));
});
var quote_widget = Quote.get_widget(quote_model);
outer.set_widget(quote_widget, Plugins.WidgetType.GTK4, 1);
@ -217,6 +217,7 @@ public class MessageMetaItem : ContentMetaItem {
if (correction_allowed) {
Plugins.MessageAction action1 = new Plugins.MessageAction();
action1.icon_name = "document-edit-symbolic";
action1.tooltip = _("Edit message");
action1.callback = (button, content_meta_item_activated, widget) => {
this.in_edit_mode = true;
};
@ -225,14 +226,16 @@ public class MessageMetaItem : ContentMetaItem {
Plugins.MessageAction reply_action = new Plugins.MessageAction();
reply_action.icon_name = "mail-reply-sender-symbolic";
reply_action.tooltip = _("Reply");
reply_action.callback = (button, content_meta_item_activated, widget) => {
GLib.Application.get_default().activate_action("quote", new GLib.Variant.tuple(new GLib.Variant[] { new GLib.Variant.int32(message_item.conversation.id), new GLib.Variant.int32(message_item.id) }));
GLib.Application.get_default().activate_action("quote", new GLib.Variant.tuple(new GLib.Variant[] { new GLib.Variant.int32(message_item.conversation.id), new GLib.Variant.int32(content_item.id) }));
};
actions.add(reply_action);
if (supports_reaction) {
Plugins.MessageAction action2 = new Plugins.MessageAction();
action2.icon_name = "dino-emoticon-add-symbolic";
action2.tooltip = _("Add reaction");
EmojiChooser chooser = new EmojiChooser();
chooser.emoji_picked.connect((emoji) => {
stream_interactor.get_module(Reactions.IDENTITY).add_reaction(message_item.conversation, message_item, emoji);

View file

@ -27,7 +27,8 @@ namespace Dino.Ui.Quote {
var message = ((MessageItem) content_item).message;
this.message = Dino.message_body_without_reply_fallback(message);
} else if (content_item.type_ == FileItem.TYPE) {
this.message = "[File]";
var file_transfer = ((FileItem) content_item).file_transfer;
this.message = _("File") + ": " + file_transfer.file_name;
}
this.message_time = content_item.time;

View file

@ -123,9 +123,10 @@ public class ReactionsWidget : Grid {
public ReactionsWidget() {
this.row_spacing = this.column_spacing = 5;
this.margin_top = 2;
this.add_css_class("reaction-grid");
add_button = new MenuButton() { tooltip_text= _("Add reaction") };
add_button.get_style_context().add_class("reaction-box");
add_button.add_css_class("pill");
Util.menu_button_set_icon_with_size(add_button, "dino-emoticon-add-symbolic", 14);
EmojiChooser chooser = new EmojiChooser();
@ -140,8 +141,8 @@ public class ReactionsWidget : Grid {
Label reaction_label = new Label("<span size='small'>" + reaction + "</span>") { use_markup=true };
Label count_label = new Label("") { use_markup=true };
Button button = new Button();
button.get_style_context().add_class("reaction-box");
Box reaction_box = new Box(Orientation.HORIZONTAL, 4);
button.add_css_class("pill");
Box reaction_box = new Box(Orientation.HORIZONTAL, 4) { halign=Align.CENTER };
reaction_box.append(reaction_label);
reaction_box.append(count_label);
button.set_child(reaction_box);
@ -161,9 +162,9 @@ public class ReactionsWidget : Grid {
reaction_counts[reaction].label = "<span font_family='monospace' size='small'>" + count.to_string() + "</span>";
if (own) {
reaction_buttons[reaction].get_style_context().add_class("own-reaction");
reaction_buttons[reaction].add_css_class("own-reaction");
} else {
reaction_buttons[reaction].get_style_context().remove_class("own-reaction");
reaction_buttons[reaction].remove_css_class("own-reaction");
}
// Build tooltip

View file

@ -15,12 +15,12 @@ public class ConversationListTitlebar : Gtk.Box {
}
}
public static HeaderBar get_conversation_list_titlebar_csd() {
public static Adw.HeaderBar get_conversation_list_titlebar_csd() {
Builder builder = new Builder.from_resource("/im/dino/Dino/conversation_list_titlebar_csd.ui");
MenuButton add_button = (MenuButton) builder.get_object("add_button");
MenuButton menu_button = (MenuButton) builder.get_object("menu_button");
create_add_menu(add_button, menu_button);
return (HeaderBar) builder.get_object("header_bar");
return (Adw.HeaderBar) builder.get_object("header_bar");
}
private static void create_add_menu(MenuButton add_button, MenuButton menu_button) {

View file

@ -38,7 +38,6 @@ public class ConversationSelector : Widget {
construct {
add_css_class("sidebar");
list_box.set_header_func(header);
list_box.set_sort_func(sort);
realize.connect(() => {
@ -75,6 +74,8 @@ public class ConversationSelector : Widget {
private void add_conversation(Conversation conversation) {
ConversationSelectorRow row;
if (!rows.has_key(conversation)) {
conversation.notify["pinned"].connect(list_box.invalidate_sort);
row = new ConversationSelectorRow(stream_interactor, conversation);
rows[conversation] = row;
list_box.append(row);
@ -119,6 +120,8 @@ public class ConversationSelector : Widget {
private async void remove_conversation(Conversation conversation) {
select_fallback_conversation(conversation);
if (rows.has_key(conversation)) {
conversation.notify["pinned"].disconnect(list_box.invalidate_sort);
yield rows[conversation].colapse();
list_box.remove(rows[conversation]);
rows.unset(conversation);
@ -135,20 +138,16 @@ public class ConversationSelector : Widget {
}
}
private void header(ListBoxRow row, ListBoxRow? before_row) {
if (row.get_header() == null && before_row != null) {
row.set_header(new Separator(Orientation.HORIZONTAL));
} else if (row.get_header() != null && before_row == null) {
row.set_header(null);
}
}
private int sort(ListBoxRow row1, ListBoxRow row2) {
ConversationSelectorRow cr1 = row1 as ConversationSelectorRow;
ConversationSelectorRow cr2 = row2 as ConversationSelectorRow;
if (cr1 != null && cr2 != null) {
Conversation c1 = cr1.conversation;
Conversation c2 = cr2.conversation;
int pin_comp = c2.pinned - c1.pinned;
if (pin_comp != 0) return pin_comp;
if (c1.last_active == null) return -1;
if (c2.last_active == null) return 1;
int comp = c2.last_active.compare(c1.last_active);

View file

@ -21,7 +21,8 @@ public class ConversationSelectorRow : ListBoxRow {
[GtkChild] protected unowned Button x_button;
[GtkChild] protected unowned Revealer time_revealer;
[GtkChild] protected unowned Revealer xbutton_revealer;
[GtkChild] protected unowned Revealer unread_count_revealer;
[GtkChild] protected unowned Revealer top_row_revealer;
[GtkChild] protected unowned Image pinned_image;
[GtkChild] public unowned Revealer main_revealer;
public Conversation conversation { get; private set; }
@ -102,8 +103,10 @@ public class ConversationSelectorRow : ListBoxRow {
});
image.set_conversation(stream_interactor, conversation);
conversation.notify["read-up-to-item"].connect(() => update_read());
conversation.notify["pinned"].connect(() => { update_pinned_icon(); });
update_name_label();
update_pinned_icon();
content_item_received();
}
@ -135,6 +138,10 @@ public class ConversationSelectorRow : ListBoxRow {
name_label.label = Util.get_conversation_display_name(stream_interactor, conversation);
}
private void update_pinned_icon() {
pinned_image.visible = conversation.pinned != 0;
}
protected void update_time_label(DateTime? new_time = null) {
if (last_content_item != null) {
time_label.visible = true;
@ -252,11 +259,11 @@ public class ConversationSelectorRow : ListBoxRow {
StateFlags curr_flags = get_state_flags();
if ((curr_flags & StateFlags.PRELIGHT) != 0) {
time_revealer.set_reveal_child(false);
unread_count_revealer.set_reveal_child(false);
top_row_revealer.set_reveal_child(false);
xbutton_revealer.set_reveal_child(true);
} else {
time_revealer.set_reveal_child(true);
unread_count_revealer.set_reveal_child(true);
top_row_revealer.set_reveal_child(true);
xbutton_revealer.set_reveal_child(false);
}
}

View file

@ -12,6 +12,9 @@ public interface ConversationTitlebar : Object {
public abstract void insert_button(Widget button);
public abstract Widget get_widget();
public abstract bool back_button_visible{ get; set; }
public signal void back_pressed();
}
public class ConversationTitlebarNoCsd : ConversationTitlebar, Object {
@ -31,14 +34,27 @@ public class ConversationTitlebarNoCsd : ConversationTitlebar, Object {
}
}
public bool back_button_visible {
get { return back_revealer.reveal_child; }
set { back_revealer.reveal_child = value; }
}
private Box widgets_box = new Box(Orientation.HORIZONTAL, 7) { margin_start=15, valign=Align.END };
private Label title_label = new Label("") { ellipsize=EllipsizeMode.END };
private Label subtitle_label = new Label("") { use_markup=true, ellipsize=EllipsizeMode.END, visible=false };
private Revealer back_revealer;
construct {
Box content_box = new Box(Orientation.HORIZONTAL, 0) { margin_start=15, margin_end=10, hexpand=true };
main.append(content_box);
back_revealer = new Revealer() { visible = true, transition_type = RevealerTransitionType.SLIDE_RIGHT, transition_duration = 200, can_focus = false, reveal_child = false };
Button back_button = new Button.from_icon_name("go-previous-symbolic") { visible = true, valign = Align.CENTER, use_underline = true };
back_button.get_style_context().add_class("image-button");
back_button.clicked.connect(() => back_pressed());
back_revealer.set_child(back_button);
content_box.append(back_revealer);
Box titles_box = new Box(Orientation.VERTICAL, 0) { valign=Align.CENTER, hexpand=true };
content_box.append(titles_box);
@ -67,10 +83,15 @@ public class ConversationTitlebarCsd : ConversationTitlebar, Object {
public new string? title { get { return title_label.label; } set { title_label.label = value; } }
public new string? subtitle { get { return subtitle_label.label; } set { subtitle_label.label = value; subtitle_label.visible = (value != null); } }
public bool back_button_visible {
get { return back_revealer.reveal_child; }
set { back_revealer.reveal_child = value; }
}
public HeaderBar header_bar = new HeaderBar();
public Adw.HeaderBar header_bar = new Adw.HeaderBar();
private Label title_label = new Label("") { ellipsize=EllipsizeMode.END };
private Label subtitle_label = new Label("") { ellipsize=EllipsizeMode.END, visible=false };
private Revealer back_revealer;
public ConversationTitlebarCsd() {
Box titles_box = new Box(Orientation.VERTICAL, 0) { valign=Align.CENTER };
@ -82,6 +103,13 @@ public class ConversationTitlebarCsd : ConversationTitlebar, Object {
subtitle_label.add_css_class("dim-label");
titles_box.append(subtitle_label);
back_revealer = new Revealer() { visible = true, transition_type = RevealerTransitionType.SLIDE_RIGHT, transition_duration = 200, can_focus = false, reveal_child = false };
Button back_button = new Button.from_icon_name("go-previous-symbolic") { visible = true, valign = Align.CENTER, use_underline = true };
back_button.get_style_context().add_class("image-button");
back_button.clicked.connect(() => back_pressed());
back_revealer.set_child(back_button);
header_bar.pack_start(back_revealer);
header_bar.set_title_widget(titles_box);
}

View file

@ -11,7 +11,7 @@ class OccupantsEntry : Plugins.ConversationTitlebarEntry, Object {
StreamInteractor stream_interactor;
private Conversation? conversation;
private MenuButton button = new MenuButton() { icon_name="system-users-symbolic", tooltip_text=Util.string_if_tooltips_active(_("Members")) };
private MenuButton button = new MenuButton() { icon_name="system-users-symbolic", tooltip_text=Util.string_if_tooltips_active(_("Members")), visible=false };
private OccupantMenu.View menu = null;

View file

@ -151,6 +151,8 @@ public class GlobalSearch {
}
private void clear_search() {
// Scroll to top
results_scrolled.vadjustment.value = 0;
foreach (Widget widget in results_box_children) {
results_box.remove(widget);
}

View file

@ -6,7 +6,7 @@ using Dino.Entities;
namespace Dino.Ui {
public class MainWindow : Gtk.Window {
public class MainWindow : Adw.Window {
public signal void conversation_selected(Conversation conversation);
@ -19,11 +19,11 @@ public class MainWindow : Gtk.Window {
public ConversationSelector conversation_selector;
public ConversationTitlebar conversation_titlebar;
public Widget conversation_list_titlebar;
public HeaderBar placeholder_headerbar = new HeaderBar() { show_title_buttons=true };
public Box box = new Box(Orientation.VERTICAL, 0) { orientation=Orientation.VERTICAL };
public Paned headerbar_paned = new Paned(Orientation.HORIZONTAL) { resize_start_child=false, shrink_start_child=false, shrink_end_child=false };
public Paned paned;
public Revealer search_revealer;
public Adw.Leaflet leaflet;
public Box left_box;
public Box right_box;
public Adw.Flap search_flap;
public GlobalSearch global_search;
private Stack stack = new Stack();
private Stack left_stack;
@ -35,7 +35,7 @@ public class MainWindow : Gtk.Window {
class construct {
var shortcut = new Shortcut(new KeyvalTrigger(Key.F, ModifierType.CONTROL_MASK), new CallbackAction((widget, args) => {
((MainWindow) widget).search_revealer.reveal_child = true;
((MainWindow) widget).search_flap.reveal_flap = true;
return false;
}));
add_shortcut(shortcut);
@ -51,65 +51,56 @@ public class MainWindow : Gtk.Window {
this.add_css_class("dino-main");
Gtk.Settings.get_default().notify["gtk-decoration-layout"].connect(set_window_buttons);
((Widget)this).realize.connect(set_window_buttons);
((Widget)this).realize.connect(restore_window_size);
setup_headerbar();
setup_unified();
setup_headerbar();
setup_stack();
paned.bind_property("position", headerbar_paned, "position", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
}
private void setup_unified() {
Builder builder = new Builder.from_resource("/im/dino/Dino/unified_main_content.ui");
paned = (Paned) builder.get_object("paned");
box.append(paned);
leaflet = (Adw.Leaflet) builder.get_object("leaflet");
box.append(leaflet);
left_box = (Box) builder.get_object("left_box");
right_box = (Box) builder.get_object("right_box");
left_stack = (Stack) builder.get_object("left_stack");
right_stack = (Stack) builder.get_object("right_stack");
conversation_view = (ConversationView) builder.get_object("conversation_view");
search_revealer = (Revealer) builder.get_object("search_revealer");
search_flap = (Adw.Flap) builder.get_object("search_flap");
conversation_selector = ((ConversationSelector) builder.get_object("conversation_list")).init(stream_interactor);
conversation_selector.conversation_selected.connect_after(() => leaflet.navigate(Adw.NavigationDirection.FORWARD));
Frame search_frame = (Frame) builder.get_object("search_frame");
Adw.Bin search_frame = (Adw.Bin) builder.get_object("search_frame");
global_search = new GlobalSearch(stream_interactor);
search_frame.set_child(global_search.get_widget());
Image conversation_list_placeholder_image = (Image) builder.get_object("conversation_list_placeholder_image");
conversation_list_placeholder_image.set_from_pixbuf(new Pixbuf.from_resource("/im/dino/Dino/dino-conversation-list-placeholder-arrow.svg"));
}
private void setup_headerbar() {
if (Util.use_csd()) {
conversation_list_titlebar = get_conversation_list_titlebar_csd();
conversation_titlebar = new ConversationTitlebarCsd();
leaflet.bind_property("folded", conversation_list_titlebar, "show-end-title-buttons", BindingFlags.SYNC_CREATE);
leaflet.bind_property("folded", conversation_titlebar.get_widget(), "show-start-title-buttons", BindingFlags.SYNC_CREATE);
} else {
Label title_label = new Label("Dino");
HeaderBar titlebar = new HeaderBar() { title_widget=title_label, show_title_buttons=true };
box.prepend(titlebar);
conversation_list_titlebar = new ConversationListTitlebar();
conversation_titlebar = new ConversationTitlebarNoCsd();
box.append(headerbar_paned);
}
headerbar_paned.set_start_child(conversation_list_titlebar);
headerbar_paned.set_end_child(conversation_titlebar.get_widget());
}
private void set_window_buttons() {
if (!Util.use_csd()) return;
Gtk.Settings? gtk_settings = Gtk.Settings.get_default();
if (gtk_settings == null) return;
string[] buttons = gtk_settings.gtk_decoration_layout.split(":");
HeaderBar conversation_headerbar = this.conversation_titlebar.get_widget() as HeaderBar;
conversation_headerbar.decoration_layout = ((buttons.length == 2) ? ":" + buttons[1] : "");
HeaderBar conversation_list_headerbar = this.conversation_list_titlebar as HeaderBar;
conversation_list_headerbar.decoration_layout = buttons[0] + ":";
left_box.prepend(conversation_list_titlebar);
right_box.prepend(conversation_titlebar.get_widget());
leaflet.notify["folded"].connect_after(() => conversation_titlebar.back_button_visible = leaflet.folded);
conversation_titlebar.back_pressed.connect(() => leaflet.navigate(Adw.NavigationDirection.BACK));
}
private void setup_stack() {
stack.add_named(box, "main");
stack.add_named(welcome_placeholder, "welcome_placeholder");
stack.add_named(accounts_placeholder, "accounts_placeholder");
set_child(stack);
set_content(stack);
}
public enum StackState {
@ -125,25 +116,16 @@ public class MainWindow : Gtk.Window {
right_stack.set_visible_child_name("content");
stack.set_visible_child_name("main");
if (Util.use_csd()) {
set_titlebar(headerbar_paned);
}
} else if (stack_state == StackState.CLEAN_START || stack_state == StackState.NO_ACTIVE_ACCOUNTS) {
if (stack_state == StackState.CLEAN_START) {
stack.set_visible_child_name("welcome_placeholder");
} else if (stack_state == StackState.NO_ACTIVE_ACCOUNTS) {
stack.set_visible_child_name("accounts_placeholder");
}
if (Util.use_csd()) {
set_titlebar(placeholder_headerbar);
}
} else if (stack_state == StackState.NO_ACTIVE_CONVERSATIONS) {
stack.set_visible_child_name("main");
left_stack.set_visible_child_name("placeholder");
right_stack.set_visible_child_name("placeholder");
if (Util.use_csd()) {
set_titlebar(headerbar_paned);
}
}
}
@ -194,28 +176,24 @@ public class MainWindow : Gtk.Window {
public class WelcomePlaceholder : MainWindowPlaceholder {
public WelcomePlaceholder() {
title_label.label = _("Welcome to Dino!");
label.label = _("Sign in or create an account to get started.");
status_page.title = _("Welcome to Dino!");
status_page.description = _("Sign in or create an account to get started.");
primary_button.label = _("Set up account");
title_label.visible = true;
secondary_button.visible = false;
primary_button.visible = true;
}
}
public class NoAccountsPlaceholder : MainWindowPlaceholder {
public NoAccountsPlaceholder() {
title_label.label = _("No active accounts");
status_page.title = _("No active accounts");
primary_button.label = _("Manage accounts");
title_label.visible = true;
label.visible = false;
secondary_button.visible = false;
primary_button.visible = true;
}
}
[GtkTemplate (ui = "/im/dino/Dino/unified_window_placeholder.ui")]
public class MainWindowPlaceholder : Box {
[GtkChild] public unowned Label title_label;
[GtkChild] public unowned Label label;
[GtkChild] public unowned Adw.StatusPage status_page;
[GtkChild] public unowned Button primary_button;
[GtkChild] public unowned Button secondary_button;
}

View file

@ -45,10 +45,10 @@ public class MainWindowController : Object {
this.conversation_view_controller = new ConversationViewController(window.conversation_view, window.conversation_titlebar, stream_interactor);
conversation_view_controller.search_menu_entry.button.bind_property("active", window.search_revealer, "reveal_child", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
conversation_view_controller.search_menu_entry.button.bind_property("active", window.search_flap, "reveal-flap", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
window.search_revealer.notify["child-revealed"].connect(() => {
if (window.search_revealer.child_revealed) {
window.search_flap.notify["reveal-flap"].connect(() => {
if (window.search_flap.reveal_flap) {
if (window.conversation_view.conversation_frame.conversation != null && window.global_search.search_entry.text == "") {
reset_search_entry();
}
@ -59,7 +59,9 @@ public class MainWindowController : Object {
window.global_search.selected_item.connect((item) => {
select_conversation(item.conversation, false, false);
window.conversation_view.conversation_frame.initialize_around_message(item.conversation, item);
close_search();
if (window.search_flap.folded) {
close_search();
}
});
window.welcome_placeholder.primary_button.clicked.connect(() => {
@ -91,16 +93,6 @@ public class MainWindowController : Object {
Widget window_widget = ((Widget) window);
GestureClick gesture_click_controller = new GestureClick();
window_widget.add_controller(gesture_click_controller);
gesture_click_controller.pressed.connect((n_press, click_x, click_y) => {
double search_x, search_y;
bool ret = window.search_revealer.translate_coordinates(window, 0, 0, out search_x, out search_y);
if (ret && click_x < search_x) {
close_search();
}
});
EventControllerKey key_event_controller = new EventControllerKey();
window_widget.add_controller(key_event_controller);
// TODO GTK4: Why doesn't this work with key_pressed signal

View file

@ -419,7 +419,8 @@ public string summarize_whitespaces_to_space(string s) {
try {
return (/\s+/).replace_literal(s, -1, 0, " ");
} catch (RegexError e) {
assert_not_reached();
critical("RegexError when summarizing whitespaces in '%s': %s", s, e.message);
return s;
}
}

View file

@ -1,184 +0,0 @@
using Gdk;
using Gtk;
namespace Dino.Ui {
class ScalingImage : Widget {
public int min_width { get; set; default = -1; }
public int target_width { get; set; default = -1; }
public int max_width { get; set; default = -1; }
public int min_height { get; set; default = -1; }
public int max_height { get; set; default = -1; }
private Pixbuf image;
private double image_ratio;
private int image_height = 0;
private int image_width = 0;
private int last_allocation_height = -1;
private int last_allocation_width = -1;
private int last_scale_factor = -1;
private Cairo.ImageSurface? cached_surface;
private static int8 use_image_surface = -1;
public void load(Pixbuf image) {
this.image = image;
this.image_ratio = ((double)image.height) / ((double)image.width);
this.image_height = image.height;
this.image_width = image.width;
queue_resize();
}
public override void dispose() {
base.dispose();
image = null;
cached_surface = null;
}
private void calculate_size(ref double exact_width, ref double exact_height) {
if (exact_width == -1 && exact_height == -1) {
if (target_width == -1) {
exact_width = ((double)image_width) / ((double)scale_factor);
exact_height = ((double)image_height) / ((double)scale_factor);
} else {
exact_width = target_width;
exact_height = exact_width * image_ratio;
}
} else if (exact_width != -1) {
exact_height = exact_width * image_ratio;
} else if (exact_height != -1) {
exact_width = exact_height / image_ratio;
} else {
if (exact_width * image_ratio > exact_height + scale_factor) {
exact_width = exact_height / image_ratio;
} else if (exact_height / image_ratio > exact_width + scale_factor) {
exact_height = exact_width * image_ratio;
}
}
if (max_width != -1 && exact_width > max_width) {
exact_width = max_width;
exact_height = exact_width * image_ratio;
}
if (max_height != -1 && exact_height > max_height) {
exact_height = max_height;
exact_width = exact_height / image_ratio;
}
if (exact_width < min_width) exact_width = min_width;
if (exact_height < min_height) exact_height = min_height;
}
public override void size_allocate(int width, int height, int baseline) {
if (max_width != -1) width = int.min(width, max_width);
if (max_height != -1) height = int.min(height, max_height);
height = int.max(height, min_height);
width = int.max(width, min_width);
double exact_width = width, exact_height = height;
calculate_size(ref exact_width, ref exact_height);
base.size_allocate(width, height, baseline);
if (last_allocation_height != height || last_allocation_width != width || last_scale_factor != scale_factor) {
last_allocation_height = height;
last_allocation_width = width;
last_scale_factor = scale_factor;
cached_surface = null;
}
}
public override void snapshot(Gtk.Snapshot snapshot) {
Cairo.Context context = snapshot.append_cairo(Graphene.Rect.alloc().init(0, 0, get_allocated_width(), get_allocated_height()));
draw(context);
}
public bool draw(Cairo.Context ctx_in) {
if (image == null) return false;
Cairo.Context ctx = ctx_in;
int width = this.get_allocated_width(), height = this.get_allocated_height(), base_factor = 1;
if (use_image_surface == -1) {
// TODO: detect if we have to buffer in image surface
use_image_surface = 1;
}
if (use_image_surface == 1) {
ctx_in.scale(1f / scale_factor, 1f / scale_factor);
if (cached_surface != null) {
ctx_in.set_source_surface(cached_surface, 0, 0);
ctx_in.paint();
ctx_in.set_source_rgb(0, 0, 0);
return true;
}
width *= scale_factor;
height *= scale_factor;
base_factor *= scale_factor;
cached_surface = new Cairo.ImageSurface(Cairo.Format.ARGB32, width, height);
ctx = new Cairo.Context(cached_surface);
}
double radius = 3 * base_factor;
double degrees = Math.PI / 180.0;
ctx.new_sub_path();
ctx.arc(width - radius, radius, radius, -90 * degrees, 0 * degrees);
ctx.arc(width - radius, height - radius, radius, 0 * degrees, 90 * degrees);
ctx.arc(radius, height - radius, radius, 90 * degrees, 180 * degrees);
ctx.arc(radius, radius, radius, 180 * degrees, 270 * degrees);
ctx.close_path();
ctx.clip();
Cairo.Surface buffer = sub_surface(ctx, width, height);
ctx.set_source_surface(buffer, 0, 0);
ctx.paint();
if (use_image_surface == 1) {
ctx_in.set_source_surface(ctx.get_target(), 0, 0);
ctx_in.paint();
ctx_in.set_source_rgb(0, 0, 0);
}
return true;
}
private Cairo.Surface sub_surface(Cairo.Context ctx, int width, int height) {
Cairo.Surface buffer = new Cairo.Surface.similar(ctx.get_target(), Cairo.Content.COLOR_ALPHA, width, height);
Cairo.Context bufctx = new Cairo.Context(buffer);
double w_scale = (double) width / image_width;
double h_scale = (double) height / image_height;
double scale = double.max(w_scale, h_scale);
bufctx.scale(scale, scale);
double x_off = 0, y_off = 0;
if (scale == h_scale) {
x_off = (width / scale - image_width) / 2.0;
} else {
y_off = (height / scale - image_height) / 2.0;
}
Gdk.cairo_set_source_pixbuf(bufctx, image, 0, 0);
bufctx.get_source().set_filter(Cairo.Filter.BILINEAR);
bufctx.paint();
bufctx.set_source_rgb(0, 0, 0);
return buffer;
}
public override void measure(Orientation orientation, int for_size, out int minimum, out int natural, out int minimum_baseline, out int natural_baseline) {
double natural_width = -1, natural_height = -1;
calculate_size(ref natural_width, ref natural_height);
if (orientation == Orientation.HORIZONTAL) {
natural = (int) Math.ceil(natural_width);
} else {
natural = (int) Math.ceil(natural_height);
}
if (for_size == -1) {
minimum = 0;
} else {
if (orientation == Orientation.HORIZONTAL) {
double exact_width = -1, exact_height = for_size;
calculate_size(ref exact_width, ref exact_height);
minimum = int.max((int)Math.floor(exact_width), min_width);
} else {
double exact_width = for_size, exact_height = -1;
calculate_size(ref exact_width, ref exact_height);
minimum = int.max((int)Math.floor(exact_height), min_height);
}
}
minimum_baseline = natural_baseline = -1;
}
public override SizeRequestMode get_request_mode() {
return SizeRequestMode.HEIGHT_FOR_WIDTH;
}
}
}

View file

@ -0,0 +1,88 @@
using Gdk;
using Gtk;
class Dino.Ui.FixedRatioPicture : Gtk.Widget {
public int min_width { get; set; default = -1; }
public int max_width { get; set; default = int.MAX; }
public int min_height { get; set; default = -1; }
public int max_height { get; set; default = int.MAX; }
public File file { get { return inner.file; } set { inner.file = value; } }
public Gdk.Paintable paintable { get { return inner.paintable; } set { inner.paintable = value; } }
#if GTK_4_8 && VALA_0_58
public Gtk.ContentFit content_fit { get { return inner.content_fit; } set { inner.content_fit = value; } }
#endif
private Gtk.Picture inner = new Gtk.Picture();
construct {
set_css_name("picture");
add_css_class("fixed-ratio");
inner.insert_after(this, null);
this.notify.connect(queue_resize);
}
private void measure_target_size(out int width, out int height) {
if (width_request != -1 && height_request != -1) {
width = width_request;
height = height_request;
return;
}
width = min_width;
height = min_height;
if (inner.should_layout()) {
int child_min = 0, child_nat = 0, child_min_baseline = -1, child_nat_baseline = -1;
inner.measure(Orientation.HORIZONTAL, -1, out child_min, out child_nat, out child_min_baseline, out child_nat_baseline);
width = int.max(child_nat, width);
}
width = int.min(width, max_width);
if (inner.should_layout()) {
int child_min = 0, child_nat = 0, child_min_baseline = -1, child_nat_baseline = -1;
inner.measure(Orientation.VERTICAL, width, out child_min, out child_nat, out child_min_baseline, out child_nat_baseline);
height = int.max(child_nat, height);
}
if (height > max_height) {
height = max_height;
width = min_width;
if (inner.should_layout()) {
int child_min = 0, child_nat = 0, child_min_baseline = -1, child_nat_baseline = -1;
inner.measure(Orientation.HORIZONTAL, max_height, out child_min, out child_nat, out child_min_baseline, out child_nat_baseline);
width = int.max(child_nat, width);
}
width = int.min(width, max_width);
}
}
public override void measure(Orientation orientation, int for_size, out int minimum, out int natural, out int minimum_baseline, out int natural_baseline) {
minimum_baseline = -1;
natural_baseline = -1;
int width, height;
measure_target_size(out width, out height);
if (orientation == Orientation.HORIZONTAL) {
minimum = min_width;
natural = width;
} else if (for_size == -1) {
minimum = min_height;
natural = height;
} else {
minimum = natural = height * for_size / width;
}
}
public override void size_allocate(int width, int height, int baseline) {
if (inner.should_layout()) {
inner.allocate(width, height, baseline, null);
}
}
public override SizeRequestMode get_request_mode() {
return SizeRequestMode.HEIGHT_FOR_WIDTH;
}
public override void dispose() {
inner.unparent();
base.dispose();
}
}

View file

@ -0,0 +1,59 @@
using Gtk;
public class Dino.Ui.NaturalSizeIncrease : Gtk.Widget {
public int min_natural_height { get; set; default = -1; }
public int min_natural_width { get; set; default = -1; }
construct {
this.notify.connect(queue_resize);
}
public override void measure(Orientation orientation, int for_size, out int minimum, out int natural, out int minimum_baseline, out int natural_baseline) {
minimum = 0;
if (orientation == Orientation.HORIZONTAL) {
natural = min_natural_width;
} else {
natural = min_natural_height;
}
natural = int.max(0, natural);
minimum_baseline = -1;
natural_baseline = -1;
Widget child = get_first_child();
while (child != null) {
if (child.should_layout()) {
int child_min = 0;
int child_nat = 0;
int child_min_baseline = -1;
int child_nat_baseline = -1;
child.measure(orientation, -1, out child_min, out child_nat, out child_min_baseline, out child_nat_baseline);
minimum = int.max(minimum, child_min);
natural = int.max(natural, child_nat);
if (child_min_baseline > 0) {
minimum_baseline = int.max(minimum_baseline, child_min_baseline);
}
if (child_nat_baseline > 0) {
natural_baseline = int.max(natural_baseline, child_nat_baseline);
}
}
child = child.get_next_sibling();
}
}
public override void size_allocate(int width, int height, int baseline) {
Widget child = get_first_child();
while (child != null) {
if (child.should_layout()) {
child.allocate(width, height, baseline, null);
}
child = child.get_next_sibling();
}
}
public override SizeRequestMode get_request_mode() {
Widget child = get_first_child();
if (child != null) {
return child.get_request_mode();
}
return SizeRequestMode.CONSTANT_SIZE;
}
}

View file

@ -241,7 +241,9 @@
<property name="margin-top">10</property>
<property name="margin-bottom">10</property>
<child>
<object class="GtkImage" id="qrcode_image"/>
<object class="GtkPicture" id="qrcode_picture">
<property name="can-shrink">False</property>
</object>
</child>
</object>
</property>

View file

@ -20,7 +20,6 @@ lb
lt
nb
nl
nl_BE
oc
pl
pt

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-02-12 22:06+0100\n"
"POT-Creation-Date: 2023-01-28 14:28+0100\n"
"PO-Revision-Date: 2021-08-31 23:44+0000\n"
"Language-Team: Arabic <https://hosted.weblate.org/projects/dino/plugin-omemo/"
"ar/>\n"
@ -36,39 +36,39 @@ msgid ""
"%s does not trust this device. That means, you might be missing messages."
msgstr "%s لا يثق بهذا الجهاز. هذا يعني أنك قد تفقد بعض الرسائل."
#: plugins/omemo/src/ui/manage_key_dialog.vala:35
#: plugins/omemo/src/ui/manage_key_dialog.vala:34
msgid "Manage Key"
msgstr "إدارة المفتاح"
#: plugins/omemo/src/ui/manage_key_dialog.vala:36
#: plugins/omemo/src/ui/manage_key_dialog.vala:35
msgid ""
"Compare the fingerprint, character by character, with the one shown on your "
"contact's device."
msgstr "قارن البصمة حرفا بحرف ، مع التي تظهر على جهاز مُراسِلك."
#: plugins/omemo/src/ui/manage_key_dialog.vala:37
#: plugins/omemo/src/ui/manage_key_dialog.vala:36
msgid "Fingerprints differ"
msgstr "البصمات غير متطابقة"
#: plugins/omemo/src/ui/manage_key_dialog.vala:38
#: plugins/omemo/src/ui/manage_key_dialog.vala:37
msgid "Fingerprints match"
msgstr "البصمات متطابقة"
#: plugins/omemo/src/ui/manage_key_dialog.vala:39
#: plugins/omemo/src/ui/manage_key_dialog.vala:82
#: plugins/omemo/src/ui/manage_key_dialog.vala:88
#: plugins/omemo/src/ui/manage_key_dialog.vala:38
#: plugins/omemo/src/ui/manage_key_dialog.vala:81
#: plugins/omemo/src/ui/manage_key_dialog.vala:87
msgid "Cancel"
msgstr "ألغاء"
#: plugins/omemo/src/ui/manage_key_dialog.vala:40
#: plugins/omemo/src/ui/manage_key_dialog.vala:39
msgid "Confirm"
msgstr "تأكيد"
#: plugins/omemo/src/ui/manage_key_dialog.vala:60
#: plugins/omemo/src/ui/manage_key_dialog.vala:59
msgid "Verify key"
msgstr "التحقق مِن المفتاح"
#: plugins/omemo/src/ui/manage_key_dialog.vala:61
#: plugins/omemo/src/ui/manage_key_dialog.vala:60
#, c-format
msgid ""
"Future messages sent by %s from the device that uses this key will be "
@ -77,11 +77,11 @@ msgstr ""
"سيتم تمييز الرسائل القادمة المرسلة بواسطة %s من الجهاز الذي يستخدم هذا "
"المفتاح وفقًا لذلك في نافذة الدردشة."
#: plugins/omemo/src/ui/manage_key_dialog.vala:71
#: plugins/omemo/src/ui/manage_key_dialog.vala:70
msgid "Fingerprints do not match"
msgstr "البصمات غير متطابقة"
#: plugins/omemo/src/ui/manage_key_dialog.vala:72
#: plugins/omemo/src/ui/manage_key_dialog.vala:71
#, c-format
msgid ""
"Please verify that you are comparing the correct fingerprint. If "
@ -92,69 +92,69 @@ msgstr ""
"المحتمل أنّ حساب %s قد تعرض لاختراق وأنه مِن المستحسن التفكير في رفض هذا "
"المفتاح."
#: plugins/omemo/src/ui/manage_key_dialog.vala:124
#: plugins/omemo/src/ui/manage_key_dialog.vala:123
msgid "Verify key fingerprint"
msgstr "التحقق مِن مفتاح البصمة"
#: plugins/omemo/src/ui/manage_key_dialog.vala:124
#: plugins/omemo/src/ui/manage_key_dialog.vala:123
msgid ""
"Compare this key's fingerprint with the fingerprint displayed on the "
"contact's device."
msgstr "قارن بصمة هذا المفتاح مع البصمة المعروضة على جهاز مُراسِلك."
#: plugins/omemo/src/ui/manage_key_dialog.vala:126
#: plugins/omemo/src/ui/manage_key_dialog.vala:152
#: plugins/omemo/src/ui/contact_details_dialog.vala:256
#: plugins/omemo/src/ui/manage_key_dialog.vala:125
#: plugins/omemo/src/ui/manage_key_dialog.vala:151
#: plugins/omemo/src/ui/contact_details_dialog.vala:269
msgid "Reject key"
msgstr "رفض المفتاح"
#: plugins/omemo/src/ui/manage_key_dialog.vala:126
#: plugins/omemo/src/ui/manage_key_dialog.vala:125
msgid ""
"Block encrypted communication with the contact's device that uses this key."
msgstr "حظر الاتصال المشفر مع جهاز المراسل الذي يستخدم هذا المفتاح."
#: plugins/omemo/src/ui/manage_key_dialog.vala:128
#: plugins/omemo/src/ui/manage_key_dialog.vala:160
#: plugins/omemo/src/ui/contact_details_dialog.vala:251
#: plugins/omemo/src/ui/manage_key_dialog.vala:127
#: plugins/omemo/src/ui/manage_key_dialog.vala:159
#: plugins/omemo/src/ui/contact_details_dialog.vala:264
msgid "Accept key"
msgstr "اقبل المفتاح"
#: plugins/omemo/src/ui/manage_key_dialog.vala:128
#: plugins/omemo/src/ui/manage_key_dialog.vala:127
msgid ""
"Allow encrypted communication with the contact's device that uses this key."
msgstr "السماح بالاتصال المشفر بجهاز المراسل الذي يستخدم هذا المفتاح."
#: plugins/omemo/src/ui/manage_key_dialog.vala:132
#: plugins/omemo/src/ui/manage_key_dialog.vala:137
#: plugins/omemo/src/ui/manage_key_dialog.vala:141
#: plugins/omemo/src/ui/manage_key_dialog.vala:131
#: plugins/omemo/src/ui/manage_key_dialog.vala:136
#: plugins/omemo/src/ui/manage_key_dialog.vala:140
#, c-format
msgid "This key is currently %s."
msgstr "المفتاح الآن %s."
#: plugins/omemo/src/ui/manage_key_dialog.vala:132
#: plugins/omemo/src/ui/manage_key_dialog.vala:131
msgid "accepted"
msgstr "مقبول"
#: plugins/omemo/src/ui/manage_key_dialog.vala:132
#: plugins/omemo/src/ui/manage_key_dialog.vala:137
#: plugins/omemo/src/ui/manage_key_dialog.vala:131
#: plugins/omemo/src/ui/manage_key_dialog.vala:136
#, c-format
msgid "This means it can be used by %s to receive and send encrypted messages."
msgstr "ذلك يعني أنه بإمكان %s أن يستخدمه لتلقي وإرسال رسائل مشفرة."
#: plugins/omemo/src/ui/manage_key_dialog.vala:137
#: plugins/omemo/src/ui/manage_key_dialog.vala:136
msgid "verified"
msgstr "تم التحقق منه"
#: plugins/omemo/src/ui/manage_key_dialog.vala:137
#: plugins/omemo/src/ui/manage_key_dialog.vala:136
msgid ""
"Additionally it has been verified to match the key on the contact's device."
msgstr "بالإضافة إلى أنه قد تم التحقق من تطابق المفتاح على جهاز المُراسِل."
#: plugins/omemo/src/ui/manage_key_dialog.vala:141
#: plugins/omemo/src/ui/manage_key_dialog.vala:140
msgid "rejected"
msgstr "مرفوض"
#: plugins/omemo/src/ui/manage_key_dialog.vala:141
#: plugins/omemo/src/ui/manage_key_dialog.vala:140
#, c-format
msgid ""
"This means it cannot be used by %s to decipher your messages, and you won't "
@ -163,7 +163,7 @@ msgstr ""
"هذا يعني أنه لن يكون بمقدور %s استخدامه لفك تشفير رسائلك، ولن ترى الرسائل "
"المشفرة به."
#: plugins/omemo/src/ui/manage_key_dialog.vala:153
#: plugins/omemo/src/ui/manage_key_dialog.vala:152
#, c-format
msgid ""
"You won't see encrypted messages from the device of %s that uses this key. "
@ -172,7 +172,7 @@ msgstr ""
"لن ترى الرسائل المشفرة من جهاز %s الذي يستخدم هذا المفتاح. على العكس من "
"ذلك ، لن يتمكن هذا الجهاز من فك تشفير رسائلك بعد الآن."
#: plugins/omemo/src/ui/manage_key_dialog.vala:161
#: plugins/omemo/src/ui/manage_key_dialog.vala:160
#, c-format
msgid ""
"You will be able to exchange encrypted messages with the device of %s that "
@ -180,7 +180,7 @@ msgid ""
msgstr ""
"سوف يكون بمقدورك تبادل الرسائل المشفرة مع جهاز %s الذي يستخدم هذا المفتاح."
#: plugins/omemo/src/ui/manage_key_dialog.vala:167
#: plugins/omemo/src/ui/manage_key_dialog.vala:166
msgid "Back"
msgstr "العودة"
@ -192,15 +192,6 @@ msgstr "الإدارة"
msgid "This contact has new devices"
msgstr "لدى هذا المُراسِل أجهزة جديدة"
#: plugins/omemo/src/ui/account_settings_widget.vala:42
#: plugins/omemo/src/ui/account_settings_widget.vala:45
msgid "Own fingerprint"
msgstr "بصمتك الخاصة"
#: plugins/omemo/src/ui/account_settings_widget.vala:42
msgid "Will be generated on first connection"
msgstr "سيتم توليدها عند أول اتصال"
#: plugins/omemo/src/ui/own_notifications.vala:29
msgid "OMEMO trust decision required"
msgstr "مطلوب قرار ثقة مع OMEMO"
@ -210,6 +201,15 @@ msgstr "مطلوب قرار ثقة مع OMEMO"
msgid "Did you add a new device for account %s?"
msgstr "هل قمت بإضافة جهاز جديد على حساب %s؟"
#: plugins/omemo/src/ui/account_settings_entry.vala:47
#: plugins/omemo/src/ui/account_settings_entry.vala:50
msgid "Own fingerprint"
msgstr "بصمتك الخاصة"
#: plugins/omemo/src/ui/account_settings_entry.vala:47
msgid "Will be generated on first connection"
msgstr "سيتم توليدها عند أول اتصال"
#: plugins/omemo/src/ui/contact_details_provider.vala:42
msgid "Encryption"
msgstr "التشفير"
@ -225,52 +225,52 @@ msgstr[3] "%d أجهزة OMEMO"
msgstr[4] "%d جهاز OMEMO"
msgstr[5] "%d أجهزة OMEMO"
#: plugins/omemo/src/ui/contact_details_dialog.vala:46
#: plugins/omemo/src/ui/contact_details_dialog.vala:48
msgid "OMEMO Key Management"
msgstr "إدارة مفاتيح أوميمو"
#: plugins/omemo/src/ui/contact_details_dialog.vala:47
#: plugins/omemo/src/ui/contact_details_dialog.vala:49
msgid "Automatically accept new keys"
msgstr "تقبّل المفاتيح الجديدة تلقائياً"
#: plugins/omemo/src/ui/contact_details_dialog.vala:48
#: plugins/omemo/src/ui/contact_details_dialog.vala:50
msgid "New encryption keys from this contact will be accepted automatically."
msgstr "سيتم قبول مفاتيح التشفير الجديدة الخاصة بهذا المراسل تلقائيًا."
#: plugins/omemo/src/ui/contact_details_dialog.vala:49
#: plugins/omemo/src/ui/contact_details_dialog.vala:51
msgid "Own key"
msgstr "مفتاحك الخاص"
#: plugins/omemo/src/ui/contact_details_dialog.vala:50
#: plugins/omemo/src/ui/contact_details_dialog.vala:52
msgid "New keys"
msgstr "المفاتيح الجديدة"
#: plugins/omemo/src/ui/contact_details_dialog.vala:51
#: plugins/omemo/src/ui/contact_details_dialog.vala:53
msgid "Associated keys"
msgstr "المفاتيح المقترِنة"
#: plugins/omemo/src/ui/contact_details_dialog.vala:52
#: plugins/omemo/src/ui/contact_details_dialog.vala:54
msgid "Inactive keys"
msgstr "المفاتيح غير المُنشَّطة"
#: plugins/omemo/src/ui/contact_details_dialog.vala:84
#: plugins/omemo/src/ui/contact_details_dialog.vala:86
msgid ""
"New encryption keys from your other devices will be accepted automatically."
msgstr "سيتم قبول مفاتيح التشفير الجديدة من أجهزتك الأخرى تلقائيًا."
#: plugins/omemo/src/ui/contact_details_dialog.vala:319
#: plugins/omemo/src/ui/contact_details_dialog.vala:335
msgid "Accepted"
msgstr "تم قبوله"
#: plugins/omemo/src/ui/contact_details_dialog.vala:324
#: plugins/omemo/src/ui/contact_details_dialog.vala:340
msgid "Rejected"
msgstr "تم رفضه"
#: plugins/omemo/src/ui/contact_details_dialog.vala:329
#: plugins/omemo/src/ui/contact_details_dialog.vala:345
msgid "Verified"
msgstr "تم التحقق منه"
#: plugins/omemo/src/ui/contact_details_dialog.vala:336
#: plugins/omemo/src/ui/contact_details_dialog.vala:352
msgid "Unused"
msgstr "غير مستخدَم"

View file

@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: dino-omemo 20180123\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-02-12 22:06+0100\n"
"POT-Creation-Date: 2023-01-28 14:28+0100\n"
"PO-Revision-Date: 2020-01-14 05:21+0000\n"
"Language-Team: Catalan <ca@dodds.net>\n"
"Language: ca\n"
@ -33,11 +33,11 @@ msgid ""
"%s does not trust this device. That means, you might be missing messages."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:35
#: plugins/omemo/src/ui/manage_key_dialog.vala:34
msgid "Manage Key"
msgstr "Gestiona la clau"
#: plugins/omemo/src/ui/manage_key_dialog.vala:36
#: plugins/omemo/src/ui/manage_key_dialog.vala:35
msgid ""
"Compare the fingerprint, character by character, with the one shown on your "
"contact's device."
@ -45,40 +45,40 @@ msgstr ""
"Compareu l'empremta, caràcter per caràcter, amb la que es mostra al vostre "
"dispositiu de contactes."
#: plugins/omemo/src/ui/manage_key_dialog.vala:37
#: plugins/omemo/src/ui/manage_key_dialog.vala:36
msgid "Fingerprints differ"
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:38
#: plugins/omemo/src/ui/manage_key_dialog.vala:37
msgid "Fingerprints match"
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:39
#: plugins/omemo/src/ui/manage_key_dialog.vala:82
#: plugins/omemo/src/ui/manage_key_dialog.vala:88
#: plugins/omemo/src/ui/manage_key_dialog.vala:38
#: plugins/omemo/src/ui/manage_key_dialog.vala:81
#: plugins/omemo/src/ui/manage_key_dialog.vala:87
msgid "Cancel"
msgstr "Cancel·la"
#: plugins/omemo/src/ui/manage_key_dialog.vala:40
#: plugins/omemo/src/ui/manage_key_dialog.vala:39
msgid "Confirm"
msgstr "Confirma"
#: plugins/omemo/src/ui/manage_key_dialog.vala:60
#: plugins/omemo/src/ui/manage_key_dialog.vala:59
msgid "Verify key"
msgstr "Comproveu la clau"
#: plugins/omemo/src/ui/manage_key_dialog.vala:61
#: plugins/omemo/src/ui/manage_key_dialog.vala:60
#, c-format
msgid ""
"Future messages sent by %s from the device that uses this key will be "
"highlighted accordingly in the chat window."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:71
#: plugins/omemo/src/ui/manage_key_dialog.vala:70
msgid "Fingerprints do not match"
msgstr "Les empremtes no es corresponen"
#: plugins/omemo/src/ui/manage_key_dialog.vala:72
#: plugins/omemo/src/ui/manage_key_dialog.vala:71
#, c-format
msgid ""
"Please verify that you are comparing the correct fingerprint. If "
@ -89,91 +89,91 @@ msgstr ""
"coincideixen, el compte de %s podria trobar-se en perill i heu de considerar "
"rebutjar aquesta clau."
#: plugins/omemo/src/ui/manage_key_dialog.vala:124
#: plugins/omemo/src/ui/manage_key_dialog.vala:123
#, fuzzy
msgid "Verify key fingerprint"
msgstr "Emprempta pròpia"
#: plugins/omemo/src/ui/manage_key_dialog.vala:124
#: plugins/omemo/src/ui/manage_key_dialog.vala:123
msgid ""
"Compare this key's fingerprint with the fingerprint displayed on the "
"contact's device."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:126
#: plugins/omemo/src/ui/manage_key_dialog.vala:152
#: plugins/omemo/src/ui/contact_details_dialog.vala:256
#: plugins/omemo/src/ui/manage_key_dialog.vala:125
#: plugins/omemo/src/ui/manage_key_dialog.vala:151
#: plugins/omemo/src/ui/contact_details_dialog.vala:269
msgid "Reject key"
msgstr "Rebutja la clau"
#: plugins/omemo/src/ui/manage_key_dialog.vala:126
#: plugins/omemo/src/ui/manage_key_dialog.vala:125
msgid ""
"Block encrypted communication with the contact's device that uses this key."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:128
#: plugins/omemo/src/ui/manage_key_dialog.vala:160
#: plugins/omemo/src/ui/contact_details_dialog.vala:251
#: plugins/omemo/src/ui/manage_key_dialog.vala:127
#: plugins/omemo/src/ui/manage_key_dialog.vala:159
#: plugins/omemo/src/ui/contact_details_dialog.vala:264
msgid "Accept key"
msgstr "Accepta la clau"
#: plugins/omemo/src/ui/manage_key_dialog.vala:128
#: plugins/omemo/src/ui/manage_key_dialog.vala:127
msgid ""
"Allow encrypted communication with the contact's device that uses this key."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:132
#: plugins/omemo/src/ui/manage_key_dialog.vala:137
#: plugins/omemo/src/ui/manage_key_dialog.vala:141
#: plugins/omemo/src/ui/manage_key_dialog.vala:131
#: plugins/omemo/src/ui/manage_key_dialog.vala:136
#: plugins/omemo/src/ui/manage_key_dialog.vala:140
#, c-format
msgid "This key is currently %s."
msgstr "Actualment, aquesta clau ha estat %s."
#: plugins/omemo/src/ui/manage_key_dialog.vala:132
#: plugins/omemo/src/ui/manage_key_dialog.vala:131
msgid "accepted"
msgstr "acceptada"
#: plugins/omemo/src/ui/manage_key_dialog.vala:132
#: plugins/omemo/src/ui/manage_key_dialog.vala:137
#: plugins/omemo/src/ui/manage_key_dialog.vala:131
#: plugins/omemo/src/ui/manage_key_dialog.vala:136
#, c-format
msgid "This means it can be used by %s to receive and send encrypted messages."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:137
#: plugins/omemo/src/ui/manage_key_dialog.vala:136
msgid "verified"
msgstr "verificada"
#: plugins/omemo/src/ui/manage_key_dialog.vala:137
#: plugins/omemo/src/ui/manage_key_dialog.vala:136
msgid ""
"Additionally it has been verified to match the key on the contact's device."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:141
#: plugins/omemo/src/ui/manage_key_dialog.vala:140
msgid "rejected"
msgstr "rebutjada"
#: plugins/omemo/src/ui/manage_key_dialog.vala:141
#: plugins/omemo/src/ui/manage_key_dialog.vala:140
#, c-format
msgid ""
"This means it cannot be used by %s to decipher your messages, and you won't "
"see messages encrypted with it."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:153
#: plugins/omemo/src/ui/manage_key_dialog.vala:152
#, c-format
msgid ""
"You won't see encrypted messages from the device of %s that uses this key. "
"Conversely, that device won't be able to decipher your messages anymore."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:161
#: plugins/omemo/src/ui/manage_key_dialog.vala:160
#, c-format
msgid ""
"You will be able to exchange encrypted messages with the device of %s that "
"uses this key."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:167
#: plugins/omemo/src/ui/manage_key_dialog.vala:166
msgid "Back"
msgstr "Enrere"
@ -185,15 +185,6 @@ msgstr "Gestiona"
msgid "This contact has new devices"
msgstr "Aquest contacte té dispositius nous"
#: plugins/omemo/src/ui/account_settings_widget.vala:42
#: plugins/omemo/src/ui/account_settings_widget.vala:45
msgid "Own fingerprint"
msgstr "Empremta pròpia"
#: plugins/omemo/src/ui/account_settings_widget.vala:42
msgid "Will be generated on first connection"
msgstr "Es generarà durant la primera connexió"
#: plugins/omemo/src/ui/own_notifications.vala:29
msgid "OMEMO trust decision required"
msgstr ""
@ -203,6 +194,15 @@ msgstr ""
msgid "Did you add a new device for account %s?"
msgstr "Heu afegit un dispositiu nou per al compte %s?"
#: plugins/omemo/src/ui/account_settings_entry.vala:47
#: plugins/omemo/src/ui/account_settings_entry.vala:50
msgid "Own fingerprint"
msgstr "Empremta pròpia"
#: plugins/omemo/src/ui/account_settings_entry.vala:47
msgid "Will be generated on first connection"
msgstr "Es generarà durant la primera connexió"
#: plugins/omemo/src/ui/contact_details_provider.vala:42
msgid "Encryption"
msgstr "Xifratge"
@ -214,52 +214,52 @@ msgid_plural "%d OMEMO devices"
msgstr[0] "%d dispositiu OMEMO"
msgstr[1] "%d dispositius OMEMO"
#: plugins/omemo/src/ui/contact_details_dialog.vala:46
#: plugins/omemo/src/ui/contact_details_dialog.vala:48
msgid "OMEMO Key Management"
msgstr "Gestió de claus OMEMO"
#: plugins/omemo/src/ui/contact_details_dialog.vala:47
#: plugins/omemo/src/ui/contact_details_dialog.vala:49
msgid "Automatically accept new keys"
msgstr "Accepta claus noves automàticament"
#: plugins/omemo/src/ui/contact_details_dialog.vala:48
#: plugins/omemo/src/ui/contact_details_dialog.vala:50
msgid "New encryption keys from this contact will be accepted automatically."
msgstr ""
#: plugins/omemo/src/ui/contact_details_dialog.vala:49
#: plugins/omemo/src/ui/contact_details_dialog.vala:51
msgid "Own key"
msgstr "Clau pròpia"
#: plugins/omemo/src/ui/contact_details_dialog.vala:50
#: plugins/omemo/src/ui/contact_details_dialog.vala:52
msgid "New keys"
msgstr "Claus noves"
#: plugins/omemo/src/ui/contact_details_dialog.vala:51
#: plugins/omemo/src/ui/contact_details_dialog.vala:53
msgid "Associated keys"
msgstr "Claus associades"
#: plugins/omemo/src/ui/contact_details_dialog.vala:52
#: plugins/omemo/src/ui/contact_details_dialog.vala:54
msgid "Inactive keys"
msgstr "Claus inactives"
#: plugins/omemo/src/ui/contact_details_dialog.vala:84
#: plugins/omemo/src/ui/contact_details_dialog.vala:86
msgid ""
"New encryption keys from your other devices will be accepted automatically."
msgstr ""
#: plugins/omemo/src/ui/contact_details_dialog.vala:319
#: plugins/omemo/src/ui/contact_details_dialog.vala:335
msgid "Accepted"
msgstr "Acceptada"
#: plugins/omemo/src/ui/contact_details_dialog.vala:324
#: plugins/omemo/src/ui/contact_details_dialog.vala:340
msgid "Rejected"
msgstr "Rebutjada"
#: plugins/omemo/src/ui/contact_details_dialog.vala:329
#: plugins/omemo/src/ui/contact_details_dialog.vala:345
msgid "Verified"
msgstr "Verificada"
#: plugins/omemo/src/ui/contact_details_dialog.vala:336
#: plugins/omemo/src/ui/contact_details_dialog.vala:352
msgid "Unused"
msgstr "No utilitzada"

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-02-12 22:06+0100\n"
"POT-Creation-Date: 2023-01-28 14:28+0100\n"
"PO-Revision-Date: 2021-07-05 16:32+0000\n"
"Language-Team: none\n"
"Language: cs\n"
@ -37,11 +37,11 @@ msgid ""
msgstr ""
"%s nedůvěřuje tomuto zařízení. Z tohoto důvodu nemusí vidět některé zprávy."
#: plugins/omemo/src/ui/manage_key_dialog.vala:35
#: plugins/omemo/src/ui/manage_key_dialog.vala:34
msgid "Manage Key"
msgstr "Spravovat klíč"
#: plugins/omemo/src/ui/manage_key_dialog.vala:36
#: plugins/omemo/src/ui/manage_key_dialog.vala:35
msgid ""
"Compare the fingerprint, character by character, with the one shown on your "
"contact's device."
@ -49,29 +49,29 @@ msgstr ""
"Porovnejte otisk klíče, znak po znaku, s tím, který je zobrazen na zařízení "
"vašeho kontaktu."
#: plugins/omemo/src/ui/manage_key_dialog.vala:37
#: plugins/omemo/src/ui/manage_key_dialog.vala:36
msgid "Fingerprints differ"
msgstr "Otisky klíčů se liší"
#: plugins/omemo/src/ui/manage_key_dialog.vala:38
#: plugins/omemo/src/ui/manage_key_dialog.vala:37
msgid "Fingerprints match"
msgstr "Otisky klíčů odpovídají"
#: plugins/omemo/src/ui/manage_key_dialog.vala:39
#: plugins/omemo/src/ui/manage_key_dialog.vala:82
#: plugins/omemo/src/ui/manage_key_dialog.vala:88
#: plugins/omemo/src/ui/manage_key_dialog.vala:38
#: plugins/omemo/src/ui/manage_key_dialog.vala:81
#: plugins/omemo/src/ui/manage_key_dialog.vala:87
msgid "Cancel"
msgstr "Zrušit"
#: plugins/omemo/src/ui/manage_key_dialog.vala:40
#: plugins/omemo/src/ui/manage_key_dialog.vala:39
msgid "Confirm"
msgstr "Potvrdit"
#: plugins/omemo/src/ui/manage_key_dialog.vala:60
#: plugins/omemo/src/ui/manage_key_dialog.vala:59
msgid "Verify key"
msgstr "Ověřit klíč"
#: plugins/omemo/src/ui/manage_key_dialog.vala:61
#: plugins/omemo/src/ui/manage_key_dialog.vala:60
#, c-format
msgid ""
"Future messages sent by %s from the device that uses this key will be "
@ -80,11 +80,11 @@ msgstr ""
"Budoucí zprávy, s odesílatelem %s, ze zařízení, které používá tento klíč, "
"budou odpovídajícím způsobem zvýrazněny v okně chatu."
#: plugins/omemo/src/ui/manage_key_dialog.vala:71
#: plugins/omemo/src/ui/manage_key_dialog.vala:70
msgid "Fingerprints do not match"
msgstr "Otisky klíčů se neshodují"
#: plugins/omemo/src/ui/manage_key_dialog.vala:72
#: plugins/omemo/src/ui/manage_key_dialog.vala:71
#, c-format
msgid ""
"Please verify that you are comparing the correct fingerprint. If "
@ -94,76 +94,76 @@ msgstr ""
"Ověřte, zda porovnáváte správný otisk klíče. Pokud se otisky neshodují, účet "
"%s může být ohrožen a měli byste zvážit zamítnutí tohoto klíče."
#: plugins/omemo/src/ui/manage_key_dialog.vala:124
#: plugins/omemo/src/ui/manage_key_dialog.vala:123
msgid "Verify key fingerprint"
msgstr "Ověření otisku klíče"
#: plugins/omemo/src/ui/manage_key_dialog.vala:124
#: plugins/omemo/src/ui/manage_key_dialog.vala:123
msgid ""
"Compare this key's fingerprint with the fingerprint displayed on the "
"contact's device."
msgstr ""
"Porovnejte otisk tohoto klíče s otiskem zobrazeným na zařízení kontaktu."
#: plugins/omemo/src/ui/manage_key_dialog.vala:126
#: plugins/omemo/src/ui/manage_key_dialog.vala:152
#: plugins/omemo/src/ui/contact_details_dialog.vala:256
#: plugins/omemo/src/ui/manage_key_dialog.vala:125
#: plugins/omemo/src/ui/manage_key_dialog.vala:151
#: plugins/omemo/src/ui/contact_details_dialog.vala:269
msgid "Reject key"
msgstr "Odmítnout klíč"
#: plugins/omemo/src/ui/manage_key_dialog.vala:126
#: plugins/omemo/src/ui/manage_key_dialog.vala:125
msgid ""
"Block encrypted communication with the contact's device that uses this key."
msgstr ""
"Zablokujte šifrovanou komunikaci se zařízením kontaktu, které tento klíč "
"používá."
#: plugins/omemo/src/ui/manage_key_dialog.vala:128
#: plugins/omemo/src/ui/manage_key_dialog.vala:160
#: plugins/omemo/src/ui/contact_details_dialog.vala:251
#: plugins/omemo/src/ui/manage_key_dialog.vala:127
#: plugins/omemo/src/ui/manage_key_dialog.vala:159
#: plugins/omemo/src/ui/contact_details_dialog.vala:264
msgid "Accept key"
msgstr "Přijmout klíč"
#: plugins/omemo/src/ui/manage_key_dialog.vala:128
#: plugins/omemo/src/ui/manage_key_dialog.vala:127
msgid ""
"Allow encrypted communication with the contact's device that uses this key."
msgstr ""
"Povolit šifrovanou komunikaci se zařízením kontaktu, které používá tento "
"klíč."
#: plugins/omemo/src/ui/manage_key_dialog.vala:132
#: plugins/omemo/src/ui/manage_key_dialog.vala:137
#: plugins/omemo/src/ui/manage_key_dialog.vala:141
#: plugins/omemo/src/ui/manage_key_dialog.vala:131
#: plugins/omemo/src/ui/manage_key_dialog.vala:136
#: plugins/omemo/src/ui/manage_key_dialog.vala:140
#, c-format
msgid "This key is currently %s."
msgstr "Tento klíč je v současné době %s."
#: plugins/omemo/src/ui/manage_key_dialog.vala:132
#: plugins/omemo/src/ui/manage_key_dialog.vala:131
msgid "accepted"
msgstr "akceptován"
#: plugins/omemo/src/ui/manage_key_dialog.vala:132
#: plugins/omemo/src/ui/manage_key_dialog.vala:137
#: plugins/omemo/src/ui/manage_key_dialog.vala:131
#: plugins/omemo/src/ui/manage_key_dialog.vala:136
#, c-format
msgid "This means it can be used by %s to receive and send encrypted messages."
msgstr ""
"To znamená, že jej %s může používat k přijímání a odesílání šifrovaných "
"zpráv."
#: plugins/omemo/src/ui/manage_key_dialog.vala:137
#: plugins/omemo/src/ui/manage_key_dialog.vala:136
msgid "verified"
msgstr "ověřen"
#: plugins/omemo/src/ui/manage_key_dialog.vala:137
#: plugins/omemo/src/ui/manage_key_dialog.vala:136
msgid ""
"Additionally it has been verified to match the key on the contact's device."
msgstr "Navíc bylo ověřeno, že odpovídá klíči v zařízení kontaktu."
#: plugins/omemo/src/ui/manage_key_dialog.vala:141
#: plugins/omemo/src/ui/manage_key_dialog.vala:140
msgid "rejected"
msgstr "odmítnut"
#: plugins/omemo/src/ui/manage_key_dialog.vala:141
#: plugins/omemo/src/ui/manage_key_dialog.vala:140
#, c-format
msgid ""
"This means it cannot be used by %s to decipher your messages, and you won't "
@ -172,7 +172,7 @@ msgstr ""
"To znamená, že jej %s nemůže použít k dešifrování zpráv a vy neuvidíte "
"zprávy šifrované tímto klíčem."
#: plugins/omemo/src/ui/manage_key_dialog.vala:153
#: plugins/omemo/src/ui/manage_key_dialog.vala:152
#, c-format
msgid ""
"You won't see encrypted messages from the device of %s that uses this key. "
@ -181,7 +181,7 @@ msgstr ""
"Ze zařízení %s, které používá tento klíč, se šifrované zprávy nezobrazí. "
"Toto zařízení už také nebude schopno rozluštit vaše zprávy."
#: plugins/omemo/src/ui/manage_key_dialog.vala:161
#: plugins/omemo/src/ui/manage_key_dialog.vala:160
#, c-format
msgid ""
"You will be able to exchange encrypted messages with the device of %s that "
@ -190,7 +190,7 @@ msgstr ""
"Budete si moci vyměňovat šifrované zprávy se zařízením %s, které tento klíč "
"používá."
#: plugins/omemo/src/ui/manage_key_dialog.vala:167
#: plugins/omemo/src/ui/manage_key_dialog.vala:166
msgid "Back"
msgstr "Zpět"
@ -202,15 +202,6 @@ msgstr "Spravovat"
msgid "This contact has new devices"
msgstr "Tento kontakt má nová zařízení"
#: plugins/omemo/src/ui/account_settings_widget.vala:42
#: plugins/omemo/src/ui/account_settings_widget.vala:45
msgid "Own fingerprint"
msgstr "Otisk vlastního klíče"
#: plugins/omemo/src/ui/account_settings_widget.vala:42
msgid "Will be generated on first connection"
msgstr "Budou generovány při prvním připojení"
#: plugins/omemo/src/ui/own_notifications.vala:29
msgid "OMEMO trust decision required"
msgstr "Vyžaduje se rozhodnutí o důvěře OMEMO"
@ -220,6 +211,15 @@ msgstr "Vyžaduje se rozhodnutí o důvěře OMEMO"
msgid "Did you add a new device for account %s?"
msgstr "Přidali jste nové zařízení pro účet %s?"
#: plugins/omemo/src/ui/account_settings_entry.vala:47
#: plugins/omemo/src/ui/account_settings_entry.vala:50
msgid "Own fingerprint"
msgstr "Otisk vlastního klíče"
#: plugins/omemo/src/ui/account_settings_entry.vala:47
msgid "Will be generated on first connection"
msgstr "Budou generovány při prvním připojení"
#: plugins/omemo/src/ui/contact_details_provider.vala:42
msgid "Encryption"
msgstr "Šifrování"
@ -232,52 +232,52 @@ msgstr[0] "%d OMEMO zařízení"
msgstr[1] "%d OMEMO zařízení"
msgstr[2] "%d OMEMO zařízení"
#: plugins/omemo/src/ui/contact_details_dialog.vala:46
#: plugins/omemo/src/ui/contact_details_dialog.vala:48
msgid "OMEMO Key Management"
msgstr "OMEMO Správa klíčů"
#: plugins/omemo/src/ui/contact_details_dialog.vala:47
#: plugins/omemo/src/ui/contact_details_dialog.vala:49
msgid "Automatically accept new keys"
msgstr "Automaticky přijímat nové klíče"
#: plugins/omemo/src/ui/contact_details_dialog.vala:48
#: plugins/omemo/src/ui/contact_details_dialog.vala:50
msgid "New encryption keys from this contact will be accepted automatically."
msgstr "Nové šifrovací klíče od tohoto kontaktu budou automaticky přijímány."
#: plugins/omemo/src/ui/contact_details_dialog.vala:49
#: plugins/omemo/src/ui/contact_details_dialog.vala:51
msgid "Own key"
msgstr "Vlastní klíč"
#: plugins/omemo/src/ui/contact_details_dialog.vala:50
#: plugins/omemo/src/ui/contact_details_dialog.vala:52
msgid "New keys"
msgstr "Nové klíče"
#: plugins/omemo/src/ui/contact_details_dialog.vala:51
#: plugins/omemo/src/ui/contact_details_dialog.vala:53
msgid "Associated keys"
msgstr "Přidružené klíče"
#: plugins/omemo/src/ui/contact_details_dialog.vala:52
#: plugins/omemo/src/ui/contact_details_dialog.vala:54
msgid "Inactive keys"
msgstr "Neaktivní klíče"
#: plugins/omemo/src/ui/contact_details_dialog.vala:84
#: plugins/omemo/src/ui/contact_details_dialog.vala:86
msgid ""
"New encryption keys from your other devices will be accepted automatically."
msgstr ""
"Nové šifrovací klíče z vašich ostatních zařízení budou automaticky přijímány."
#: plugins/omemo/src/ui/contact_details_dialog.vala:319
#: plugins/omemo/src/ui/contact_details_dialog.vala:335
msgid "Accepted"
msgstr "Přijat"
#: plugins/omemo/src/ui/contact_details_dialog.vala:324
#: plugins/omemo/src/ui/contact_details_dialog.vala:340
msgid "Rejected"
msgstr "Odmítnut"
#: plugins/omemo/src/ui/contact_details_dialog.vala:329
#: plugins/omemo/src/ui/contact_details_dialog.vala:345
msgid "Verified"
msgstr "Ověřen"
#: plugins/omemo/src/ui/contact_details_dialog.vala:336
#: plugins/omemo/src/ui/contact_details_dialog.vala:352
msgid "Unused"
msgstr "Nepoužit"

View file

@ -2,7 +2,7 @@ msgid ""
msgstr ""
"Project-Id-Version: dino-omemo-0.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-02-12 22:06+0100\n"
"POT-Creation-Date: 2023-01-28 14:28+0100\n"
"PO-Revision-Date: 2020-10-10 10:49+0000\n"
"Language-Team: German <https://hosted.weblate.org/projects/dino/plugin-omemo/"
"de/>\n"
@ -33,11 +33,11 @@ msgid ""
msgstr ""
"%s akzeptiert dieses Gerät nicht. Deshalb verpasst du vielleicht Nachrichten."
#: plugins/omemo/src/ui/manage_key_dialog.vala:35
#: plugins/omemo/src/ui/manage_key_dialog.vala:34
msgid "Manage Key"
msgstr "Schlüssel verwalten"
#: plugins/omemo/src/ui/manage_key_dialog.vala:36
#: plugins/omemo/src/ui/manage_key_dialog.vala:35
msgid ""
"Compare the fingerprint, character by character, with the one shown on your "
"contact's device."
@ -45,29 +45,29 @@ msgstr ""
"Vergleiche den Fingerabdruck, Zeichen für Zeichen, mit dem der auf dem Gerät "
"deines Kontakts angezeigt wird."
#: plugins/omemo/src/ui/manage_key_dialog.vala:37
#: plugins/omemo/src/ui/manage_key_dialog.vala:36
msgid "Fingerprints differ"
msgstr "Fingerabdrücke unterscheiden sich"
#: plugins/omemo/src/ui/manage_key_dialog.vala:38
#: plugins/omemo/src/ui/manage_key_dialog.vala:37
msgid "Fingerprints match"
msgstr "Fingerabdrücke stimmen überein"
#: plugins/omemo/src/ui/manage_key_dialog.vala:39
#: plugins/omemo/src/ui/manage_key_dialog.vala:82
#: plugins/omemo/src/ui/manage_key_dialog.vala:88
#: plugins/omemo/src/ui/manage_key_dialog.vala:38
#: plugins/omemo/src/ui/manage_key_dialog.vala:81
#: plugins/omemo/src/ui/manage_key_dialog.vala:87
msgid "Cancel"
msgstr "Abbrechen"
#: plugins/omemo/src/ui/manage_key_dialog.vala:40
#: plugins/omemo/src/ui/manage_key_dialog.vala:39
msgid "Confirm"
msgstr "Bestätigen"
#: plugins/omemo/src/ui/manage_key_dialog.vala:60
#: plugins/omemo/src/ui/manage_key_dialog.vala:59
msgid "Verify key"
msgstr "Schlüssel verifizieren"
#: plugins/omemo/src/ui/manage_key_dialog.vala:61
#: plugins/omemo/src/ui/manage_key_dialog.vala:60
#, c-format
msgid ""
"Future messages sent by %s from the device that uses this key will be "
@ -76,11 +76,11 @@ msgstr ""
"Zukünftige Nachrichten, die von %s von dem Gerät gesendet werden, das diesen "
"Schlüssel verwendet, werden im Chat-Fenster entsprechend hervorgehoben."
#: plugins/omemo/src/ui/manage_key_dialog.vala:71
#: plugins/omemo/src/ui/manage_key_dialog.vala:70
msgid "Fingerprints do not match"
msgstr "Fingerabdrücke stimmen nicht überein"
#: plugins/omemo/src/ui/manage_key_dialog.vala:72
#: plugins/omemo/src/ui/manage_key_dialog.vala:71
#, c-format
msgid ""
"Please verify that you are comparing the correct fingerprint. If "
@ -91,11 +91,11 @@ msgstr ""
"Fingerabdrücke nicht übereinstimmen, ist das Konto von %s möglicherweise "
"kompromittiert und du solltest in Betracht ziehen, den Schlüssel abzulehnen."
#: plugins/omemo/src/ui/manage_key_dialog.vala:124
#: plugins/omemo/src/ui/manage_key_dialog.vala:123
msgid "Verify key fingerprint"
msgstr "Fingerabdruck überprüfen"
#: plugins/omemo/src/ui/manage_key_dialog.vala:124
#: plugins/omemo/src/ui/manage_key_dialog.vala:123
msgid ""
"Compare this key's fingerprint with the fingerprint displayed on the "
"contact's device."
@ -103,67 +103,67 @@ msgstr ""
"Diesen Fingerabdruck mit dem Fingerabdruck vergleichen, der auf dem Gerät "
"des Kontaktes angezeigt wird."
#: plugins/omemo/src/ui/manage_key_dialog.vala:126
#: plugins/omemo/src/ui/manage_key_dialog.vala:152
#: plugins/omemo/src/ui/contact_details_dialog.vala:256
#: plugins/omemo/src/ui/manage_key_dialog.vala:125
#: plugins/omemo/src/ui/manage_key_dialog.vala:151
#: plugins/omemo/src/ui/contact_details_dialog.vala:269
msgid "Reject key"
msgstr "Schlüssel ablehnen"
#: plugins/omemo/src/ui/manage_key_dialog.vala:126
#: plugins/omemo/src/ui/manage_key_dialog.vala:125
msgid ""
"Block encrypted communication with the contact's device that uses this key."
msgstr ""
"Blockiert die verschlüsselte Kommunikation mit dem Gerät des Kontakts, das "
"diesen Schlüssel verwendet."
#: plugins/omemo/src/ui/manage_key_dialog.vala:128
#: plugins/omemo/src/ui/manage_key_dialog.vala:160
#: plugins/omemo/src/ui/contact_details_dialog.vala:251
#: plugins/omemo/src/ui/manage_key_dialog.vala:127
#: plugins/omemo/src/ui/manage_key_dialog.vala:159
#: plugins/omemo/src/ui/contact_details_dialog.vala:264
msgid "Accept key"
msgstr "Schlüssel akzeptieren"
#: plugins/omemo/src/ui/manage_key_dialog.vala:128
#: plugins/omemo/src/ui/manage_key_dialog.vala:127
msgid ""
"Allow encrypted communication with the contact's device that uses this key."
msgstr ""
"Erlaubt die verschlüsselte Kommunikation mit dem Gerät des Kontakts, das "
"diesen Schlüssel verwendet."
#: plugins/omemo/src/ui/manage_key_dialog.vala:132
#: plugins/omemo/src/ui/manage_key_dialog.vala:137
#: plugins/omemo/src/ui/manage_key_dialog.vala:141
#: plugins/omemo/src/ui/manage_key_dialog.vala:131
#: plugins/omemo/src/ui/manage_key_dialog.vala:136
#: plugins/omemo/src/ui/manage_key_dialog.vala:140
#, c-format
msgid "This key is currently %s."
msgstr "Dieser Schlüssel ist aktuell %s."
#: plugins/omemo/src/ui/manage_key_dialog.vala:132
#: plugins/omemo/src/ui/manage_key_dialog.vala:131
msgid "accepted"
msgstr "akzeptiert"
#: plugins/omemo/src/ui/manage_key_dialog.vala:132
#: plugins/omemo/src/ui/manage_key_dialog.vala:137
#: plugins/omemo/src/ui/manage_key_dialog.vala:131
#: plugins/omemo/src/ui/manage_key_dialog.vala:136
#, c-format
msgid "This means it can be used by %s to receive and send encrypted messages."
msgstr ""
"Das bedeutet, er kann von %s zum Empfangen und Senden verschlüsselter "
"Nachrichten verwendet werden."
#: plugins/omemo/src/ui/manage_key_dialog.vala:137
#: plugins/omemo/src/ui/manage_key_dialog.vala:136
msgid "verified"
msgstr "überprüft"
#: plugins/omemo/src/ui/manage_key_dialog.vala:137
#: plugins/omemo/src/ui/manage_key_dialog.vala:136
msgid ""
"Additionally it has been verified to match the key on the contact's device."
msgstr ""
"Darüber hinaus wurde überprüft, dass er mit dem Schlüssel auf dem Gerät des "
"Kontaktes übereinstimmt."
#: plugins/omemo/src/ui/manage_key_dialog.vala:141
#: plugins/omemo/src/ui/manage_key_dialog.vala:140
msgid "rejected"
msgstr "abgelehnt"
#: plugins/omemo/src/ui/manage_key_dialog.vala:141
#: plugins/omemo/src/ui/manage_key_dialog.vala:140
#, c-format
msgid ""
"This means it cannot be used by %s to decipher your messages, and you won't "
@ -172,7 +172,7 @@ msgstr ""
"Das bedeutet %s kann ihn nicht benutzen, um deine Nachrichten zu "
"entschlüsseln, und du wirst damit verschlüsselte Nachrichten nicht sehen."
#: plugins/omemo/src/ui/manage_key_dialog.vala:153
#: plugins/omemo/src/ui/manage_key_dialog.vala:152
#, c-format
msgid ""
"You won't see encrypted messages from the device of %s that uses this key. "
@ -182,7 +182,7 @@ msgstr ""
"Schlüssel benutzt nicht mehr sehen. Umgekehrt kann dieses Gerät deine "
"Nachrichten nicht mehr entschlüsseln."
#: plugins/omemo/src/ui/manage_key_dialog.vala:161
#: plugins/omemo/src/ui/manage_key_dialog.vala:160
#, c-format
msgid ""
"You will be able to exchange encrypted messages with the device of %s that "
@ -191,7 +191,7 @@ msgstr ""
"Du wirst verschlüsselte Nachrichten mit dem Gerät von %s, welches diesen "
"Schlüssel verwendet, austauschen können."
#: plugins/omemo/src/ui/manage_key_dialog.vala:167
#: plugins/omemo/src/ui/manage_key_dialog.vala:166
msgid "Back"
msgstr "Zurück"
@ -203,15 +203,6 @@ msgstr "Verwalten"
msgid "This contact has new devices"
msgstr "Dieser Kontakt hat neue Geräte"
#: plugins/omemo/src/ui/account_settings_widget.vala:42
#: plugins/omemo/src/ui/account_settings_widget.vala:45
msgid "Own fingerprint"
msgstr "Eigener Fingerabdruck"
#: plugins/omemo/src/ui/account_settings_widget.vala:42
msgid "Will be generated on first connection"
msgstr "Wird beim ersten Verbinden erzeugt"
#: plugins/omemo/src/ui/own_notifications.vala:29
msgid "OMEMO trust decision required"
msgstr "OMEMO-Vertrauensentscheidung erforderlich"
@ -221,6 +212,15 @@ msgstr "OMEMO-Vertrauensentscheidung erforderlich"
msgid "Did you add a new device for account %s?"
msgstr "Hast du ein neues Gerät für deinen Account %s hinzugefügt?"
#: plugins/omemo/src/ui/account_settings_entry.vala:47
#: plugins/omemo/src/ui/account_settings_entry.vala:50
msgid "Own fingerprint"
msgstr "Eigener Fingerabdruck"
#: plugins/omemo/src/ui/account_settings_entry.vala:47
msgid "Will be generated on first connection"
msgstr "Wird beim ersten Verbinden erzeugt"
#: plugins/omemo/src/ui/contact_details_provider.vala:42
msgid "Encryption"
msgstr "Verschlüsselung"
@ -232,53 +232,53 @@ msgid_plural "%d OMEMO devices"
msgstr[0] "%d OMEMO Gerät"
msgstr[1] "%d OMEMO Geräte"
#: plugins/omemo/src/ui/contact_details_dialog.vala:46
#: plugins/omemo/src/ui/contact_details_dialog.vala:48
msgid "OMEMO Key Management"
msgstr "OMEMO-Schlüssel Verwaltung"
#: plugins/omemo/src/ui/contact_details_dialog.vala:47
#: plugins/omemo/src/ui/contact_details_dialog.vala:49
msgid "Automatically accept new keys"
msgstr "Neue Schlüssel automatisch akzeptieren"
#: plugins/omemo/src/ui/contact_details_dialog.vala:48
#: plugins/omemo/src/ui/contact_details_dialog.vala:50
msgid "New encryption keys from this contact will be accepted automatically."
msgstr "Neue Schlüssel von diesem Kontakt werden automatisch akzeptiert."
#: plugins/omemo/src/ui/contact_details_dialog.vala:49
#: plugins/omemo/src/ui/contact_details_dialog.vala:51
msgid "Own key"
msgstr "Eigener Schlüssel"
#: plugins/omemo/src/ui/contact_details_dialog.vala:50
#: plugins/omemo/src/ui/contact_details_dialog.vala:52
msgid "New keys"
msgstr "Neue Schlüssel"
#: plugins/omemo/src/ui/contact_details_dialog.vala:51
#: plugins/omemo/src/ui/contact_details_dialog.vala:53
msgid "Associated keys"
msgstr "Zugehörige Schlüssel"
#: plugins/omemo/src/ui/contact_details_dialog.vala:52
#: plugins/omemo/src/ui/contact_details_dialog.vala:54
msgid "Inactive keys"
msgstr "Inaktive Schlüssel"
#: plugins/omemo/src/ui/contact_details_dialog.vala:84
#: plugins/omemo/src/ui/contact_details_dialog.vala:86
msgid ""
"New encryption keys from your other devices will be accepted automatically."
msgstr ""
"Neue Schlüssel von deinen anderen Geräten werden automatisch akzeptiert."
#: plugins/omemo/src/ui/contact_details_dialog.vala:319
#: plugins/omemo/src/ui/contact_details_dialog.vala:335
msgid "Accepted"
msgstr "Akzeptiert"
#: plugins/omemo/src/ui/contact_details_dialog.vala:324
#: plugins/omemo/src/ui/contact_details_dialog.vala:340
msgid "Rejected"
msgstr "Zurückgewiesen"
#: plugins/omemo/src/ui/contact_details_dialog.vala:329
#: plugins/omemo/src/ui/contact_details_dialog.vala:345
msgid "Verified"
msgstr "Verifiziert"
#: plugins/omemo/src/ui/contact_details_dialog.vala:336
#: plugins/omemo/src/ui/contact_details_dialog.vala:352
msgid "Unused"
msgstr "Unbenutzt"

View file

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-02-12 22:06+0100\n"
"POT-Creation-Date: 2023-01-28 14:28+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -35,50 +35,50 @@ msgid ""
"%s does not trust this device. That means, you might be missing messages."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:35
#: plugins/omemo/src/ui/manage_key_dialog.vala:34
msgid "Manage Key"
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:36
#: plugins/omemo/src/ui/manage_key_dialog.vala:35
msgid ""
"Compare the fingerprint, character by character, with the one shown on your "
"contact's device."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:37
#: plugins/omemo/src/ui/manage_key_dialog.vala:36
msgid "Fingerprints differ"
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:38
#: plugins/omemo/src/ui/manage_key_dialog.vala:37
msgid "Fingerprints match"
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:39
#: plugins/omemo/src/ui/manage_key_dialog.vala:82
#: plugins/omemo/src/ui/manage_key_dialog.vala:88
#: plugins/omemo/src/ui/manage_key_dialog.vala:38
#: plugins/omemo/src/ui/manage_key_dialog.vala:81
#: plugins/omemo/src/ui/manage_key_dialog.vala:87
msgid "Cancel"
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:40
#: plugins/omemo/src/ui/manage_key_dialog.vala:39
msgid "Confirm"
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:60
#: plugins/omemo/src/ui/manage_key_dialog.vala:59
msgid "Verify key"
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:61
#: plugins/omemo/src/ui/manage_key_dialog.vala:60
#, c-format
msgid ""
"Future messages sent by %s from the device that uses this key will be "
"highlighted accordingly in the chat window."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:71
#: plugins/omemo/src/ui/manage_key_dialog.vala:70
msgid "Fingerprints do not match"
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:72
#: plugins/omemo/src/ui/manage_key_dialog.vala:71
#, c-format
msgid ""
"Please verify that you are comparing the correct fingerprint. If "
@ -86,90 +86,90 @@ msgid ""
"consider rejecting this key."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:124
#: plugins/omemo/src/ui/manage_key_dialog.vala:123
msgid "Verify key fingerprint"
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:124
#: plugins/omemo/src/ui/manage_key_dialog.vala:123
msgid ""
"Compare this key's fingerprint with the fingerprint displayed on the "
"contact's device."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:126
#: plugins/omemo/src/ui/manage_key_dialog.vala:152
#: plugins/omemo/src/ui/contact_details_dialog.vala:256
#: plugins/omemo/src/ui/manage_key_dialog.vala:125
#: plugins/omemo/src/ui/manage_key_dialog.vala:151
#: plugins/omemo/src/ui/contact_details_dialog.vala:269
msgid "Reject key"
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:126
#: plugins/omemo/src/ui/manage_key_dialog.vala:125
msgid ""
"Block encrypted communication with the contact's device that uses this key."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:128
#: plugins/omemo/src/ui/manage_key_dialog.vala:160
#: plugins/omemo/src/ui/contact_details_dialog.vala:251
#: plugins/omemo/src/ui/manage_key_dialog.vala:127
#: plugins/omemo/src/ui/manage_key_dialog.vala:159
#: plugins/omemo/src/ui/contact_details_dialog.vala:264
msgid "Accept key"
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:128
#: plugins/omemo/src/ui/manage_key_dialog.vala:127
msgid ""
"Allow encrypted communication with the contact's device that uses this key."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:132
#: plugins/omemo/src/ui/manage_key_dialog.vala:137
#: plugins/omemo/src/ui/manage_key_dialog.vala:141
#: plugins/omemo/src/ui/manage_key_dialog.vala:131
#: plugins/omemo/src/ui/manage_key_dialog.vala:136
#: plugins/omemo/src/ui/manage_key_dialog.vala:140
#, c-format
msgid "This key is currently %s."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:132
#: plugins/omemo/src/ui/manage_key_dialog.vala:131
msgid "accepted"
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:132
#: plugins/omemo/src/ui/manage_key_dialog.vala:137
#: plugins/omemo/src/ui/manage_key_dialog.vala:131
#: plugins/omemo/src/ui/manage_key_dialog.vala:136
#, c-format
msgid "This means it can be used by %s to receive and send encrypted messages."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:137
#: plugins/omemo/src/ui/manage_key_dialog.vala:136
msgid "verified"
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:137
#: plugins/omemo/src/ui/manage_key_dialog.vala:136
msgid ""
"Additionally it has been verified to match the key on the contact's device."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:141
#: plugins/omemo/src/ui/manage_key_dialog.vala:140
msgid "rejected"
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:141
#: plugins/omemo/src/ui/manage_key_dialog.vala:140
#, c-format
msgid ""
"This means it cannot be used by %s to decipher your messages, and you won't "
"see messages encrypted with it."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:153
#: plugins/omemo/src/ui/manage_key_dialog.vala:152
#, c-format
msgid ""
"You won't see encrypted messages from the device of %s that uses this key. "
"Conversely, that device won't be able to decipher your messages anymore."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:161
#: plugins/omemo/src/ui/manage_key_dialog.vala:160
#, c-format
msgid ""
"You will be able to exchange encrypted messages with the device of %s that "
"uses this key."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:167
#: plugins/omemo/src/ui/manage_key_dialog.vala:166
msgid "Back"
msgstr ""
@ -181,15 +181,6 @@ msgstr ""
msgid "This contact has new devices"
msgstr ""
#: plugins/omemo/src/ui/account_settings_widget.vala:42
#: plugins/omemo/src/ui/account_settings_widget.vala:45
msgid "Own fingerprint"
msgstr ""
#: plugins/omemo/src/ui/account_settings_widget.vala:42
msgid "Will be generated on first connection"
msgstr ""
#: plugins/omemo/src/ui/own_notifications.vala:29
msgid "OMEMO trust decision required"
msgstr ""
@ -199,6 +190,15 @@ msgstr ""
msgid "Did you add a new device for account %s?"
msgstr ""
#: plugins/omemo/src/ui/account_settings_entry.vala:47
#: plugins/omemo/src/ui/account_settings_entry.vala:50
msgid "Own fingerprint"
msgstr ""
#: plugins/omemo/src/ui/account_settings_entry.vala:47
msgid "Will be generated on first connection"
msgstr ""
#: plugins/omemo/src/ui/contact_details_provider.vala:42
msgid "Encryption"
msgstr ""
@ -210,51 +210,51 @@ msgid_plural "%d OMEMO devices"
msgstr[0] ""
msgstr[1] ""
#: plugins/omemo/src/ui/contact_details_dialog.vala:46
#: plugins/omemo/src/ui/contact_details_dialog.vala:48
msgid "OMEMO Key Management"
msgstr ""
#: plugins/omemo/src/ui/contact_details_dialog.vala:47
#: plugins/omemo/src/ui/contact_details_dialog.vala:49
msgid "Automatically accept new keys"
msgstr ""
#: plugins/omemo/src/ui/contact_details_dialog.vala:48
#: plugins/omemo/src/ui/contact_details_dialog.vala:50
msgid "New encryption keys from this contact will be accepted automatically."
msgstr ""
#: plugins/omemo/src/ui/contact_details_dialog.vala:49
#: plugins/omemo/src/ui/contact_details_dialog.vala:51
msgid "Own key"
msgstr ""
#: plugins/omemo/src/ui/contact_details_dialog.vala:50
#: plugins/omemo/src/ui/contact_details_dialog.vala:52
msgid "New keys"
msgstr ""
#: plugins/omemo/src/ui/contact_details_dialog.vala:51
#: plugins/omemo/src/ui/contact_details_dialog.vala:53
msgid "Associated keys"
msgstr ""
#: plugins/omemo/src/ui/contact_details_dialog.vala:52
#: plugins/omemo/src/ui/contact_details_dialog.vala:54
msgid "Inactive keys"
msgstr ""
#: plugins/omemo/src/ui/contact_details_dialog.vala:84
#: plugins/omemo/src/ui/contact_details_dialog.vala:86
msgid ""
"New encryption keys from your other devices will be accepted automatically."
msgstr ""
#: plugins/omemo/src/ui/contact_details_dialog.vala:319
#: plugins/omemo/src/ui/contact_details_dialog.vala:335
msgid "Accepted"
msgstr ""
#: plugins/omemo/src/ui/contact_details_dialog.vala:324
#: plugins/omemo/src/ui/contact_details_dialog.vala:340
msgid "Rejected"
msgstr ""
#: plugins/omemo/src/ui/contact_details_dialog.vala:329
#: plugins/omemo/src/ui/contact_details_dialog.vala:345
msgid "Verified"
msgstr ""
#: plugins/omemo/src/ui/contact_details_dialog.vala:336
#: plugins/omemo/src/ui/contact_details_dialog.vala:352
msgid "Unused"
msgstr ""

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-02-12 22:06+0100\n"
"POT-Creation-Date: 2023-01-28 14:28+0100\n"
"PO-Revision-Date: 2022-02-04 09:55+0000\n"
"Language-Team: none\n"
"Language: el\n"
@ -38,11 +38,11 @@ msgstr ""
"%s δεν εμπιστεύεται αυτήν τη συσκευή. Αυτό σημαίνει ότι μπορεί να χάσετε "
"μηνύματα."
#: plugins/omemo/src/ui/manage_key_dialog.vala:35
#: plugins/omemo/src/ui/manage_key_dialog.vala:34
msgid "Manage Key"
msgstr "Διαχείριση Κλειδιού"
#: plugins/omemo/src/ui/manage_key_dialog.vala:36
#: plugins/omemo/src/ui/manage_key_dialog.vala:35
msgid ""
"Compare the fingerprint, character by character, with the one shown on your "
"contact's device."
@ -50,29 +50,29 @@ msgstr ""
"Συγκρίνετε το δακτυλικό αποτύπωμα (fingerprint), χαρακτήρα προς χαρακτήρα, "
"με αυτό που εμφανίζεται στη συσκευή της επαφής σας."
#: plugins/omemo/src/ui/manage_key_dialog.vala:37
#: plugins/omemo/src/ui/manage_key_dialog.vala:36
msgid "Fingerprints differ"
msgstr "Τα δακτυλικά αποτυπώματα (fingerprints) διαφέρουν"
#: plugins/omemo/src/ui/manage_key_dialog.vala:38
#: plugins/omemo/src/ui/manage_key_dialog.vala:37
msgid "Fingerprints match"
msgstr "Τα δακτυλικά αποτυπώματα (fingerprints) ταιριάζουν"
#: plugins/omemo/src/ui/manage_key_dialog.vala:39
#: plugins/omemo/src/ui/manage_key_dialog.vala:82
#: plugins/omemo/src/ui/manage_key_dialog.vala:88
#: plugins/omemo/src/ui/manage_key_dialog.vala:38
#: plugins/omemo/src/ui/manage_key_dialog.vala:81
#: plugins/omemo/src/ui/manage_key_dialog.vala:87
msgid "Cancel"
msgstr "Ακύρωση"
#: plugins/omemo/src/ui/manage_key_dialog.vala:40
#: plugins/omemo/src/ui/manage_key_dialog.vala:39
msgid "Confirm"
msgstr "Επιβεβαίωση"
#: plugins/omemo/src/ui/manage_key_dialog.vala:60
#: plugins/omemo/src/ui/manage_key_dialog.vala:59
msgid "Verify key"
msgstr "Επαλήθευση κλειδιού"
#: plugins/omemo/src/ui/manage_key_dialog.vala:61
#: plugins/omemo/src/ui/manage_key_dialog.vala:60
#, c-format
msgid ""
"Future messages sent by %s from the device that uses this key will be "
@ -81,11 +81,11 @@ msgstr ""
"Μελλοντικά μηνύματα που αποστέλλονται από %s από τη συσκευή που χρησιμοποιεί "
"αυτό το κλειδί θα επισημαίνονται ανάλογα στο παράθυρο συνομιλίας."
#: plugins/omemo/src/ui/manage_key_dialog.vala:71
#: plugins/omemo/src/ui/manage_key_dialog.vala:70
msgid "Fingerprints do not match"
msgstr "Τα δακτυλικά αποτυπώματα (fingerprints) δεν ταιριάζουν"
#: plugins/omemo/src/ui/manage_key_dialog.vala:72
#: plugins/omemo/src/ui/manage_key_dialog.vala:71
#, c-format
msgid ""
"Please verify that you are comparing the correct fingerprint. If "
@ -97,11 +97,11 @@ msgstr ""
"μπορεί να παραβιαστεί και θα πρέπει να σκεφτείτε να απορρίψετε αυτό το "
"κλειδί."
#: plugins/omemo/src/ui/manage_key_dialog.vala:124
#: plugins/omemo/src/ui/manage_key_dialog.vala:123
msgid "Verify key fingerprint"
msgstr "Επαλήθευση δακτυλικού αποτυπώματος (fingerprint) κλειδιού"
#: plugins/omemo/src/ui/manage_key_dialog.vala:124
#: plugins/omemo/src/ui/manage_key_dialog.vala:123
msgid ""
"Compare this key's fingerprint with the fingerprint displayed on the "
"contact's device."
@ -109,66 +109,66 @@ msgstr ""
"Συγκρίνετε το δακτυλικό αποτύπωμα (fingerprint) αυτού του κλειδιού με το "
"δακτυλικό αποτύπωμα που εμφανίζεται στη συσκευή της επαφής."
#: plugins/omemo/src/ui/manage_key_dialog.vala:126
#: plugins/omemo/src/ui/manage_key_dialog.vala:152
#: plugins/omemo/src/ui/contact_details_dialog.vala:256
#: plugins/omemo/src/ui/manage_key_dialog.vala:125
#: plugins/omemo/src/ui/manage_key_dialog.vala:151
#: plugins/omemo/src/ui/contact_details_dialog.vala:269
msgid "Reject key"
msgstr "Απόρριψη κλειδιού"
#: plugins/omemo/src/ui/manage_key_dialog.vala:126
#: plugins/omemo/src/ui/manage_key_dialog.vala:125
msgid ""
"Block encrypted communication with the contact's device that uses this key."
msgstr ""
"Αποκλεισμός κρυπτογραφημένης επικοινωνίας με τη συσκευή της επαφής που "
"χρησιμοποιεί αυτό το κλειδί."
#: plugins/omemo/src/ui/manage_key_dialog.vala:128
#: plugins/omemo/src/ui/manage_key_dialog.vala:160
#: plugins/omemo/src/ui/contact_details_dialog.vala:251
#: plugins/omemo/src/ui/manage_key_dialog.vala:127
#: plugins/omemo/src/ui/manage_key_dialog.vala:159
#: plugins/omemo/src/ui/contact_details_dialog.vala:264
msgid "Accept key"
msgstr "Αποδοχή κλειδιού"
#: plugins/omemo/src/ui/manage_key_dialog.vala:128
#: plugins/omemo/src/ui/manage_key_dialog.vala:127
msgid ""
"Allow encrypted communication with the contact's device that uses this key."
msgstr ""
"Να επιτρέπεται η κρυπτογραφημένη επικοινωνία με τη συσκευή της επαφής που "
"χρησιμοποιεί αυτό το κλειδί."
#: plugins/omemo/src/ui/manage_key_dialog.vala:132
#: plugins/omemo/src/ui/manage_key_dialog.vala:137
#: plugins/omemo/src/ui/manage_key_dialog.vala:141
#: plugins/omemo/src/ui/manage_key_dialog.vala:131
#: plugins/omemo/src/ui/manage_key_dialog.vala:136
#: plugins/omemo/src/ui/manage_key_dialog.vala:140
#, c-format
msgid "This key is currently %s."
msgstr "Αυτό το κλειδί είναι αυτή τη στιγμή %s."
#: plugins/omemo/src/ui/manage_key_dialog.vala:132
#: plugins/omemo/src/ui/manage_key_dialog.vala:131
msgid "accepted"
msgstr "δεκτό"
#: plugins/omemo/src/ui/manage_key_dialog.vala:132
#: plugins/omemo/src/ui/manage_key_dialog.vala:137
#: plugins/omemo/src/ui/manage_key_dialog.vala:131
#: plugins/omemo/src/ui/manage_key_dialog.vala:136
#, c-format
msgid "This means it can be used by %s to receive and send encrypted messages."
msgstr ""
"Αυτό σημαίνει ότι μπορεί να χρησιμοποιηθεί από το %s για λήψη και αποστολή "
"κρυπτογραφημένων μηνυμάτων."
#: plugins/omemo/src/ui/manage_key_dialog.vala:137
#: plugins/omemo/src/ui/manage_key_dialog.vala:136
msgid "verified"
msgstr "επαληθευμένο"
#: plugins/omemo/src/ui/manage_key_dialog.vala:137
#: plugins/omemo/src/ui/manage_key_dialog.vala:136
msgid ""
"Additionally it has been verified to match the key on the contact's device."
msgstr ""
"Επιπλέον, έχει επαληθευτεί ότι ταιριάζει με το κλειδί στη συσκευή της επαφής."
#: plugins/omemo/src/ui/manage_key_dialog.vala:141
#: plugins/omemo/src/ui/manage_key_dialog.vala:140
msgid "rejected"
msgstr "απορριφθέν"
#: plugins/omemo/src/ui/manage_key_dialog.vala:141
#: plugins/omemo/src/ui/manage_key_dialog.vala:140
#, c-format
msgid ""
"This means it cannot be used by %s to decipher your messages, and you won't "
@ -178,7 +178,7 @@ msgstr ""
"αποκρυπτογράφηση των μηνυμάτων σας και δεν θα βλέπετε μηνύματα "
"κρυπτογραφημένα με αυτό."
#: plugins/omemo/src/ui/manage_key_dialog.vala:153
#: plugins/omemo/src/ui/manage_key_dialog.vala:152
#, c-format
msgid ""
"You won't see encrypted messages from the device of %s that uses this key. "
@ -188,7 +188,7 @@ msgstr ""
"χρησιμοποιούν αυτό το κλειδί. Αντίθετα, αυτή η συσκευή δεν θα μπορεί πλέον "
"να αποκρυπτογραφήσει τα μηνύματά σας."
#: plugins/omemo/src/ui/manage_key_dialog.vala:161
#: plugins/omemo/src/ui/manage_key_dialog.vala:160
#, c-format
msgid ""
"You will be able to exchange encrypted messages with the device of %s that "
@ -197,7 +197,7 @@ msgstr ""
"Θα μπορείτε να ανταλλάσσετε κρυπτογραφημένα μηνύματα με τη συσκευή των %s "
"που χρησιμοποιούν αυτό το κλειδί."
#: plugins/omemo/src/ui/manage_key_dialog.vala:167
#: plugins/omemo/src/ui/manage_key_dialog.vala:166
msgid "Back"
msgstr "Πίσω"
@ -209,15 +209,6 @@ msgstr "Διαχείρηση"
msgid "This contact has new devices"
msgstr "Αυτή η επαφή έχει νέες συσκευές"
#: plugins/omemo/src/ui/account_settings_widget.vala:42
#: plugins/omemo/src/ui/account_settings_widget.vala:45
msgid "Own fingerprint"
msgstr "Το δακτυλικό μου αποτύπωμα (fingerprint)"
#: plugins/omemo/src/ui/account_settings_widget.vala:42
msgid "Will be generated on first connection"
msgstr "Θα δημιουργηθεί με την πρώτη σύνδεση"
#: plugins/omemo/src/ui/own_notifications.vala:29
msgid "OMEMO trust decision required"
msgstr "Απαιτείται απόφαση εμπιστοσύνης OMEMO"
@ -227,6 +218,15 @@ msgstr "Απαιτείται απόφαση εμπιστοσύνης OMEMO"
msgid "Did you add a new device for account %s?"
msgstr "Προσθέσατε νέα συσκευή για τον λογαριασμό %s;"
#: plugins/omemo/src/ui/account_settings_entry.vala:47
#: plugins/omemo/src/ui/account_settings_entry.vala:50
msgid "Own fingerprint"
msgstr "Το δακτυλικό μου αποτύπωμα (fingerprint)"
#: plugins/omemo/src/ui/account_settings_entry.vala:47
msgid "Will be generated on first connection"
msgstr "Θα δημιουργηθεί με την πρώτη σύνδεση"
#: plugins/omemo/src/ui/contact_details_provider.vala:42
msgid "Encryption"
msgstr "Κρυπτογράφηση"
@ -238,54 +238,54 @@ msgid_plural "%d OMEMO devices"
msgstr[0] "%d OMEMO συσκευή"
msgstr[1] "%d OMEMO συσκευές"
#: plugins/omemo/src/ui/contact_details_dialog.vala:46
#: plugins/omemo/src/ui/contact_details_dialog.vala:48
msgid "OMEMO Key Management"
msgstr "Διαχείριση κλειδιών OMEMO"
#: plugins/omemo/src/ui/contact_details_dialog.vala:47
#: plugins/omemo/src/ui/contact_details_dialog.vala:49
msgid "Automatically accept new keys"
msgstr "Αυτόματη αποδοχή νέων κλειδιών"
#: plugins/omemo/src/ui/contact_details_dialog.vala:48
#: plugins/omemo/src/ui/contact_details_dialog.vala:50
msgid "New encryption keys from this contact will be accepted automatically."
msgstr ""
"Τα νέα κλειδιά κρυπτογράφησης από αυτήν την επαφή θα γίνονται δεκτά αυτόματα."
#: plugins/omemo/src/ui/contact_details_dialog.vala:49
#: plugins/omemo/src/ui/contact_details_dialog.vala:51
msgid "Own key"
msgstr "Το κλειδί μου"
#: plugins/omemo/src/ui/contact_details_dialog.vala:50
#: plugins/omemo/src/ui/contact_details_dialog.vala:52
msgid "New keys"
msgstr "Νέα κλειδιά"
#: plugins/omemo/src/ui/contact_details_dialog.vala:51
#: plugins/omemo/src/ui/contact_details_dialog.vala:53
msgid "Associated keys"
msgstr "Συσχετισμένα κλειδιά"
#: plugins/omemo/src/ui/contact_details_dialog.vala:52
#: plugins/omemo/src/ui/contact_details_dialog.vala:54
msgid "Inactive keys"
msgstr "Ανενεργά κλειδιά"
#: plugins/omemo/src/ui/contact_details_dialog.vala:84
#: plugins/omemo/src/ui/contact_details_dialog.vala:86
msgid ""
"New encryption keys from your other devices will be accepted automatically."
msgstr ""
"Τα νέα κλειδιά κρυπτογράφησης από τις άλλες συσκευές σας θα γίνονται δεκτά "
"αυτόματα."
#: plugins/omemo/src/ui/contact_details_dialog.vala:319
#: plugins/omemo/src/ui/contact_details_dialog.vala:335
msgid "Accepted"
msgstr "Αποδεκτά"
#: plugins/omemo/src/ui/contact_details_dialog.vala:324
#: plugins/omemo/src/ui/contact_details_dialog.vala:340
msgid "Rejected"
msgstr "Απορριφθέντα"
#: plugins/omemo/src/ui/contact_details_dialog.vala:329
#: plugins/omemo/src/ui/contact_details_dialog.vala:345
msgid "Verified"
msgstr "Επαληθευμένα"
#: plugins/omemo/src/ui/contact_details_dialog.vala:336
#: plugins/omemo/src/ui/contact_details_dialog.vala:352
msgid "Unused"
msgstr "Αχρησιμοποίητα"

View file

@ -2,7 +2,7 @@ msgid ""
msgstr ""
"Project-Id-Version: dino-omemo-0.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-02-12 22:06+0100\n"
"POT-Creation-Date: 2023-01-28 14:28+0100\n"
"Language: en\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@ -25,50 +25,50 @@ msgid ""
"%s does not trust this device. That means, you might be missing messages."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:35
#: plugins/omemo/src/ui/manage_key_dialog.vala:34
msgid "Manage Key"
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:36
#: plugins/omemo/src/ui/manage_key_dialog.vala:35
msgid ""
"Compare the fingerprint, character by character, with the one shown on your "
"contact's device."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:37
#: plugins/omemo/src/ui/manage_key_dialog.vala:36
msgid "Fingerprints differ"
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:38
#: plugins/omemo/src/ui/manage_key_dialog.vala:37
msgid "Fingerprints match"
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:39
#: plugins/omemo/src/ui/manage_key_dialog.vala:82
#: plugins/omemo/src/ui/manage_key_dialog.vala:88
#: plugins/omemo/src/ui/manage_key_dialog.vala:38
#: plugins/omemo/src/ui/manage_key_dialog.vala:81
#: plugins/omemo/src/ui/manage_key_dialog.vala:87
msgid "Cancel"
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:40
#: plugins/omemo/src/ui/manage_key_dialog.vala:39
msgid "Confirm"
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:60
#: plugins/omemo/src/ui/manage_key_dialog.vala:59
msgid "Verify key"
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:61
#: plugins/omemo/src/ui/manage_key_dialog.vala:60
#, c-format
msgid ""
"Future messages sent by %s from the device that uses this key will be "
"highlighted accordingly in the chat window."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:71
#: plugins/omemo/src/ui/manage_key_dialog.vala:70
msgid "Fingerprints do not match"
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:72
#: plugins/omemo/src/ui/manage_key_dialog.vala:71
#, c-format
msgid ""
"Please verify that you are comparing the correct fingerprint. If "
@ -76,90 +76,90 @@ msgid ""
"consider rejecting this key."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:124
#: plugins/omemo/src/ui/manage_key_dialog.vala:123
msgid "Verify key fingerprint"
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:124
#: plugins/omemo/src/ui/manage_key_dialog.vala:123
msgid ""
"Compare this key's fingerprint with the fingerprint displayed on the "
"contact's device."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:126
#: plugins/omemo/src/ui/manage_key_dialog.vala:152
#: plugins/omemo/src/ui/contact_details_dialog.vala:256
#: plugins/omemo/src/ui/manage_key_dialog.vala:125
#: plugins/omemo/src/ui/manage_key_dialog.vala:151
#: plugins/omemo/src/ui/contact_details_dialog.vala:269
msgid "Reject key"
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:126
#: plugins/omemo/src/ui/manage_key_dialog.vala:125
msgid ""
"Block encrypted communication with the contact's device that uses this key."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:128
#: plugins/omemo/src/ui/manage_key_dialog.vala:160
#: plugins/omemo/src/ui/contact_details_dialog.vala:251
#: plugins/omemo/src/ui/manage_key_dialog.vala:127
#: plugins/omemo/src/ui/manage_key_dialog.vala:159
#: plugins/omemo/src/ui/contact_details_dialog.vala:264
msgid "Accept key"
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:128
#: plugins/omemo/src/ui/manage_key_dialog.vala:127
msgid ""
"Allow encrypted communication with the contact's device that uses this key."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:132
#: plugins/omemo/src/ui/manage_key_dialog.vala:137
#: plugins/omemo/src/ui/manage_key_dialog.vala:141
#: plugins/omemo/src/ui/manage_key_dialog.vala:131
#: plugins/omemo/src/ui/manage_key_dialog.vala:136
#: plugins/omemo/src/ui/manage_key_dialog.vala:140
#, c-format
msgid "This key is currently %s."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:132
#: plugins/omemo/src/ui/manage_key_dialog.vala:131
msgid "accepted"
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:132
#: plugins/omemo/src/ui/manage_key_dialog.vala:137
#: plugins/omemo/src/ui/manage_key_dialog.vala:131
#: plugins/omemo/src/ui/manage_key_dialog.vala:136
#, c-format
msgid "This means it can be used by %s to receive and send encrypted messages."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:137
#: plugins/omemo/src/ui/manage_key_dialog.vala:136
msgid "verified"
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:137
#: plugins/omemo/src/ui/manage_key_dialog.vala:136
msgid ""
"Additionally it has been verified to match the key on the contact's device."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:141
#: plugins/omemo/src/ui/manage_key_dialog.vala:140
msgid "rejected"
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:141
#: plugins/omemo/src/ui/manage_key_dialog.vala:140
#, c-format
msgid ""
"This means it cannot be used by %s to decipher your messages, and you won't "
"see messages encrypted with it."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:153
#: plugins/omemo/src/ui/manage_key_dialog.vala:152
#, c-format
msgid ""
"You won't see encrypted messages from the device of %s that uses this key. "
"Conversely, that device won't be able to decipher your messages anymore."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:161
#: plugins/omemo/src/ui/manage_key_dialog.vala:160
#, c-format
msgid ""
"You will be able to exchange encrypted messages with the device of %s that "
"uses this key."
msgstr ""
#: plugins/omemo/src/ui/manage_key_dialog.vala:167
#: plugins/omemo/src/ui/manage_key_dialog.vala:166
msgid "Back"
msgstr ""
@ -171,15 +171,6 @@ msgstr ""
msgid "This contact has new devices"
msgstr ""
#: plugins/omemo/src/ui/account_settings_widget.vala:42
#: plugins/omemo/src/ui/account_settings_widget.vala:45
msgid "Own fingerprint"
msgstr ""
#: plugins/omemo/src/ui/account_settings_widget.vala:42
msgid "Will be generated on first connection"
msgstr ""
#: plugins/omemo/src/ui/own_notifications.vala:29
msgid "OMEMO trust decision required"
msgstr ""
@ -189,6 +180,15 @@ msgstr ""
msgid "Did you add a new device for account %s?"
msgstr ""
#: plugins/omemo/src/ui/account_settings_entry.vala:47
#: plugins/omemo/src/ui/account_settings_entry.vala:50
msgid "Own fingerprint"
msgstr ""
#: plugins/omemo/src/ui/account_settings_entry.vala:47
msgid "Will be generated on first connection"
msgstr ""
#: plugins/omemo/src/ui/contact_details_provider.vala:42
msgid "Encryption"
msgstr ""
@ -200,51 +200,51 @@ msgid_plural "%d OMEMO devices"
msgstr[0] ""
msgstr[1] ""
#: plugins/omemo/src/ui/contact_details_dialog.vala:46
#: plugins/omemo/src/ui/contact_details_dialog.vala:48
msgid "OMEMO Key Management"
msgstr ""
#: plugins/omemo/src/ui/contact_details_dialog.vala:47
#: plugins/omemo/src/ui/contact_details_dialog.vala:49
msgid "Automatically accept new keys"
msgstr ""
#: plugins/omemo/src/ui/contact_details_dialog.vala:48
#: plugins/omemo/src/ui/contact_details_dialog.vala:50
msgid "New encryption keys from this contact will be accepted automatically."
msgstr ""
#: plugins/omemo/src/ui/contact_details_dialog.vala:49
#: plugins/omemo/src/ui/contact_details_dialog.vala:51
msgid "Own key"
msgstr ""
#: plugins/omemo/src/ui/contact_details_dialog.vala:50
#: plugins/omemo/src/ui/contact_details_dialog.vala:52
msgid "New keys"
msgstr ""
#: plugins/omemo/src/ui/contact_details_dialog.vala:51
#: plugins/omemo/src/ui/contact_details_dialog.vala:53
msgid "Associated keys"
msgstr ""
#: plugins/omemo/src/ui/contact_details_dialog.vala:52
#: plugins/omemo/src/ui/contact_details_dialog.vala:54
msgid "Inactive keys"
msgstr ""
#: plugins/omemo/src/ui/contact_details_dialog.vala:84
#: plugins/omemo/src/ui/contact_details_dialog.vala:86
msgid ""
"New encryption keys from your other devices will be accepted automatically."
msgstr ""
#: plugins/omemo/src/ui/contact_details_dialog.vala:319
#: plugins/omemo/src/ui/contact_details_dialog.vala:335
msgid "Accepted"
msgstr ""
#: plugins/omemo/src/ui/contact_details_dialog.vala:324
#: plugins/omemo/src/ui/contact_details_dialog.vala:340
msgid "Rejected"
msgstr ""
#: plugins/omemo/src/ui/contact_details_dialog.vala:329
#: plugins/omemo/src/ui/contact_details_dialog.vala:345
msgid "Verified"
msgstr ""
#: plugins/omemo/src/ui/contact_details_dialog.vala:336
#: plugins/omemo/src/ui/contact_details_dialog.vala:352
msgid "Unused"
msgstr ""

Some files were not shown because too many files have changed in this diff Show more