@@ -20,13 +20,12 @@ namespace Signal_Windows.Controls
20
20
public sealed partial class Conversation : UserControl , INotifyPropertyChanged
21
21
{
22
22
public event PropertyChangedEventHandler PropertyChanged ;
23
-
24
- private Dictionary < long , Message > OutgoingCache = new Dictionary < long , Message > ( ) ;
25
-
26
- public RangeObservableCollection < object > Messages { get ; set ; } = new RangeObservableCollection < object > ( ) ;
27
- private SignalUnreadMarker UnreadMarker = new SignalUnreadMarker ( ) ;
28
- private bool UnreadMarkerAdded = false ;
29
23
private bool SendingMessage = false ;
24
+ private Dictionary < long , SignalMessageContainer > OutgoingCache = new Dictionary < long , SignalMessageContainer > ( ) ;
25
+ //private SignalUnreadMarker UnreadMarker = new SignalUnreadMarker();
26
+ private SignalConversation SignalConversation ;
27
+ //private bool UnreadMarkerAdded = false;
28
+ private VirtualizedCollection Collection ;
30
29
31
30
private string _ThreadDisplayName ;
32
31
@@ -126,92 +125,86 @@ private void UpdateHeader(SignalConversation thread)
126
125
}
127
126
}
128
127
129
- public void ScrollToBottom ( )
128
+ public void Load ( SignalConversation conversation )
130
129
{
131
- SelectedMessagesScrollViewer . UpdateLayout ( ) ;
132
- if ( UnreadMarkerAdded )
133
- {
134
- var transform = UnreadMarker . View . TransformToVisual ( ( UIElement ) SelectedMessagesScrollViewer . Content ) ;
135
- var position = transform . TransformPoint ( new Point ( 0 , 0 ) ) ;
136
- SelectedMessagesScrollViewer . ChangeView ( null , position . Y , null , true ) ;
137
- }
138
- else
139
- {
140
- SelectedMessagesScrollViewer . ChangeView ( null , double . MaxValue , null , true ) ;
141
- }
142
- }
143
-
144
- public async Task Load ( SignalConversation thread )
145
- {
146
- UnreadMarkerAdded = false ;
130
+ SignalConversation = conversation ;
131
+ //UnreadMarkerAdded = false;
147
132
InputTextBox . IsEnabled = false ;
148
133
DisposeCurrentThread ( ) ;
149
- UpdateHeader ( thread ) ;
150
- var before = Util . CurrentTimeMillis ( ) ;
151
- var messages = await Task . Run ( ( ) =>
152
- {
153
- return SignalDBContext . GetMessagesLocked ( thread ) ;
154
- } ) ;
155
- var after1 = Util . CurrentTimeMillis ( ) ;
156
- foreach ( var message in messages )
157
- {
158
- Messages . AddSilently ( message ) ;
159
- if ( thread . LastSeenMessageId == message . Id && thread . LastMessageId != message . Id )
160
- {
161
- UnreadMarkerAdded = true ;
162
- Messages . AddSilently ( UnreadMarker ) ;
163
- }
164
- }
165
- Messages . ForceCollectionChanged ( ) ;
134
+ UpdateHeader ( conversation ) ;
135
+
136
+ /*
137
+ * When selecting a small (~650 messages) conversation after a bigger (~1800 messages) one,
138
+ * ScrollToBottom would scroll so far south that the entire screen was white. Seems like the
139
+ * scrollbar is not properly notified that the collection changed. I tried things like throwing
140
+ * CollectionChanged (reset) event, but to no avail. This hack works, though.
141
+ */
142
+ ConversationItemsControl . ItemsSource = new List < object > ( ) ;
166
143
UpdateLayout ( ) ;
167
- if ( UnreadMarkerAdded )
168
- {
169
- UnreadMarker . View . SetText ( thread . UnreadCount + " UNREAD MESSAGE" + ( thread . UnreadCount > 1 ? "S" : "" ) ) ;
170
- }
171
- foreach ( var message in messages )
172
- {
173
- if ( message . Direction != SignalMessageDirection . Incoming )
174
- {
175
- AddToOutgoingMessagesCache ( message ) ;
176
- }
177
- }
178
- var after2 = Util . CurrentTimeMillis ( ) ;
179
- Debug . WriteLine ( "db query: " + ( after1 - before ) ) ;
180
- Debug . WriteLine ( "ui: " + ( after2 - after1 ) ) ;
181
- InputTextBox . IsEnabled = thread . CanReceive ;
144
+ Collection = new VirtualizedCollection ( conversation ) ;
145
+ ConversationItemsControl . ItemsSource = Collection ;
146
+ UpdateLayout ( ) ;
147
+ InputTextBox . IsEnabled = conversation . CanReceive ;
148
+ ScrollToBottom ( ) ;
182
149
}
183
150
184
151
public void DisposeCurrentThread ( )
185
152
{
186
- Messages . Clear ( ) ;
187
153
OutgoingCache . Clear ( ) ;
188
154
}
189
155
156
+ public T FindElementByName < T > ( FrameworkElement element , string sChildName ) where T : FrameworkElement
157
+ {
158
+ T childElement = null ;
159
+ var nChildCount = VisualTreeHelper . GetChildrenCount ( element ) ;
160
+ for ( int i = 0 ; i < nChildCount ; i ++ )
161
+ {
162
+ FrameworkElement child = VisualTreeHelper . GetChild ( element , i ) as FrameworkElement ;
163
+
164
+ if ( child == null )
165
+ continue ;
166
+
167
+ if ( child is T && child . Name . Equals ( sChildName ) )
168
+ {
169
+ childElement = ( T ) child ;
170
+ break ;
171
+ }
172
+
173
+ childElement = FindElementByName < T > ( child , sChildName ) ;
174
+
175
+ if ( childElement != null )
176
+ break ;
177
+ }
178
+ return childElement ;
179
+ }
180
+
190
181
public void UpdateMessageBox ( SignalMessage updatedMessage )
191
182
{
192
183
if ( OutgoingCache . ContainsKey ( updatedMessage . Id ) )
193
184
{
194
185
var m = OutgoingCache [ updatedMessage . Id ] ;
195
- m . UpdateMessageBox ( updatedMessage ) ;
186
+ var item = ( ListBoxItem ) ConversationItemsControl . ContainerFromIndex ( m . Index ) ;
187
+ var message = FindElementByName < Message > ( item , "ListBoxItemContent" ) ;
188
+ bool retain = message . HandleUpdate ( updatedMessage ) ;
189
+ if ( ! retain )
190
+ {
191
+ OutgoingCache . Remove ( m . Index ) ;
192
+ }
196
193
}
197
194
}
198
195
199
- public void Append ( SignalMessage sm )
196
+ public void Append ( SignalMessageContainer sm , bool forceScroll )
200
197
{
201
- Messages . Add ( sm ) ;
202
- //TODO move scrolltobottom here
198
+ Collection . Add ( sm ) ;
199
+ if ( forceScroll || true ) //TODO
200
+ {
201
+ ScrollToBottom ( ) ;
202
+ }
203
203
}
204
204
205
- public void AddToOutgoingMessagesCache ( SignalMessage sm )
205
+ public void AddToOutgoingMessagesCache ( SignalMessageContainer m )
206
206
{
207
- if ( sm . View != null )
208
- {
209
- OutgoingCache [ sm . Id ] = sm . View ;
210
- }
211
- else
212
- {
213
- throw new Exception ( "Attempt to add null view to OutgoingCache" ) ;
214
- }
207
+ OutgoingCache [ m . Message . Id ] = m ;
215
208
}
216
209
217
210
private async void TextBox_KeyDown ( object sender , KeyRoutedEventArgs e )
@@ -228,13 +221,11 @@ private async void TextBox_KeyDown(object sender, KeyRoutedEventArgs e)
228
221
}
229
222
}
230
223
231
- public void RemoveUnreadMarker ( )
224
+ private void ScrollToBottom ( )
232
225
{
233
- if ( UnreadMarkerAdded )
234
- {
235
- Messages . Remove ( UnreadMarker ) ;
236
- UnreadMarkerAdded = false ;
237
- }
226
+ var lastMsg = ConversationItemsControl . Items [ ConversationItemsControl . Items . Count - 1 ] as SignalMessageContainer ;
227
+ Debug . WriteLine ( $ "scroll to { lastMsg } ") ;
228
+ ConversationItemsControl . ScrollIntoView ( lastMsg ) ;
238
229
}
239
230
240
231
private async void SendMessageButton_Click ( object sender , RoutedEventArgs e )
@@ -253,7 +244,6 @@ public class SignalUnreadMarker
253
244
{
254
245
public UnreadMarker View { get ; set ; }
255
246
}
256
-
257
247
public class MessageTemplateSelector : DataTemplateSelector
258
248
{
259
249
public DataTemplate NormalMessage { get ; set ; }
@@ -263,19 +253,15 @@ public class MessageTemplateSelector : DataTemplateSelector
263
253
protected override DataTemplate SelectTemplateCore ( object item , DependencyObject container )
264
254
{
265
255
FrameworkElement element = container as FrameworkElement ;
266
- if ( item is SignalMessage )
256
+ if ( item is SignalMessageContainer )
267
257
{
268
- SignalMessage sm = ( SignalMessage ) item ;
258
+ SignalMessage sm = ( ( SignalMessageContainer ) item ) . Message ;
269
259
if ( sm . Type == SignalMessageType . IdentityKeyChange )
270
260
{
271
261
return IdentityKeyChangeMessage ;
272
262
}
273
263
return NormalMessage ;
274
264
}
275
- if ( item is SignalUnreadMarker )
276
- {
277
- return UnreadMarker ;
278
- }
279
265
return null ;
280
266
}
281
267
}
0 commit comments