diff --git a/Signal-Windows.sln b/Signal-Windows.sln
index 5e1310f..2d70456 100644
--- a/Signal-Windows.sln
+++ b/Signal-Windows.sln
@@ -1,7 +1,6 @@
-
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
-VisualStudioVersion = 15.0.26730.3
+VisualStudioVersion = 15.0.27004.2002
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Signal-Windows", "Signal-Windows\Signal-Windows.csproj", "{41736A64-5B66-44AF-879A-501192A46920}"
EndProject
diff --git a/Signal-Windows/Controls/AddContactListElement.xaml b/Signal-Windows/Controls/AddContactListElement.xaml
new file mode 100644
index 0000000..0968212
--- /dev/null
+++ b/Signal-Windows/Controls/AddContactListElement.xaml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Signal-Windows/Controls/AddContactListElement.xaml.cs b/Signal-Windows/Controls/AddContactListElement.xaml.cs
new file mode 100644
index 0000000..7e75722
--- /dev/null
+++ b/Signal-Windows/Controls/AddContactListElement.xaml.cs
@@ -0,0 +1,100 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices.WindowsRuntime;
+using Signal_Windows.Models;
+using Windows.Foundation;
+using Windows.Foundation.Collections;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Controls.Primitives;
+using Windows.UI.Xaml.Data;
+using Windows.UI.Xaml.Input;
+using Windows.UI.Xaml.Media;
+using Windows.UI.Xaml.Navigation;
+
+// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236
+
+namespace Signal_Windows.Controls
+{
+ public sealed partial class AddContactListElement : UserControl, INotifyPropertyChanged
+ {
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ public AddContactListElement()
+ {
+ this.InitializeComponent();
+ this.DataContextChanged += AddContactListElement_DataContextChanged;
+ }
+
+ public string _DisplayName;
+ public string DisplayName
+ {
+ get { return _DisplayName; }
+ set
+ {
+ _DisplayName = value;
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(DisplayName)));
+ }
+ }
+
+ public string _PhoneNumber;
+ public string PhoneNumber
+ {
+ get { return _PhoneNumber; }
+ set
+ {
+ _PhoneNumber = value;
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(PhoneNumber)));
+ }
+ }
+
+ public ImageSource _ContactPhoto = null;
+ public ImageSource ContactPhoto
+ {
+ get { return _ContactPhoto; }
+ set
+ {
+ _ContactPhoto = value;
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ContactPhoto)));
+ }
+ }
+
+ public bool _OnSignal;
+ public bool OnSignal
+ {
+ get { return _OnSignal; }
+ set
+ {
+ _OnSignal = value;
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(OnSignal)));
+ }
+ }
+
+ public PhoneContact Model
+ {
+ get
+ {
+ return DataContext as PhoneContact;
+ }
+ set
+ {
+ DataContext = value;
+ }
+ }
+
+ private void AddContactListElement_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
+ {
+ if (Model != null)
+ {
+ Model.View = this;
+ DisplayName = Model.Name;
+ PhoneNumber = Model.PhoneNumber;
+ ContactPhoto = Model.Photo;
+ OnSignal = Model.OnSignal;
+ }
+ }
+ }
+}
diff --git a/Signal-Windows/Controls/VirtualizedMessagesCollection.cs b/Signal-Windows/Controls/VirtualizedMessagesCollection.cs
index d659ceb..17909b7 100644
--- a/Signal-Windows/Controls/VirtualizedMessagesCollection.cs
+++ b/Signal-Windows/Controls/VirtualizedMessagesCollection.cs
@@ -1,230 +1,230 @@
-
-using Signal_Windows.Models;
-using Signal_Windows.Storage;
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Collections.Specialized;
-using System.Diagnostics;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Signal_Windows.Controls
-{
- public class SignalMessageContainer
- {
- public SignalMessage Message;
- public int Index;
- public SignalMessageContainer(SignalMessage message, int index)
- {
- Message = message;
- Index = index;
- }
- }
-
- public class SignalUnreadMarker
- {
- public string Text = "";
- }
-
- public class VirtualizedCollection : IList, INotifyCollectionChanged
- {
- private const int PAGE_SIZE = 50;
- public event NotifyCollectionChangedEventHandler CollectionChanged;
- private Dictionary> Cache = new Dictionary>();
- private SignalConversation Conversation;
- private SignalUnreadMarker UnreadMarker = new SignalUnreadMarker();
- public int UnreadMarkerIndex = -1;
-
- public VirtualizedCollection(SignalConversation c)
- {
- Conversation = c;
- if (Conversation.LastSeenMessageIndex > 0 && Conversation.LastSeenMessageIndex < Conversation.MessagesCount )
- {
- UnreadMarkerIndex = (int) Conversation.LastSeenMessageIndex;
- UnreadMarker.Text = Conversation.UnreadCount > 1 ? $"{Conversation.UnreadCount} new messages" : "1 new message";
- }
- else
- {
- UnreadMarkerIndex = -1;
- }
- }
-
- private static int GetPageIndex(int itemIndex)
- {
- return itemIndex / PAGE_SIZE;
- }
-
-
- public object this[int index]
- {
- get
- {
- if (UnreadMarkerIndex > 0)
- {
- if (index < UnreadMarkerIndex)
- {
- return Get(index);
- }
- else if (index == UnreadMarkerIndex)
- {
- return UnreadMarker;
- }
- else
- {
- return Get(index - 1);
- }
- }
- else
- {
- return Get(index);
- }
- }
- set => throw new NotImplementedException();
- }
-
- private SignalMessageContainer Get(int index)
- {
- int inpageIndex = index % PAGE_SIZE;
- int pageIndex = GetPageIndex(index);
- if (!Cache.ContainsKey(pageIndex))
- {
- Debug.WriteLine($"cache miss {pageIndex}");
- Cache[pageIndex] = SignalDBContext.GetMessagesLocked(Conversation, pageIndex * PAGE_SIZE, PAGE_SIZE);
- }
- var page = Cache[pageIndex];
- var item = page[inpageIndex];
- return page[inpageIndex];
- }
-
- public bool IsFixedSize => false;
-
- public bool IsReadOnly => false;
-
- public int Count
- {
- get
- {
- if (UnreadMarkerIndex > 0)
- {
- return (int)Conversation.MessagesCount + 1;
- }
- else
- {
- return (int)Conversation.MessagesCount;
- }
- }
- }
-
- public bool IsSynchronized => false;
-
- public object SyncRoot => this;
-
- ///
- /// "Adds" a SignalMessageContainer to this virtualized collection.
- ///
- /// The message may (if incoming) or may not (if outgoing) already be present in the database, so we explicitly insert at the correct position in the cache line.
- /// Count is mapped to the SignalConversation's MessagesCount, so callers must update appropriately before calling this method, and no async method must be called in between.
- /// The object to add to the VirtualizedMessagesCollection.
- /// The position into which the new element was inserted, or -1 to indicate that the item was not inserted into the collection.
- public int Add(object value, bool forcedScroll)
- {
- if (forcedScroll && UnreadMarkerIndex > 0)
- {
- var old = UnreadMarkerIndex;
- UnreadMarkerIndex = -1;
- CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, UnreadMarker, old));
- }
- var message = value as SignalMessageContainer;
- int inpageIndex = message.Index % PAGE_SIZE;
- int pageIndex = GetPageIndex(message.Index);
- Debug.WriteLine($"VirtualizedCollection.Add Id={message.Message.Id} Index={message.Index} PageIndex={pageIndex} InpageIndex={inpageIndex} ");
- if (!Cache.ContainsKey(pageIndex))
- {
- Cache[pageIndex] = SignalDBContext.GetMessagesLocked(Conversation, pageIndex * PAGE_SIZE, PAGE_SIZE);
- }
- Cache[pageIndex].Insert(inpageIndex, message);
- int virtualIndex = GetVirtualIndex(message.Index);
- Debug.WriteLine($"NotifyCollectionChangedAction.Add index={message.Index} virtualIndex={virtualIndex}");
- CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, message, virtualIndex));
- return message.Index;
- }
-
- public void Clear()
- {
- throw new NotImplementedException();
- }
-
- public bool Contains(object value)
- {
- throw new NotImplementedException();
- }
-
- public void CopyTo(Array array, int index)
- {
- throw new NotImplementedException();
- }
-
- public IEnumerator GetEnumerator()
- {
- throw new NotImplementedException();
- }
-
- public int IndexOf(object value)
- {
- if (value is SignalMessageContainer)
- {
- SignalMessageContainer smc = (SignalMessageContainer) value;
- return GetVirtualIndex(smc.Index);
- }
- else if (value is SignalUnreadMarker)
- {
- return UnreadMarkerIndex;
- }
- else
- {
- return -1;
- }
- }
-
- internal int GetVirtualIndex(int rawIndex)
- {
- if (UnreadMarkerIndex > 0)
- {
- if (rawIndex < UnreadMarkerIndex)
- {
- return rawIndex;
- }
- else
- {
- return rawIndex + 1;
- }
- }
- else
- {
- return rawIndex;
- }
- }
-
- public void Insert(int index, object value)
- {
- throw new NotImplementedException();
- }
-
- public void Remove(object value)
- {
- throw new NotImplementedException();
- }
-
- public void RemoveAt(int index)
- {
- throw new NotImplementedException();
- }
-
- public int Add(object value)
- {
- throw new NotImplementedException();
- }
- }
-}
+
+using Signal_Windows.Models;
+using Signal_Windows.Storage;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Signal_Windows.Controls
+{
+ public class SignalMessageContainer
+ {
+ public SignalMessage Message;
+ public int Index;
+ public SignalMessageContainer(SignalMessage message, int index)
+ {
+ Message = message;
+ Index = index;
+ }
+ }
+
+ public class SignalUnreadMarker
+ {
+ public string Text = "";
+ }
+
+ public class VirtualizedCollection : IList, INotifyCollectionChanged
+ {
+ private const int PAGE_SIZE = 50;
+ public event NotifyCollectionChangedEventHandler CollectionChanged;
+ private Dictionary> Cache = new Dictionary>();
+ private SignalConversation Conversation;
+ private SignalUnreadMarker UnreadMarker = new SignalUnreadMarker();
+ public int UnreadMarkerIndex = -1;
+
+ public VirtualizedCollection(SignalConversation c)
+ {
+ Conversation = c;
+ if (Conversation.LastSeenMessageIndex > 0 && Conversation.LastSeenMessageIndex < Conversation.MessagesCount )
+ {
+ UnreadMarkerIndex = (int) Conversation.LastSeenMessageIndex;
+ UnreadMarker.Text = Conversation.UnreadCount > 1 ? $"{Conversation.UnreadCount} new messages" : "1 new message";
+ }
+ else
+ {
+ UnreadMarkerIndex = -1;
+ }
+ }
+
+ private static int GetPageIndex(int itemIndex)
+ {
+ return itemIndex / PAGE_SIZE;
+ }
+
+
+ public object this[int index]
+ {
+ get
+ {
+ if (UnreadMarkerIndex > 0)
+ {
+ if (index < UnreadMarkerIndex)
+ {
+ return Get(index);
+ }
+ else if (index == UnreadMarkerIndex)
+ {
+ return UnreadMarker;
+ }
+ else
+ {
+ return Get(index - 1);
+ }
+ }
+ else
+ {
+ return Get(index);
+ }
+ }
+ set => throw new NotImplementedException();
+ }
+
+ private SignalMessageContainer Get(int index)
+ {
+ int inpageIndex = index % PAGE_SIZE;
+ int pageIndex = GetPageIndex(index);
+ if (!Cache.ContainsKey(pageIndex))
+ {
+ Debug.WriteLine($"cache miss {pageIndex}");
+ Cache[pageIndex] = SignalDBContext.GetMessagesLocked(Conversation, pageIndex * PAGE_SIZE, PAGE_SIZE);
+ }
+ var page = Cache[pageIndex];
+ var item = page[inpageIndex];
+ return page[inpageIndex];
+ }
+
+ public bool IsFixedSize => false;
+
+ public bool IsReadOnly => false;
+
+ public int Count
+ {
+ get
+ {
+ if (UnreadMarkerIndex > 0)
+ {
+ return (int)Conversation.MessagesCount + 1;
+ }
+ else
+ {
+ return (int)Conversation.MessagesCount;
+ }
+ }
+ }
+
+ public bool IsSynchronized => false;
+
+ public object SyncRoot => this;
+
+ ///
+ /// "Adds" a SignalMessageContainer to this virtualized collection.
+ ///
+ /// The message may (if incoming) or may not (if outgoing) already be present in the database, so we explicitly insert at the correct position in the cache line.
+ /// Count is mapped to the SignalConversation's MessagesCount, so callers must update appropriately before calling this method, and no async method must be called in between.
+ /// The object to add to the VirtualizedMessagesCollection.
+ /// The position into which the new element was inserted, or -1 to indicate that the item was not inserted into the collection.
+ public int Add(object value, bool forcedScroll)
+ {
+ if (forcedScroll && UnreadMarkerIndex > 0)
+ {
+ var old = UnreadMarkerIndex;
+ UnreadMarkerIndex = -1;
+ CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, UnreadMarker, old));
+ }
+ var message = value as SignalMessageContainer;
+ int inpageIndex = message.Index % PAGE_SIZE;
+ int pageIndex = GetPageIndex(message.Index);
+ Debug.WriteLine($"VirtualizedCollection.Add Id={message.Message.Id} Index={message.Index} PageIndex={pageIndex} InpageIndex={inpageIndex} ");
+ if (!Cache.ContainsKey(pageIndex))
+ {
+ Cache[pageIndex] = SignalDBContext.GetMessagesLocked(Conversation, pageIndex * PAGE_SIZE, PAGE_SIZE);
+ }
+ Cache[pageIndex].Insert(inpageIndex, message);
+ int virtualIndex = GetVirtualIndex(message.Index);
+ Debug.WriteLine($"NotifyCollectionChangedAction.Add index={message.Index} virtualIndex={virtualIndex}");
+ CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, message, virtualIndex));
+ return message.Index;
+ }
+
+ public void Clear()
+ {
+ throw new NotImplementedException();
+ }
+
+ public bool Contains(object value)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void CopyTo(Array array, int index)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ throw new NotImplementedException();
+ }
+
+ public int IndexOf(object value)
+ {
+ if (value is SignalMessageContainer)
+ {
+ SignalMessageContainer smc = (SignalMessageContainer) value;
+ return GetVirtualIndex(smc.Index);
+ }
+ else if (value is SignalUnreadMarker)
+ {
+ return UnreadMarkerIndex;
+ }
+ else
+ {
+ return -1;
+ }
+ }
+
+ internal int GetVirtualIndex(int rawIndex)
+ {
+ if (UnreadMarkerIndex > 0)
+ {
+ if (rawIndex < UnreadMarkerIndex)
+ {
+ return rawIndex;
+ }
+ else
+ {
+ return rawIndex + 1;
+ }
+ }
+ else
+ {
+ return rawIndex;
+ }
+ }
+
+ public void Insert(int index, object value)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Remove(object value)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void RemoveAt(int index)
+ {
+ throw new NotImplementedException();
+ }
+
+ public int Add(object value)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/Signal-Windows/Migrations/SignalDB/20170901124533_m3.Designer.cs b/Signal-Windows/Migrations/SignalDB/20170901124533_m3.Designer.cs
index db49348..2242a92 100644
--- a/Signal-Windows/Migrations/SignalDB/20170901124533_m3.Designer.cs
+++ b/Signal-Windows/Migrations/SignalDB/20170901124533_m3.Designer.cs
@@ -1,252 +1,252 @@
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Metadata;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Signal_Windows.Storage;
-using Signal_Windows.Models;
-
-namespace Signal_Windows.Migrations
-{
- [DbContext(typeof(SignalDBContext))]
- [Migration("20170901124533_m3")]
- partial class m3
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
- modelBuilder
- .HasAnnotation("ProductVersion", "1.1.2");
-
- modelBuilder.Entity("Signal_Windows.Models.GroupMembership", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd();
-
- b.Property("ContactId");
-
- b.Property("GroupId");
-
- b.HasKey("Id");
-
- b.HasIndex("ContactId");
-
- b.HasIndex("GroupId");
-
- b.ToTable("GroupMemberships");
- });
-
- modelBuilder.Entity("Signal_Windows.Models.SignalAttachment", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd();
-
- b.Property("ContentType");
-
- b.Property("FileName");
-
- b.Property("Key");
-
- b.Property("MessageId");
-
- b.Property("Relay");
-
- b.Property("SentFileName");
-
- b.Property("Status");
-
- b.Property("StorageId");
-
- b.HasKey("Id");
-
- b.HasIndex("MessageId");
-
- b.ToTable("Attachments");
- });
-
- modelBuilder.Entity("Signal_Windows.Models.SignalConversation", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd();
-
- b.Property("AvatarFile");
-
- b.Property("CanReceive");
-
- b.Property("Discriminator")
- .IsRequired();
-
- b.Property("Draft");
-
- b.Property("ExpiresInSeconds");
-
- b.Property("LastActiveTimestamp");
-
- b.Property("LastMessageId");
-
- b.Property("LastSeenMessageId");
-
- b.Property("LastSeenMessageIndex");
-
- b.Property("MessagesCount");
-
- b.Property("ThreadDisplayName");
-
- b.Property("ThreadId");
-
- b.Property("UnreadCount");
-
- b.HasKey("Id");
-
- b.HasIndex("LastMessageId");
-
- b.HasIndex("LastSeenMessageId");
-
- b.HasIndex("ThreadId");
-
- b.ToTable("SignalConversation");
-
- b.HasDiscriminator("Discriminator").HasValue("SignalConversation");
- });
-
- modelBuilder.Entity("Signal_Windows.Models.SignalEarlyReceipt", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd();
-
- b.Property("DeviceId");
-
- b.Property("Timestamp");
-
- b.Property("Username");
-
- b.HasKey("Id");
-
- b.HasIndex("DeviceId");
-
- b.HasIndex("Timestamp");
-
- b.HasIndex("Username");
-
- b.ToTable("EarlyReceipts");
- });
-
- modelBuilder.Entity("Signal_Windows.Models.SignalMessage", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd();
-
- b.Property("AttachmentsCount");
-
- b.Property("AuthorId");
-
- b.Property("ComposedTimestamp");
-
- b.Property("Contentrowid");
-
- b.Property("DeviceId");
-
- b.Property("Direction");
-
- b.Property("ExpiresAt");
-
- b.Property("Read");
-
- b.Property("Receipts");
-
- b.Property("ReceivedTimestamp");
-
- b.Property("Status");
-
- b.Property("ThreadId");
-
- b.Property("Type");
-
- b.HasKey("Id");
-
- b.HasIndex("AuthorId");
-
- b.HasIndex("Contentrowid");
-
- b.HasIndex("ThreadId");
-
- b.ToTable("Messages");
- });
-
- modelBuilder.Entity("Signal_Windows.Models.SignalMessageContent", b =>
- {
- b.Property("rowid")
- .ValueGeneratedOnAdd();
-
- b.Property("Content");
-
- b.HasKey("rowid");
-
- b.ToTable("Messages_fts");
- });
-
- modelBuilder.Entity("Signal_Windows.Models.SignalContact", b =>
- {
- b.HasBaseType("Signal_Windows.Models.SignalConversation");
-
- b.Property("Color");
-
- b.ToTable("SignalContact");
-
- b.HasDiscriminator().HasValue("SignalContact");
- });
-
- modelBuilder.Entity("Signal_Windows.Models.SignalGroup", b =>
- {
- b.HasBaseType("Signal_Windows.Models.SignalConversation");
-
-
- b.ToTable("SignalGroup");
-
- b.HasDiscriminator().HasValue("SignalGroup");
- });
-
- modelBuilder.Entity("Signal_Windows.Models.GroupMembership", b =>
- {
- b.HasOne("Signal_Windows.Models.SignalContact", "Contact")
- .WithMany("GroupMemberships")
- .HasForeignKey("ContactId")
- .OnDelete(DeleteBehavior.Cascade);
-
- b.HasOne("Signal_Windows.Models.SignalGroup", "Group")
- .WithMany("GroupMemberships")
- .HasForeignKey("GroupId")
- .OnDelete(DeleteBehavior.Cascade);
- });
-
- modelBuilder.Entity("Signal_Windows.Models.SignalAttachment", b =>
- {
- b.HasOne("Signal_Windows.Models.SignalMessage", "Message")
- .WithMany("Attachments")
- .HasForeignKey("MessageId")
- .OnDelete(DeleteBehavior.Cascade);
- });
-
- modelBuilder.Entity("Signal_Windows.Models.SignalConversation", b =>
- {
- b.HasOne("Signal_Windows.Models.SignalMessage", "LastMessage")
- .WithMany()
- .HasForeignKey("LastMessageId");
-
- b.HasOne("Signal_Windows.Models.SignalMessage", "LastSeenMessage")
- .WithMany()
- .HasForeignKey("LastSeenMessageId");
- });
-
- modelBuilder.Entity("Signal_Windows.Models.SignalMessage", b =>
- {
- b.HasOne("Signal_Windows.Models.SignalContact", "Author")
- .WithMany()
- .HasForeignKey("AuthorId");
-
- b.HasOne("Signal_Windows.Models.SignalMessageContent", "Content")
- .WithMany()
- .HasForeignKey("Contentrowid");
- });
- }
- }
-}
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Signal_Windows.Storage;
+using Signal_Windows.Models;
+
+namespace Signal_Windows.Migrations
+{
+ [DbContext(typeof(SignalDBContext))]
+ [Migration("20170901124533_m3")]
+ partial class m3
+ {
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+ modelBuilder
+ .HasAnnotation("ProductVersion", "1.1.2");
+
+ modelBuilder.Entity("Signal_Windows.Models.GroupMembership", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("ContactId");
+
+ b.Property("GroupId");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ContactId");
+
+ b.HasIndex("GroupId");
+
+ b.ToTable("GroupMemberships");
+ });
+
+ modelBuilder.Entity("Signal_Windows.Models.SignalAttachment", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("ContentType");
+
+ b.Property("FileName");
+
+ b.Property("Key");
+
+ b.Property("MessageId");
+
+ b.Property("Relay");
+
+ b.Property("SentFileName");
+
+ b.Property("Status");
+
+ b.Property("StorageId");
+
+ b.HasKey("Id");
+
+ b.HasIndex("MessageId");
+
+ b.ToTable("Attachments");
+ });
+
+ modelBuilder.Entity("Signal_Windows.Models.SignalConversation", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("AvatarFile");
+
+ b.Property("CanReceive");
+
+ b.Property("Discriminator")
+ .IsRequired();
+
+ b.Property("Draft");
+
+ b.Property("ExpiresInSeconds");
+
+ b.Property("LastActiveTimestamp");
+
+ b.Property("LastMessageId");
+
+ b.Property("LastSeenMessageId");
+
+ b.Property("LastSeenMessageIndex");
+
+ b.Property("MessagesCount");
+
+ b.Property("ThreadDisplayName");
+
+ b.Property("ThreadId");
+
+ b.Property("UnreadCount");
+
+ b.HasKey("Id");
+
+ b.HasIndex("LastMessageId");
+
+ b.HasIndex("LastSeenMessageId");
+
+ b.HasIndex("ThreadId");
+
+ b.ToTable("SignalConversation");
+
+ b.HasDiscriminator("Discriminator").HasValue("SignalConversation");
+ });
+
+ modelBuilder.Entity("Signal_Windows.Models.SignalEarlyReceipt", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("DeviceId");
+
+ b.Property("Timestamp");
+
+ b.Property("Username");
+
+ b.HasKey("Id");
+
+ b.HasIndex("DeviceId");
+
+ b.HasIndex("Timestamp");
+
+ b.HasIndex("Username");
+
+ b.ToTable("EarlyReceipts");
+ });
+
+ modelBuilder.Entity("Signal_Windows.Models.SignalMessage", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd();
+
+ b.Property("AttachmentsCount");
+
+ b.Property("AuthorId");
+
+ b.Property("ComposedTimestamp");
+
+ b.Property("Contentrowid");
+
+ b.Property("DeviceId");
+
+ b.Property("Direction");
+
+ b.Property("ExpiresAt");
+
+ b.Property("Read");
+
+ b.Property("Receipts");
+
+ b.Property("ReceivedTimestamp");
+
+ b.Property("Status");
+
+ b.Property("ThreadId");
+
+ b.Property("Type");
+
+ b.HasKey("Id");
+
+ b.HasIndex("AuthorId");
+
+ b.HasIndex("Contentrowid");
+
+ b.HasIndex("ThreadId");
+
+ b.ToTable("Messages");
+ });
+
+ modelBuilder.Entity("Signal_Windows.Models.SignalMessageContent", b =>
+ {
+ b.Property("rowid")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Content");
+
+ b.HasKey("rowid");
+
+ b.ToTable("Messages_fts");
+ });
+
+ modelBuilder.Entity("Signal_Windows.Models.SignalContact", b =>
+ {
+ b.HasBaseType("Signal_Windows.Models.SignalConversation");
+
+ b.Property("Color");
+
+ b.ToTable("SignalContact");
+
+ b.HasDiscriminator().HasValue("SignalContact");
+ });
+
+ modelBuilder.Entity("Signal_Windows.Models.SignalGroup", b =>
+ {
+ b.HasBaseType("Signal_Windows.Models.SignalConversation");
+
+
+ b.ToTable("SignalGroup");
+
+ b.HasDiscriminator().HasValue("SignalGroup");
+ });
+
+ modelBuilder.Entity("Signal_Windows.Models.GroupMembership", b =>
+ {
+ b.HasOne("Signal_Windows.Models.SignalContact", "Contact")
+ .WithMany("GroupMemberships")
+ .HasForeignKey("ContactId")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("Signal_Windows.Models.SignalGroup", "Group")
+ .WithMany("GroupMemberships")
+ .HasForeignKey("GroupId")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ modelBuilder.Entity("Signal_Windows.Models.SignalAttachment", b =>
+ {
+ b.HasOne("Signal_Windows.Models.SignalMessage", "Message")
+ .WithMany("Attachments")
+ .HasForeignKey("MessageId")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ modelBuilder.Entity("Signal_Windows.Models.SignalConversation", b =>
+ {
+ b.HasOne("Signal_Windows.Models.SignalMessage", "LastMessage")
+ .WithMany()
+ .HasForeignKey("LastMessageId");
+
+ b.HasOne("Signal_Windows.Models.SignalMessage", "LastSeenMessage")
+ .WithMany()
+ .HasForeignKey("LastSeenMessageId");
+ });
+
+ modelBuilder.Entity("Signal_Windows.Models.SignalMessage", b =>
+ {
+ b.HasOne("Signal_Windows.Models.SignalContact", "Author")
+ .WithMany()
+ .HasForeignKey("AuthorId");
+
+ b.HasOne("Signal_Windows.Models.SignalMessageContent", "Content")
+ .WithMany()
+ .HasForeignKey("Contentrowid");
+ });
+ }
+ }
+}
diff --git a/Signal-Windows/Migrations/SignalDB/20170901124533_m3.cs b/Signal-Windows/Migrations/SignalDB/20170901124533_m3.cs
index 5672155..0c5fa3c 100644
--- a/Signal-Windows/Migrations/SignalDB/20170901124533_m3.cs
+++ b/Signal-Windows/Migrations/SignalDB/20170901124533_m3.cs
@@ -1,25 +1,25 @@
-using System;
-using System.Collections.Generic;
-using Microsoft.EntityFrameworkCore.Migrations;
-
-namespace Signal_Windows.Migrations
-{
- public partial class m3 : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.AddColumn(
- name: "LastSeenMessageIndex",
- table: "SignalConversation",
- nullable: false,
- defaultValue: 0L);
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropColumn(
- name: "LastSeenMessageIndex",
- table: "SignalConversation");
- }
- }
-}
+using System;
+using System.Collections.Generic;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+namespace Signal_Windows.Migrations
+{
+ public partial class m3 : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AddColumn(
+ name: "LastSeenMessageIndex",
+ table: "SignalConversation",
+ nullable: false,
+ defaultValue: 0L);
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropColumn(
+ name: "LastSeenMessageIndex",
+ table: "SignalConversation");
+ }
+ }
+}
diff --git a/Signal-Windows/Models/PhoneContact.cs b/Signal-Windows/Models/PhoneContact.cs
new file mode 100644
index 0000000..209fd44
--- /dev/null
+++ b/Signal-Windows/Models/PhoneContact.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Signal_Windows.Controls;
+using Windows.UI.Xaml.Media;
+
+namespace Signal_Windows.Models
+{
+ public class PhoneContact
+ {
+ public string Id { get; set; }
+ public string Name { get; set; }
+ public string PhoneNumber { get; set; }
+ public ImageSource Photo { get; set; }
+ public bool OnSignal { get; set; }
+ public AddContactListElement View;
+ }
+}
diff --git a/Signal-Windows/Package.appxmanifest b/Signal-Windows/Package.appxmanifest
index b898f84..a553b18 100644
--- a/Signal-Windows/Package.appxmanifest
+++ b/Signal-Windows/Package.appxmanifest
@@ -20,6 +20,18 @@
+
+
+
+ Signal Private Messenger Contact
+
+
+
+
+ Signal Private Messenger Message
+
+
+
diff --git a/Signal-Windows/Signal-Windows.csproj b/Signal-Windows/Signal-Windows.csproj
index fa81960..d0cab39 100644
--- a/Signal-Windows/Signal-Windows.csproj
+++ b/Signal-Windows/Signal-Windows.csproj
@@ -94,7 +94,6 @@
-
@@ -141,6 +140,9 @@
App.xaml
+
+ AddContactListElement.xaml
+
IdentityKeyChangeMessage.xaml
@@ -179,6 +181,7 @@
20170901124533_m3.cs
+
@@ -247,6 +250,10 @@
MSBuild:Compile
Designer
+
+ Designer
+ MSBuild:Compile
+
Designer
MSBuild:Compile
@@ -300,16 +307,53 @@
MSBuild:Compile
-
+
+
+ 2.5.10.3
+
+
+ 1.1.2
+
+
+ 1.1.2
+
+
+ 1.1.2
+
+
+ 1.1.1
+
+
+ 6.0.1
+
+
+ 1.5.1
+
+
+ 2.0.0
+
+
+ 5.3.0
+
+
+ 4.0.1
+
+
+ 1.0.0
+
+
+ 2.2.9
+
+
14.0
-
\ No newline at end of file
diff --git a/Signal-Windows/Storage/DB.cs b/Signal-Windows/Storage/DB.cs
index 7b2efd0..7aa3fd4 100644
--- a/Signal-Windows/Storage/DB.cs
+++ b/Signal-Windows/Storage/DB.cs
@@ -1130,7 +1130,7 @@ public static SignalContact GetOrCreateContactLocked(string username, long times
ThreadDisplayName = username,
CanReceive = true,
LastActiveTimestamp = timestamp,
- Color = Utils.Colors[Utils.CalculateDefaultColorIndex(username)]
+ Color = Utils.CalculateDefaultColor(username)
};
ctx.Contacts.Add(contact);
ctx.SaveChanges();
diff --git a/Signal-Windows/Utils.cs b/Signal-Windows/Utils.cs
index 0b12799..acd8614 100644
--- a/Signal-Windows/Utils.cs
+++ b/Signal-Windows/Utils.cs
@@ -12,7 +12,7 @@
namespace Signal_Windows
{
- public class Utils
+ public static class Utils
{
public static string[] Colors = {
"red",
@@ -86,19 +86,35 @@ public static SolidColorBrush GetBrushFromColor(string signalcolor)
}
}
- public static int CalculateDefaultColorIndex(string title)
+ public static void AddRange(this ObservableCollection observableCollection, IEnumerable collection)
{
- if (title.Length == 0)
+ foreach (var item in collection)
{
- return 0;
+ observableCollection.Add(item);
}
- var hash = 0;
- for (int i = 0; i < title.Length; i++)
+ }
+
+ public static string CalculateDefaultColor(string title)
+ {
+ return Colors[Math.Abs(JavaStringHashCode(title)) % Colors.Length];
+ }
+
+ public static SolidColorBrush GetDefaultColor(string title)
+ {
+ return GetBrushFromColor(CalculateDefaultColor(title));
+ }
+
+ public static int JavaStringHashCode(string str)
+ {
+ int h = 0;
+ if (str.Length > 0)
{
- hash = ((hash << 5) - hash) + title[i];
- hash = hash & hash;
+ for (int i = 0; i < str.Length; i++)
+ {
+ h = 31 * h + str[i];
+ }
}
- return Math.Abs(hash) % 15;
+ return h;
}
public static void EnableBackButton()
@@ -135,10 +151,15 @@ public static PageStyle GetViewStyle(Size s)
}
}
- private string GetCountryCode()
+ public static string GetCountryISO()
+ {
+ var c = CultureInfo.CurrentCulture.Name;
+ return c.Substring(c.Length - 2);
+ }
+
+ public static bool ContainsCaseInsensitive(this string str, string value)
{
- var c = CultureInfo.CurrentCulture.TwoLetterISOLanguageName;
- return GetCountryCode(c.ToUpper());
+ return CultureInfo.InvariantCulture.CompareInfo.IndexOf(str, value, CompareOptions.IgnoreCase) >= 0;
}
public static string GetCountryCode(string ISO3166) //https://stackoverflow.com/questions/34837436/uwp-get-country-phone-number-prefix
@@ -936,4 +957,4 @@ public static class CountryArrays
"ZW"
};
};
-}
\ No newline at end of file
+}
diff --git a/Signal-Windows/ViewModels/AddContactPageViewModel.cs b/Signal-Windows/ViewModels/AddContactPageViewModel.cs
index 27a7c73..4e0859d 100644
--- a/Signal-Windows/ViewModels/AddContactPageViewModel.cs
+++ b/Signal-Windows/ViewModels/AddContactPageViewModel.cs
@@ -12,137 +12,331 @@
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
+using System.Collections.ObjectModel;
+using Windows.ApplicationModel.Core;
+using libsignalservice;
+using PhoneNumbers;
+using System.Collections.Generic;
+using System.Linq;
+using Windows.UI.Xaml.Controls;
+using System.Globalization;
+using System.Threading;
namespace Signal_Windows.ViewModels
{
public class AddContactPageViewModel : ViewModelBase
{
+ public ObservableCollection Contacts;
+ private List signalContacts;
+ private PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.GetInstance();
public MainPageViewModel MainPageVM;
public AddContactPage View;
- private ImageSource _ContactPhoto = null;
-
- public ImageSource ContactPhoto
- {
- get { return _ContactPhoto; }
- set { _ContactPhoto = value; RaisePropertyChanged(nameof(ContactPhoto)); }
- }
-
- private string _ContactName = "";
+ private string _ContactName = string.Empty;
public string ContactName
{
get { return _ContactName; }
set { _ContactName = value; RaisePropertyChanged(nameof(ContactName)); }
}
- private string _ContactNumber = "";
-
+ private string _ContactNumber = string.Empty;
public string ContactNumber
{
get { return _ContactNumber; }
set { _ContactNumber = value; RaisePropertyChanged(nameof(ContactNumber)); }
}
- private bool _UIEnabled = true;
+ private bool _ContactsVisible = true;
+ public bool ContactsVisible
+ {
+ get { return _ContactsVisible; }
+ set { _ContactsVisible = value; RaisePropertyChanged(nameof(ContactsVisible)); }
+ }
+ private bool _RefreshingContacts = false;
+ public bool RefreshingContacts
+ {
+ get { return _RefreshingContacts; }
+ set { _RefreshingContacts = value; RaisePropertyChanged(nameof(RefreshingContacts)); }
+ }
+
+ public AddContactPageViewModel()
+ {
+ Contacts = new ObservableCollection();
+ signalContacts = new List();
+ }
+
+ private bool _UIEnabled = true;
public bool UIEnabled
{
get { return _UIEnabled; }
set { _UIEnabled = value; RaisePropertyChanged(nameof(UIEnabled)); }
}
- internal void BackButton_Click(object sender, BackRequestedEventArgs e)
+ private bool _AddEnabled = false;
+ public bool AddEnabled
{
- if (UIEnabled)
+ get { return _AddEnabled; }
+ set { _AddEnabled = value; RaisePropertyChanged(nameof(AddEnabled)); }
+ }
+
+ private bool validName = false;
+ private bool ValidName
+ {
+ get { return validName; }
+ set
{
- View.Frame.GoBack();
- e.Handled = true;
+ validName = value;
+ SetAddEnabled();
}
}
- internal async void AddButton_Click(object sender, RoutedEventArgs e)
+ private bool validNumber = false;
+ private bool ValidNumber
{
- if (UIEnabled)
+ get { return validNumber; }
+ set
{
- UIEnabled = false;
- Debug.WriteLine("creating contact {0} ({1})", ContactName, ContactNumber);
- SignalContact contact = new SignalContact()
- {
- ThreadDisplayName = ContactName,
- ThreadId = ContactNumber,
- CanReceive = true,
- AvatarFile = null,
- LastActiveTimestamp = 0,
- Draft = null,
- Color = "red",
- UnreadCount = 0
- };
- ContactName = "";
- ContactNumber = "";
- await Task.Run(() =>
- {
- SignalDBContext.InsertOrUpdateContactLocked(contact, MainPageVM);
- });
- UIEnabled = true;
+ validNumber = value;
+ SetAddEnabled();
}
}
- internal async void PickButton_Click(object sender, RoutedEventArgs e)
+ public async Task OnNavigatedTo(CancellationToken? cancellationToken = null)
{
- if (UIEnabled)
+ ContactName = string.Empty;
+ ContactNumber = string.Empty;
+ await RefreshContacts(cancellationToken);
+ }
+
+ public async Task RefreshContacts(CancellationToken? cancellationToken = null)
+ {
+ RefreshingContacts = true;
+ Contacts.Clear();
+ signalContacts.Clear();
+ SignalServiceAccountManager accountManager = new SignalServiceAccountManager(App.ServiceUrls, App.Store.Username, App.Store.Password, (int)App.Store.DeviceId, App.USER_AGENT);
+ ContactStore contactStore = await ContactManager.RequestStoreAsync(ContactStoreAccessType.AllContactsReadOnly);
+ List intermediateContacts = new List();
+ if (contactStore != null)
{
- UIEnabled = false;
- ContactPicker contactPicker = new ContactPicker();
- contactPicker.SelectionMode = ContactSelectionMode.Fields;
- contactPicker.DesiredFieldsWithContactFieldType.Add(ContactFieldType.PhoneNumber);
- var contact = await contactPicker.PickContactAsync();
- if (contact != null)
+ HashSet seenNumbers = new HashSet();
+ var contacts = await contactStore.FindContactsAsync();
+ ContactAnnotationStore contactAnnotationStore = await ContactManager.RequestAnnotationStoreAsync(ContactAnnotationStoreAccessType.AppAnnotationsReadWrite);
+ ContactAnnotationList contactAnnotationList;
+ var contactAnnotationLists = await contactAnnotationStore.FindAnnotationListsAsync();
+ if (contactAnnotationLists.Count == 0)
+ {
+ contactAnnotationList = await contactAnnotationStore.CreateAnnotationListAsync();
+ }
+ else
+ {
+ contactAnnotationList = contactAnnotationLists[0];
+ }
+
+ foreach (var contact in contacts)
{
- // The contact we just got doesn't contain the contact picture so we need to fetch it
- // see https://stackoverflow.com/questions/33401625/cant-get-contact-profile-images-in-uwp
- ContactStore contactStore = await ContactManager.RequestStoreAsync(ContactStoreAccessType.AllContactsReadOnly);
- // If we do not have access to contacts the ContactStore will be null, we can still use the contact the user
- // seleceted however
- if (contactStore != null)
+ var phones = contact.Phones;
+ foreach (var phone in contact.Phones)
{
- Contact realContact = await contactStore.GetContactAsync(contact.Id);
- if (realContact.SourceDisplayPicture != null)
+ if (phone.Kind == ContactPhoneKind.Mobile)
{
- using (var stream = await realContact.SourceDisplayPicture.OpenReadAsync())
+ string formattedNumber = null;
+ try
{
- BitmapImage bitmapImage = new BitmapImage();
- await bitmapImage.SetSourceAsync(stream);
- ContactPhoto = bitmapImage;
+ formattedNumber = ParsePhoneNumber(phone.Number);
}
- }
- else
- {
- ContactPhoto = null;
- }
- }
- ContactName = contact.Name;
- if (contact.Phones.Count > 0)
- {
- var originalNumber = contact.Phones[0].Number;
- if (originalNumber[0] != '+')
- {
- // need a better way of determining the "default" country code here
- var formattedPhoneNumber = PhoneNumberFormatter.FormatE164("1", originalNumber);
- if (string.IsNullOrEmpty(formattedPhoneNumber))
+ catch (NumberParseException)
{
- ContactNumber = originalNumber;
- MessageDialog message = new MessageDialog("Please format the number in E.164 format.", "Could not format number");
- await message.ShowAsync();
+ Debug.WriteLine($"Couldn't parse {phone.Number}");
+ continue;
}
- else
+ if (!seenNumbers.Contains(formattedNumber))
{
- ContactNumber = formattedPhoneNumber;
+ seenNumbers.Add(formattedNumber);
+ PhoneContact phoneContact = new PhoneContact
+ {
+ Id = contact.Id,
+ Name = contact.FullName,
+ PhoneNumber = formattedNumber,
+ OnSignal = false
+ };
+ if (contact.SourceDisplayPicture != null)
+ {
+ using (var stream = await contact.SourceDisplayPicture.OpenReadAsync())
+ {
+ BitmapImage bitmapImage = new BitmapImage();
+ await bitmapImage.SetSourceAsync(stream);
+ phoneContact.Photo = bitmapImage;
+ }
+ }
+ intermediateContacts.Add(phoneContact);
}
}
}
}
+
+ // check if we've annotated a contact as a Signal contact already, if we have we don't need to ask Signal about them
+ for (int i = 0; i < intermediateContacts.Count; i++)
+ {
+ var annotatedContact = await contactAnnotationList.FindAnnotationsByRemoteIdAsync(intermediateContacts[i].PhoneNumber);
+ if (annotatedContact.Count > 0)
+ {
+ intermediateContacts[i].OnSignal = true;
+ signalContacts.Add(intermediateContacts[i]);
+ intermediateContacts.RemoveAt(i);
+ i--;
+ }
+ }
+
+ var signalContactDetails = accountManager.getContacts(intermediateContacts.Select(c => c.PhoneNumber).ToList());
+ foreach (var contact in intermediateContacts)
+ {
+ var foundContact = signalContactDetails.FirstOrDefault(c => c.getNumber() == contact.PhoneNumber);
+ if (foundContact != null)
+ {
+ contact.OnSignal = true;
+ ContactAnnotation contactAnnotation = new ContactAnnotation();
+ contactAnnotation.ContactId = contact.Id;
+ contactAnnotation.RemoteId = contact.PhoneNumber;
+ contactAnnotation.SupportedOperations = ContactAnnotationOperations.Message | ContactAnnotationOperations.ContactProfile;
+ await contactAnnotationList.TrySaveAnnotationAsync(contactAnnotation);
+ signalContacts.Add(contact);
+ }
+ }
+ Contacts.AddRange(signalContacts);
+ }
+ else
+ {
+ ContactsVisible = false;
+ }
+ RefreshingContacts = false;
+ }
+
+ private void SetAddEnabled()
+ {
+ AddEnabled = ValidName && ValidNumber && UIEnabled;
+ }
+
+ internal void BackButton_Click(object sender, BackRequestedEventArgs e)
+ {
+ if (UIEnabled)
+ {
+ View.Frame.GoBack();
+ e.Handled = true;
+ }
+ }
+
+ internal void searchBox_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
+ {
+ if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
+ {
+ string text = sender.Text;
+ var validContacts = GetContactsMatchingText(text, signalContacts).ToList();
+ Contacts.Clear();
+ Contacts.AddRange(validContacts);
+ }
+ }
+
+ private IEnumerable GetContactsMatchingText(string text, List contacts)
+ {
+ return contacts.Where(
+ c => c.Name.ContainsCaseInsensitive(text) ||
+ c.PhoneNumber.ContainsCaseInsensitive(text));
+ }
+
+ internal void ContactNameTextBox_TextChanged(object sender, TextChangedEventArgs e)
+ {
+ TextBox textBox = sender as TextBox;
+ string text = textBox.Text;
+ if (string.IsNullOrEmpty(text))
+ {
+ ValidName = false;
+ }
+ else
+ {
+ ValidName = true;
+ }
+ }
+
+ internal void ContactNumberTextBox_TextChanged(object sender, TextChangedEventArgs e)
+ {
+ // TODO: See the TODO for AddButton_Click
+ TextBox textBox = sender as TextBox;
+ string text = textBox.Text;
+ if (string.IsNullOrEmpty(text))
+ {
+ ValidNumber = false;
+ }
+ else
+ {
+ ValidNumber = true;
+ }
+ }
+
+ // TODO: use the AsYouTypeFormatter when typing into the ContactNumber box so we don't have to validate here
+ // we need to be sure that the number here is valid
+ internal async Task AddButton_Click(object sender, RoutedEventArgs e)
+ {
+ if (UIEnabled)
+ {
+ UIEnabled = false;
+ string formattedPhoneNumber = null;
+ try
+ {
+ formattedPhoneNumber = ParsePhoneNumber(ContactNumber);
+ }
+ catch (NumberParseException)
+ {
+ MessageDialog message = new MessageDialog("Please format the number in E.164 format.", "Could not format number");
+ await message.ShowAsync();
+ return;
+ }
+ await AddContact(ContactName, formattedPhoneNumber);
+ UIEnabled = true;
+ }
+ }
+
+ internal async Task ContactsList_ItemClick(object sender, ItemClickEventArgs e)
+ {
+ if (UIEnabled)
+ {
+ UIEnabled = false;
+ PhoneContact phoneContact = e.ClickedItem as PhoneContact;
+ await AddContact(phoneContact.Name, phoneContact.PhoneNumber);
UIEnabled = true;
}
}
+
+ private async Task AddContact(string name, string number)
+ {
+ Debug.WriteLine("creating contact {0} ({1})", name, number);
+ SignalContact contact = new SignalContact()
+ {
+ ThreadDisplayName = name,
+ ThreadId = number,
+ CanReceive = true,
+ AvatarFile = null,
+ LastActiveTimestamp = 0,
+ Draft = null,
+ Color = Utils.CalculateDefaultColor(name),
+ UnreadCount = 0
+ };
+ await Task.Run(() =>
+ {
+ SignalDBContext.InsertOrUpdateContactLocked(contact, MainPageVM);
+ });
+ }
+
+ ///
+ /// Parses and formats a number in E164 format
+ ///
+ /// The number to parse
+ ///
+ /// A number in E164 format
+ private string ParsePhoneNumber(string number)
+ {
+ PhoneNumber phoneNumber = phoneNumberUtil.Parse(number, Utils.GetCountryISO());
+ return phoneNumberUtil.Format(phoneNumber, PhoneNumberFormat.E164);
+ }
}
-}
\ No newline at end of file
+}
diff --git a/Signal-Windows/Views/AddContactPage.xaml b/Signal-Windows/Views/AddContactPage.xaml
index 2f7d22f..c509fce 100644
--- a/Signal-Windows/Views/AddContactPage.xaml
+++ b/Signal-Windows/Views/AddContactPage.xaml
@@ -5,26 +5,54 @@
xmlns:local="using:Signal_Windows.Views"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:viewmodels="using:Signal_Windows.ViewModels"
+ xmlns:controls="using:Signal_Windows.Controls"
+ xmlns:toolbox="using:Microsoft.Toolkit.Uwp.UI.Controls"
mc:Ignorable="d"
DataContext="{Binding AddContactPageInstance, Source={StaticResource Locator}}">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Signal-Windows/Views/AddContactPage.xaml.cs b/Signal-Windows/Views/AddContactPage.xaml.cs
index e6acec1..59fea35 100644
--- a/Signal-Windows/Views/AddContactPage.xaml.cs
+++ b/Signal-Windows/Views/AddContactPage.xaml.cs
@@ -26,12 +26,11 @@ public AddContactPageViewModel Vm
}
}
- protected override void OnNavigatedTo(NavigationEventArgs ev)
+ protected override async void OnNavigatedTo(NavigationEventArgs ev)
{
base.OnNavigatedTo(ev);
Utils.EnableBackButton(Vm.BackButton_Click);
- // probably not the best way to do this
- Vm.ContactPhoto = null;
+ await Vm.OnNavigatedTo();
}
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
@@ -40,15 +39,36 @@ protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
Utils.DisableBackButton(Vm.BackButton_Click);
}
- private void AddButton_Click(object sender, RoutedEventArgs e)
+ private async void AddButton_Click(object sender, RoutedEventArgs e)
{
- Vm.AddButton_Click(sender, e);
+ await Vm.AddButton_Click(sender, e);
Frame.Navigate(typeof(MainPage));
}
- private void PickButton_Click(object sender, RoutedEventArgs e)
+ private async void ContactsList_ItemClick(object sender, ItemClickEventArgs e)
{
- Vm.PickButton_Click(sender, e);
+ await Vm.ContactsList_ItemClick(sender, e);
+ Frame.Navigate(typeof(MainPage));
+ }
+
+ private void searchBox_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
+ {
+ Vm.searchBox_TextChanged(sender, args);
+ }
+
+ private void ContactNameTextBox_TextChanged(object sender, TextChangedEventArgs e)
+ {
+ Vm.ContactNameTextBox_TextChanged(sender, e);
+ }
+
+ private void ContactNumberTextBox_TextChanged(object sender, TextChangedEventArgs e)
+ {
+ Vm.ContactNumberTextBox_TextChanged(sender, e);
+ }
+
+ private async void ContactsList_RefreshRequested(object sender, System.EventArgs e)
+ {
+ await Vm.RefreshContacts();
}
}
}
\ No newline at end of file
diff --git a/Signal-Windows/Views/MainPage.xaml.cs b/Signal-Windows/Views/MainPage.xaml.cs
index 995c2f9..816bb5b 100644
--- a/Signal-Windows/Views/MainPage.xaml.cs
+++ b/Signal-Windows/Views/MainPage.xaml.cs
@@ -156,8 +156,8 @@ public void ReselectTop()
private void AddContactButton_Click(object sender, RoutedEventArgs e)
{
App.ViewModels.AddContactPageInstance.MainPageVM = Vm;
- App.ViewModels.AddContactPageInstance.ContactName = "";
- App.ViewModels.AddContactPageInstance.ContactNumber = "";
+ //App.ViewModels.AddContactPageInstance.ContactName = "";
+ //App.ViewModels.AddContactPageInstance.ContactNumber = "";
Frame.Navigate(typeof(AddContactPage));
}
}
diff --git a/Signal-Windows/project.json b/Signal-Windows/project.json
deleted file mode 100644
index 16abea6..0000000
--- a/Signal-Windows/project.json
+++ /dev/null
@@ -1,26 +0,0 @@
-{
- "dependencies": {
- "libsignal-service-dotnet": "2.5.10.3",
- "Microsoft.EntityFrameworkCore": "1.1.2",
- "Microsoft.EntityFrameworkCore.Design": "1.1.2",
- "Microsoft.EntityFrameworkCore.Sqlite": "1.1.2",
- "Microsoft.EntityFrameworkCore.Tools": "1.1.1",
- "Microsoft.NETCore.UniversalWindowsPlatform": "5.3.4",
- "Microsoft.Toolkit.Uwp.Notifications": "1.5.1",
- "MvvmLight": "5.3.0",
- "Nito.AsyncEx": "4.0.1",
- "QueryString.NET": "1.0.0",
- "ZXing.Net.Mobile": "2.2.9"
- },
- "frameworks": {
- "uap10.0.14393": {}
- },
- "runtimes": {
- "win10-arm": {},
- "win10-arm-aot": {},
- "win10-x86": {},
- "win10-x86-aot": {},
- "win10-x64": {},
- "win10-x64-aot": {}
- }
-}
\ No newline at end of file