{"action":"created","comment":{"url":"https://api.github.com/repos/realm/realm-dotnet/pulls/comments/89457327","pull_request_review_id":9970277,"id":89457327,"diff_hunk":"@@ -0,0 +1,572 @@\n+﻿////////////////////////////////////////////////////////////////////////////\n+//\n+// Copyright 2014 Realm Inc.\n+//\n+// Licensed under the Apache License, Version 2.0 (the \"License\");\n+// you may not use this file except in compliance with the License.\n+// You may obtain a copy of the License at\n+//\n+// http://www.apache.org/licenses/LICENSE-2.0\n+//\n+// Unless required by applicable law or agreed to in writing, software\n+// distributed under the License is distributed on an \"AS IS\" BASIS,\n+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+// See the License for the specific language governing permissions and\n+// limitations under the License.\n+//\n+////////////////////////////////////////////////////////////////////////////\n+\n+using System;\n+using System.Collections.Generic;\n+using System.Diagnostics;\n+using System.Linq;\n+using System.Linq.Expressions;\n+using Realms;\n+using Realms.Sync;\n+using SkiaSharp;\n+\n+namespace DrawXShared\n+{\n+    /***\n+     Class that does almost everything for the demo that can be shared.\n+     It combines drawing with logging in and connecting to the server.\n+\n+     DrawXSettingsManager provides a singleton to wrap settings and their local Realm.\n+\n+    Login\n+    -----\n+    Credentials come from DrawXSettings.\n+    It is the responsibility of external GUI classes to get credentials entered and delay\n+    starting RealmDraw until connection is made.\n+\n+    Drawing\n+    -------\n+    There are three components to drawing:\n+    - the background images for \"controls\"\n+    - previously drawn completed, paths which will no longer grow,\n+    - the currently growing paths (one per app updating the shared Realm).\n+\n+    However, with the paths, if we only worry about drawing _added_ or _changed_ paths\n+    then we don't care if they are completed or not, just what more we have to draw of them.\n+\n+    Caching and Responsiveness\n+    --------------------------\n+    Ideally, we want to have almost all the drawn content cached in a bitmap for redisplay, \n+    and only draw new line segments for each added point. It is relatively easy to optimise for\n+    local draw updates because we know when touched that we are drawing a single added segment.\n+\n+    There are two distinct patterns which can cause a poor demo with synchronised Draw.\n+    - A long, winding path being updated - there may be enough lag that we have to add more than \n+      one point to it locally but we don't want to redraw the whole thing from scratch.      \n+    - Many small, single \"dab\" strokes being drawn, say from someone tapping a display, \n+      which mean we have, at least, a TouchesBegan and TouchesEnded and probably AddPoint in between.\n+    \n+    We make use of a non-persistent field to help a given Draw differentiate which \n+    points it has processed - in the case of an incoming path it may have multiple points \n+    we have not yet seen.\n+\n+    Most importantly, to get the fastest possible response as the user moves their finger,\n+    we draw the local line immediately as a continuation of the path they started drawing \n+    earlier. (See _currentlyDrawing)\n+    \n+    */\n+    public class RealmDraw\n+    {\n+        private const float NORMALISE_TO = 4000.0f;\n+        private const float PENCIL_MARGIN = 4.0f;\n+        private const float INVALID_LAST_COORD = -1.0f;\n+        private float _lastX = INVALID_LAST_COORD;\n+        private float _lastY = INVALID_LAST_COORD;\n+\n+        #region Synchronised data\n+        private Realm _realm;\n+        private RealmResults<DrawPath> _allPaths;  // we observe all and filter based on changes\n+        #endregion\n+\n+        #region GUI Callbacks\n+        internal Action RefreshOnRealmUpdate { get; set; }\n+\n+        internal Action CredentialsEditor { get; set; }\n+        #endregion\n+\n+        #region DrawingState\n+        private bool _isDrawing = false;\n+        private bool _ignoringTouches = false;\n+        private DrawPath _drawPath;\n+        private SKPath _currentlyDrawing;  // caches for responsive drawing on this device\n+        private float _canvasWidth, _canvasHeight;\n+        private IList<DrawPath> _pathsToDraw = null;  // set in notification callback\n+        #endregion\n+\n+        #region CachedCanvas\n+        private int _canvasSaveCount;  // from SaveLayer\n+        private bool _hasSavedBitmap = false;  // separate flag so we don't rely on any given value in _canvasSaveCount\n+        private bool _redrawPathsAtNextDraw = true;\n+        #endregion\n+\n+        #region Touch Areas\n+        private SKRect _loginIconRect;\n+        private SKRect _loginIconTouchRect;\n+        //// setup in DrawBackground\n+        private float _pencilWidth;\n+        private float _pencilsTop;\n+        private int _numPencils;\n+        private List<SKBitmap> _pencilBitmaps;\n+        private SKBitmap _loginIconBitmap;\n+        #endregion\n+\n+        #region LoginState\n+        private bool _waitingForLogin = false;\n+        #endregion\n+\n+        #region Settings\n+        private DrawXSettings Settings => DrawXSettingsManager.Settings;\n+\n+        private int _currentColorIndex = -1;  //// for quick check if pencil we draw is current color\n+\n+        private SwatchColor _currentColor;\n+\n+        private SwatchColor CurrentColor\n+        {\n+            get\n+            {\n+                if (string.IsNullOrEmpty(_currentColor.Name))\n+                {\n+                    _currentColor = SwatchColor.ColorsByName[Settings.LastColorUsed];\n+                    _currentColorIndex = SwatchColor.Colors.IndexOf(_currentColor);\n+                }\n+\n+                return _currentColor;\n+            }\n+\n+            set\n+            {\n+                if (!_currentColor.Name.Equals(value.Name))\n+                {\n+                    _currentColor = value;\n+                    DrawXSettingsManager.Write(() => Settings.LastColorUsed = _currentColor.Name);\n+                    _currentColorIndex = SwatchColor.Colors.IndexOf(_currentColor);\n+                }\n+            }\n+        }\n+        #endregion Settings\n+\n+        public RealmDraw(float inWidth, float inHeight)\n+        {\n+            // TODO close the Realm\n+            _canvasWidth = inWidth;\n+            _canvasHeight = inHeight;\n+\n+            // simple local open            \n+            // _realm = Realm.GetInstance(\"DrawX.realm\");\n+\n+            _pencilBitmaps = new List<SKBitmap>(SwatchColor.Colors.Count);\n+            foreach (var swatch in SwatchColor.Colors)\n+            {\n+                _pencilBitmaps.Add(EmbeddedMedia.BitmapNamed(swatch.Name + \".png\"));\n+            }\n+\n+            _loginIconBitmap = EmbeddedMedia.BitmapNamed(\"CloudIcon.png\");\n+        }\n+\n+        internal void InvalidateCachedPaths()\n+        {\n+            _redrawPathsAtNextDraw = true;\n+            _hasSavedBitmap = false;\n+            _currentlyDrawing = null;\n+        }\n+\n+        internal async void LoginToServerAsync()\n+        {\n+            // in case have lingering subscriptions, clear by clearing the results to which we subscribe\n+            _allPaths = null;\n+\n+            _waitingForLogin = true;\n+            var s = Settings;\n+            //// TODO allow entering Create User flag on credentials to pass in here instead of false\n+            var credentials = Credentials.UsernamePassword(s.Username, s.Password, false);\n+            var user = await User.LoginAsync(credentials, new Uri($\"http://{s.ServerIP}\"));\n+            Debug.WriteLine($\"Got user logged in with refresh token {user.RefreshToken}\");\n+\n+            var loginConf = new SyncConfiguration(user, new Uri($\"realm://{s.ServerIP}/~/Draw\"));\n+            _realm = Realm.GetInstance(loginConf);\n+            #if DEBUG\n+            _realm.RealmChanged += (sender, e) => \n+            {\n+                Debug.WriteLine(\"Extra Realm Update callback invoked\");\n+            };\n+            #endif\n+            _allPaths = _realm.All<DrawPath>() as RealmResults<DrawPath>;\n+            _allPaths.SubscribeForNotifications((sender, changes, error) =>\n+            {\n+                // WARNING ChangeSet indices are only valid inside this callback\n+                if (changes == null)  // initial call\n+                {\n+                    RefreshOnRealmUpdate();  // force initial draw on login\n+                    return;\n+                }\n+                //// we assume if at least one path deleted, drastic stuff happened, probably erase all\n+                if (_allPaths.Count() == 0 || changes.DeletedIndices.Length > 0)\n+                {\n+                    Debug.WriteLine($\"Realm notifier: Invalidating on Realm change as paths deleted or none\");\n+                    InvalidateCachedPaths();  // someone erased their tablet\n+                    RefreshOnRealmUpdate();\n+                    return;\n+                }\n+\n+                int numInserted = changes.InsertedIndices.Length;\n+                int numChanged = changes.ModifiedIndices.Length;\n+                Debug.WriteLine($\"Realm notifier: {numInserted} inserts, {numChanged} changes\");\n+                if ((numInserted == 0 && numChanged == 1 && _allPaths.ElementAt(changes.ModifiedIndices[0]) == _drawPath) ||\n+                    (numInserted == 1 && numChanged == 0 && _allPaths.ElementAt(changes.InsertedIndices[0]) == _drawPath))\n+                {\n+                    // current path is drawn by immediate action, not by a callback\n+                    Debug.WriteLine(\"Realm notifier: no action because is just current path\");\n+                    return;\n+                }\n+\n+                _pathsToDraw = new List<DrawPath>();\n+                foreach (var index in changes.InsertedIndices)\n+                {\n+                    Debug.WriteLine($\"Realm notifier: caching path object inserted at {index}\");\n+                    _pathsToDraw.Add(_allPaths.ElementAt(index));\n+                }\n+\n+                foreach (var index in changes.ModifiedIndices)\n+                {\n+                    Debug.WriteLine($\"Realm notifier: caching object modified at {index}\");\n+                    _pathsToDraw.Add(_allPaths.ElementAt(index));\n+                }\n+\n+                RefreshOnRealmUpdate();\n+            });\n+            _waitingForLogin = false;\n+        }\n+\n+        private void ScalePointsToStore(ref float w, ref float h)\n+        {\n+            w *= NORMALISE_TO / _canvasWidth;\n+            h *= NORMALISE_TO / _canvasHeight;\n+        }\n+\n+        private void ScalePointsToDraw(ref float w, ref float h)\n+        {\n+            w *= _canvasWidth / NORMALISE_TO;\n+            h *= _canvasHeight / NORMALISE_TO;\n+        }\n+\n+        private bool TouchInControlArea(float inX, float inY)\n+        {\n+            if (_loginIconTouchRect.Contains(inX, inY))\n+            {\n+                InvalidateCachedPaths();\n+                CredentialsEditor();  // TODO only invalidate if changed server??\n+                return true;\n+            }\n+\n+            if (inY < _pencilsTop)\n+            {\n+                return false;\n+            }\n+\n+            // see opposite calc in DrawBackground\n+            int pencilIndex = (int)(inX / (_pencilWidth + PENCIL_MARGIN));\n+            var selectecColor = SwatchColor.Colors[pencilIndex];\n+            if (!selectecColor.Name.Equals(CurrentColor.Name))\n+            {\n+                CurrentColor = selectecColor;  // will update saved settings\n+            }\n+\n+            InvalidateCachedPaths();\n+            return true;  // if in this area even if didn't actually change\n+        }\n+\n+        private void DrawPencils(SKCanvas canvas, SKPaint paint)\n+        {\n+            // draw pencils, assigning the fields used for touch detection\n+            _numPencils = SwatchColor.ColorsByName.Count;\n+            if (_currentColorIndex == -1) \n+            {\n+                var getToSetIndex = CurrentColor;  // use getter to ensure is set consistently\n+            }\n+\n+            var marginAlloc = (_numPencils + 1) * PENCIL_MARGIN;\n+            _pencilWidth = (canvas.ClipBounds.Width - marginAlloc) / _numPencils;  // see opposite calc in TouchInControlArea\n+            var pencilHeight = _pencilWidth * 334.0f / 112.0f;  // scale as per originals\n+            float runningLeft = PENCIL_MARGIN;\n+            float pencilsBottom = canvas.ClipBounds.Height;\n+            _pencilsTop = pencilsBottom - pencilHeight;\n+            int _pencilIndex = 0;\n+            foreach (var swatchBM in _pencilBitmaps)\n+            {\n+                var pencilRect = new SKRect(runningLeft, _pencilsTop, runningLeft + _pencilWidth, pencilsBottom);\n+                if (_pencilIndex++ == _currentColorIndex)\n+                {\n+                    var offsetY = -Math.Max(20.0f, pencilHeight / 4.0f);\n+                    pencilRect.Offset(0.0f, offsetY);  // show selected color\n+                }\n+\n+                canvas.DrawBitmap(swatchBM, pencilRect, paint);\n+                runningLeft += PENCIL_MARGIN + _pencilWidth;\n+            }\n+        }\n+\n+        private void DrawLoginIcon(SKCanvas canvas, SKPaint paint)\n+        {\n+            if (_loginIconRect.Width <= 0.1f)\n+            {\n+                const float ICON_WIDTH = 84.0f;\n+                const float ICON_HEIGHT = 54.0f;\n+#if __IOS__\n+                const float TOP_BAR_OFFSET = 48.0f;\n+#else\n+                const float TOP_BAR_OFFSET = 8.0f;\n+#endif\n+                _loginIconRect = new SKRect(8.0f, TOP_BAR_OFFSET, 8.0f + ICON_WIDTH, TOP_BAR_OFFSET + ICON_HEIGHT);\n+                _loginIconTouchRect = new SKRect(0.0f, 0.0f,\n+                                                 Math.Max(_loginIconRect.Right + 4.0f, 44.0f),\n+                                                 Math.Max(_loginIconRect.Bottom + 4.0f, 44.0f));\n+            }\n+\n+            canvas.DrawBitmap(_loginIconBitmap, _loginIconRect, paint);\n+        }\n+\n+        private void DrawAPath(SKCanvas canvas, SKPaint paint, DrawPath drawPath)\n+        {\n+            using (SKPath path = new SKPath())\n+            {\n+                var pathColor = SwatchColor.ColorsByName[drawPath.color].Color;\n+                paint.Color = pathColor;\n+                bool isFirst = true;\n+                foreach (var point in drawPath.points)\n+                {\n+                    // for compatibility with iOS Realm, stores floats, normalised to NORMALISE_TO\n+                    float fx = (float)point.x;\n+                    float fy = (float)point.y;\n+                    ScalePointsToDraw(ref fx, ref fy);\n+                    if (isFirst)\n+                    {\n+                        isFirst = false;\n+                        path.MoveTo(fx, fy);\n+                    }\n+                    else\n+                    {\n+                        path.LineTo(fx, fy);\n+                    }\n+                }\n+\n+                canvas.DrawPath(path, paint);\n+            }\n+        }\n+\n+        private void InitPaint(SKPaint paint)\n+        {\n+            paint.Style = SKPaintStyle.Stroke;\n+            paint.StrokeWidth = 10;  // TODO scale width depending on device width\n+            paint.IsAntialias = true;\n+            paint.StrokeCap = SKStrokeCap.Round;\n+            paint.StrokeJoin = SKStrokeJoin.Round;\n+        }\n+\n+        // replaces the CanvasView.drawRect of the original\n+        // draw routine called when screen refreshed\n+        // Note the canvas only exists during this call\n+        public void DrawTouches(SKCanvas canvas)\n+        {\n+            if (_realm == null)\n+            {\n+                return;  // too early to have finished login\n+            }\n+\n+            if (_hasSavedBitmap)\n+            {\n+                Debug.WriteLine($\"DrawTouches - blitting saved canvas\");\n+                canvas.RestoreToCount(_canvasSaveCount);  // use up the offscreen bitmap regardless\n+            }\n+\n+            using (SKPaint paint = new SKPaint())\n+            {\n+                InitPaint(paint);\n+                if (_redrawPathsAtNextDraw)\n+                {\n+                    Debug.WriteLine($\"DrawTouches - Redrawing all paths\");\n+                    canvas.Clear(SKColors.White);\n+                    DrawPencils(canvas, paint);\n+                    DrawLoginIcon(canvas, paint);\n+                    foreach (var drawPath in _realm.All<DrawPath>())\n+                    {\n+                        DrawAPath(canvas, paint, drawPath);\n+                    }\n+\n+                    _pathsToDraw = null;\n+                }\n+                else\n+                {\n+                    // current paths being drawn, by other devices\n+                    if (_pathsToDraw != null)\n+                    {\n+                        Debug.WriteLine($\"DrawTouches - drawing remote paths in progress\");\n+                        foreach (var drawPath in _pathsToDraw)\n+                        {\n+                            Debug.WriteLine($\"DrawTouches - drawing path from Realm starting at {drawPath.points[0]}\");\n+                            DrawAPath(canvas, paint, drawPath);\n+                        }\n+                    }\n+                }\n+\n+                if (_currentlyDrawing != null)\n+                {\n+                    Debug.WriteLine($\"DrawTouches - drawing current in-memory path\");\n+                    paint.Color = CurrentColor.Color;\n+                    canvas.DrawPath(_currentlyDrawing, paint);\n+                }\n+\n+                _canvasSaveCount = canvas.SaveLayer(paint);  // cache everything to-date\n+                _hasSavedBitmap = true;\n+            } // SKPaint\n+\n+            _redrawPathsAtNextDraw = false;\n+        }\n+\n+        public void StartDrawing(float inX, float inY)\n+        {\n+            _currentlyDrawing = null;  // don't clear in Stop as will lose last point, clear when we know done\n+            if (TouchInControlArea(inX, inY))\n+            {\n+                _ignoringTouches = true;\n+                return;\n+            }\n+\n+            _ignoringTouches = false;\n+            if (_realm == null)\n+            {\n+                if (!_waitingForLogin)\n+                {\n+                    LoginToServerAsync();\n+                }\n+\n+                return;  // not yet logged into server, let next touch invoke us\n+            }\n+\n+            _lastX = inX;\n+            _lastY = inY;\n+            Debug.WriteLine($\"Writing a new path starting at {inX}, {inY}\");\n+\n+            // start a local path for responsive drawing\n+            _currentlyDrawing = new SKPath();\n+            _currentlyDrawing.MoveTo(inX, inY);\n+\n+            ScalePointsToStore(ref inX, ref inY);\n+            _isDrawing = true;\n+            _realm.Write(() =>\n+            {\n+                _drawPath = new DrawPath() { color = CurrentColor.Name };  // Realm saves name of color\n+                _drawPath.points.Add(new DrawPoint() { x = inX, y = inY });\n+                _realm.Add(_drawPath);\n+            });\n+            /*            var ignoredTask = _realm.WriteAsync((asyncRealm) =>\n+                        {\n+                            _drawPath = new DrawPath() { color = currentColor.Name, NumPointsDrawnLocally = 1 };  // Realm saves name of color\n+                            _drawPath.points.Add(new DrawPoint() { x = inX, y = inY });\n+                            asyncRealm.Add(_drawPath);\n+                        });*/\n+        }\n+\n+        public void AddPoint(float inX, float inY)\n+        {\n+            if (_ignoringTouches)\n+            {\n+                return;  // probably touched in pencil area\n+            }\n+\n+            if (_realm == null)\n+            {\n+                return;  // not yet logged into server\n+            }\n+\n+            if (!_isDrawing)\n+            {\n+                // has finished connecting to Realm so this is actually a start\n+                StartDrawing(inX, inY);\n+                return;\n+            }\n+\n+            _lastX = inX;\n+            _lastY = inY;\n+            Debug.WriteLine($\"Adding a point at {inX}, {inY}\");\n+            _currentlyDrawing.LineTo(inX, inY);\n+\n+            ScalePointsToStore(ref inX, ref inY);\n+            _realm.Write(() =>\n+            {\n+                _drawPath.points.Add(new DrawPoint() { x = inX, y = inY });\n+            });\n+            /*            _realm.WriteAsync((asyncRealm) =>\n+                        {\n+                            _drawPath.points.Add(new DrawPoint() { x = inX, y = inY });","path":"examples/DrawXNative/DrawXShared/RealmDraw.cs","position":506,"original_position":506,"commit_id":"140b93dcb0ebbef3355feb3851c1880a52263795","original_commit_id":"f714fc9fc6dd86f8dbc855d159c0c6181f24da10","user":{"login":"nirinchev","id":2315687,"avatar_url":"https://avatars.githubusercontent.com/u/2315687?v=3","gravatar_id":"","url":"https://api.github.com/users/nirinchev","html_url":"https://github.com/nirinchev","followers_url":"https://api.github.com/users/nirinchev/followers","following_url":"https://api.github.com/users/nirinchev/following{/other_user}","gists_url":"https://api.github.com/users/nirinchev/gists{/gist_id}","starred_url":"https://api.github.com/users/nirinchev/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nirinchev/subscriptions","organizations_url":"https://api.github.com/users/nirinchev/orgs","repos_url":"https://api.github.com/users/nirinchev/repos","events_url":"https://api.github.com/users/nirinchev/events{/privacy}","received_events_url":"https://api.github.com/users/nirinchev/received_events","type":"User","site_admin":false},"body":"It seems you're not using the `asyncRealm`, so that is likely the reason it was not working.","created_at":"2016-11-24T09:09:03Z","updated_at":"2016-11-24T09:09:03Z","html_url":"https://github.com/realm/realm-dotnet/pull/941#discussion_r89457327","pull_request_url":"https://api.github.com/repos/realm/realm-dotnet/pulls/941","_links":{"self":{"href":"https://api.github.com/repos/realm/realm-dotnet/pulls/comments/89457327"},"html":{"href":"https://github.com/realm/realm-dotnet/pull/941#discussion_r89457327"},"pull_request":{"href":"https://api.github.com/repos/realm/realm-dotnet/pulls/941"}}},"pull_request":{"url":"https://api.github.com/repos/realm/realm-dotnet/pulls/941","id":95014475,"html_url":"https://github.com/realm/realm-dotnet/pull/941","diff_url":"https://github.com/realm/realm-dotnet/pull/941.diff","patch_url":"https://github.com/realm/realm-dotnet/pull/941.patch","issue_url":"https://api.github.com/repos/realm/realm-dotnet/issues/941","number":941,"state":"open","locked":false,"title":"Ad/draw based off introduce sync","user":{"login":"AndyDentFree","id":1586093,"avatar_url":"https://avatars.githubusercontent.com/u/1586093?v=3","gravatar_id":"","url":"https://api.github.com/users/AndyDentFree","html_url":"https://github.com/AndyDentFree","followers_url":"https://api.github.com/users/AndyDentFree/followers","following_url":"https://api.github.com/users/AndyDentFree/following{/other_user}","gists_url":"https://api.github.com/users/AndyDentFree/gists{/gist_id}","starred_url":"https://api.github.com/users/AndyDentFree/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/AndyDentFree/subscriptions","organizations_url":"https://api.github.com/users/AndyDentFree/orgs","repos_url":"https://api.github.com/users/AndyDentFree/repos","events_url":"https://api.github.com/users/AndyDentFree/events{/privacy}","received_events_url":"https://api.github.com/users/AndyDentFree/received_events","type":"User","site_admin":false},"body":"Has had master merged in plus the fix to make it run on iOS devices just discussed on Slack. So, essentially, it's _just_ adding the Draw sample app. It has no warnings from StyleCop.\r\n\r\n@kristiandupont @fealebenpae @nirinchev spend as much time on it as you like. Ideally we get this merged fairly quickly and then further enhancements will be small branches off master.","created_at":"2016-11-23T14:35:34Z","updated_at":"2016-11-24T09:21:09Z","closed_at":null,"merged_at":null,"merge_commit_sha":"b32eab6cd3de885cef50bbc65a9ff02372207086","assignee":{"login":"AndyDentFree","id":1586093,"avatar_url":"https://avatars.githubusercontent.com/u/1586093?v=3","gravatar_id":"","url":"https://api.github.com/users/AndyDentFree","html_url":"https://github.com/AndyDentFree","followers_url":"https://api.github.com/users/AndyDentFree/followers","following_url":"https://api.github.com/users/AndyDentFree/following{/other_user}","gists_url":"https://api.github.com/users/AndyDentFree/gists{/gist_id}","starred_url":"https://api.github.com/users/AndyDentFree/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/AndyDentFree/subscriptions","organizations_url":"https://api.github.com/users/AndyDentFree/orgs","repos_url":"https://api.github.com/users/AndyDentFree/repos","events_url":"https://api.github.com/users/AndyDentFree/events{/privacy}","received_events_url":"https://api.github.com/users/AndyDentFree/received_events","type":"User","site_admin":false},"assignees":[{"login":"AndyDentFree","id":1586093,"avatar_url":"https://avatars.githubusercontent.com/u/1586093?v=3","gravatar_id":"","url":"https://api.github.com/users/AndyDentFree","html_url":"https://github.com/AndyDentFree","followers_url":"https://api.github.com/users/AndyDentFree/followers","following_url":"https://api.github.com/users/AndyDentFree/following{/other_user}","gists_url":"https://api.github.com/users/AndyDentFree/gists{/gist_id}","starred_url":"https://api.github.com/users/AndyDentFree/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/AndyDentFree/subscriptions","organizations_url":"https://api.github.com/users/AndyDentFree/orgs","repos_url":"https://api.github.com/users/AndyDentFree/repos","events_url":"https://api.github.com/users/AndyDentFree/events{/privacy}","received_events_url":"https://api.github.com/users/AndyDentFree/received_events","type":"User","site_admin":false}],"milestone":null,"commits_url":"https://api.github.com/repos/realm/realm-dotnet/pulls/941/commits","review_comments_url":"https://api.github.com/repos/realm/realm-dotnet/pulls/941/comments","review_comment_url":"https://api.github.com/repos/realm/realm-dotnet/pulls/comments{/number}","comments_url":"https://api.github.com/repos/realm/realm-dotnet/issues/941/comments","statuses_url":"https://api.github.com/repos/realm/realm-dotnet/statuses/140b93dcb0ebbef3355feb3851c1880a52263795","head":{"label":"realm:ad/draw-based-off-introduce-sync","ref":"ad/draw-based-off-introduce-sync","sha":"140b93dcb0ebbef3355feb3851c1880a52263795","user":{"login":"realm","id":7575099,"avatar_url":"https://avatars.githubusercontent.com/u/7575099?v=3","gravatar_id":"","url":"https://api.github.com/users/realm","html_url":"https://github.com/realm","followers_url":"https://api.github.com/users/realm/followers","following_url":"https://api.github.com/users/realm/following{/other_user}","gists_url":"https://api.github.com/users/realm/gists{/gist_id}","starred_url":"https://api.github.com/users/realm/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/realm/subscriptions","organizations_url":"https://api.github.com/users/realm/orgs","repos_url":"https://api.github.com/users/realm/repos","events_url":"https://api.github.com/users/realm/events{/privacy}","received_events_url":"https://api.github.com/users/realm/received_events","type":"Organization","site_admin":false},"repo":{"id":8485842,"name":"realm-dotnet","full_name":"realm/realm-dotnet","owner":{"login":"realm","id":7575099,"avatar_url":"https://avatars.githubusercontent.com/u/7575099?v=3","gravatar_id":"","url":"https://api.github.com/users/realm","html_url":"https://github.com/realm","followers_url":"https://api.github.com/users/realm/followers","following_url":"https://api.github.com/users/realm/following{/other_user}","gists_url":"https://api.github.com/users/realm/gists{/gist_id}","starred_url":"https://api.github.com/users/realm/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/realm/subscriptions","organizations_url":"https://api.github.com/users/realm/orgs","repos_url":"https://api.github.com/users/realm/repos","events_url":"https://api.github.com/users/realm/events{/privacy}","received_events_url":"https://api.github.com/users/realm/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/realm/realm-dotnet","description":"Realm is a mobile database: a replacement for SQLite & ORMs","fork":false,"url":"https://api.github.com/repos/realm/realm-dotnet","forks_url":"https://api.github.com/repos/realm/realm-dotnet/forks","keys_url":"https://api.github.com/repos/realm/realm-dotnet/keys{/key_id}","collaborators_url":"https://api.github.com/repos/realm/realm-dotnet/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/realm/realm-dotnet/teams","hooks_url":"https://api.github.com/repos/realm/realm-dotnet/hooks","issue_events_url":"https://api.github.com/repos/realm/realm-dotnet/issues/events{/number}","events_url":"https://api.github.com/repos/realm/realm-dotnet/events","assignees_url":"https://api.github.com/repos/realm/realm-dotnet/assignees{/user}","branches_url":"https://api.github.com/repos/realm/realm-dotnet/branches{/branch}","tags_url":"https://api.github.com/repos/realm/realm-dotnet/tags","blobs_url":"https://api.github.com/repos/realm/realm-dotnet/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/realm/realm-dotnet/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/realm/realm-dotnet/git/refs{/sha}","trees_url":"https://api.github.com/repos/realm/realm-dotnet/git/trees{/sha}","statuses_url":"https://api.github.com/repos/realm/realm-dotnet/statuses/{sha}","languages_url":"https://api.github.com/repos/realm/realm-dotnet/languages","stargazers_url":"https://api.github.com/repos/realm/realm-dotnet/stargazers","contributors_url":"https://api.github.com/repos/realm/realm-dotnet/contributors","subscribers_url":"https://api.github.com/repos/realm/realm-dotnet/subscribers","subscription_url":"https://api.github.com/repos/realm/realm-dotnet/subscription","commits_url":"https://api.github.com/repos/realm/realm-dotnet/commits{/sha}","git_commits_url":"https://api.github.com/repos/realm/realm-dotnet/git/commits{/sha}","comments_url":"https://api.github.com/repos/realm/realm-dotnet/comments{/number}","issue_comment_url":"https://api.github.com/repos/realm/realm-dotnet/issues/comments{/number}","contents_url":"https://api.github.com/repos/realm/realm-dotnet/contents/{+path}","compare_url":"https://api.github.com/repos/realm/realm-dotnet/compare/{base}...{head}","merges_url":"https://api.github.com/repos/realm/realm-dotnet/merges","archive_url":"https://api.github.com/repos/realm/realm-dotnet/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/realm/realm-dotnet/downloads","issues_url":"https://api.github.com/repos/realm/realm-dotnet/issues{/number}","pulls_url":"https://api.github.com/repos/realm/realm-dotnet/pulls{/number}","milestones_url":"https://api.github.com/repos/realm/realm-dotnet/milestones{/number}","notifications_url":"https://api.github.com/repos/realm/realm-dotnet/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/realm/realm-dotnet/labels{/name}","releases_url":"https://api.github.com/repos/realm/realm-dotnet/releases{/id}","deployments_url":"https://api.github.com/repos/realm/realm-dotnet/deployments","created_at":"2013-02-28T19:03:12Z","updated_at":"2016-11-23T04:23:27Z","pushed_at":"2016-11-24T08:38:32Z","git_url":"git://github.com/realm/realm-dotnet.git","ssh_url":"git@github.com:realm/realm-dotnet.git","clone_url":"https://github.com/realm/realm-dotnet.git","svn_url":"https://github.com/realm/realm-dotnet","homepage":"https://realm.io","size":70603,"stargazers_count":404,"watchers_count":404,"language":"C#","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":36,"mirror_url":null,"open_issues_count":167,"forks":36,"open_issues":167,"watchers":404,"default_branch":"master"}},"base":{"label":"realm:master","ref":"master","sha":"f9be43fc737180794d75d4ee0af90f4cd900512d","user":{"login":"realm","id":7575099,"avatar_url":"https://avatars.githubusercontent.com/u/7575099?v=3","gravatar_id":"","url":"https://api.github.com/users/realm","html_url":"https://github.com/realm","followers_url":"https://api.github.com/users/realm/followers","following_url":"https://api.github.com/users/realm/following{/other_user}","gists_url":"https://api.github.com/users/realm/gists{/gist_id}","starred_url":"https://api.github.com/users/realm/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/realm/subscriptions","organizations_url":"https://api.github.com/users/realm/orgs","repos_url":"https://api.github.com/users/realm/repos","events_url":"https://api.github.com/users/realm/events{/privacy}","received_events_url":"https://api.github.com/users/realm/received_events","type":"Organization","site_admin":false},"repo":{"id":8485842,"name":"realm-dotnet","full_name":"realm/realm-dotnet","owner":{"login":"realm","id":7575099,"avatar_url":"https://avatars.githubusercontent.com/u/7575099?v=3","gravatar_id":"","url":"https://api.github.com/users/realm","html_url":"https://github.com/realm","followers_url":"https://api.github.com/users/realm/followers","following_url":"https://api.github.com/users/realm/following{/other_user}","gists_url":"https://api.github.com/users/realm/gists{/gist_id}","starred_url":"https://api.github.com/users/realm/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/realm/subscriptions","organizations_url":"https://api.github.com/users/realm/orgs","repos_url":"https://api.github.com/users/realm/repos","events_url":"https://api.github.com/users/realm/events{/privacy}","received_events_url":"https://api.github.com/users/realm/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/realm/realm-dotnet","description":"Realm is a mobile database: a replacement for SQLite & ORMs","fork":false,"url":"https://api.github.com/repos/realm/realm-dotnet","forks_url":"https://api.github.com/repos/realm/realm-dotnet/forks","keys_url":"https://api.github.com/repos/realm/realm-dotnet/keys{/key_id}","collaborators_url":"https://api.github.com/repos/realm/realm-dotnet/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/realm/realm-dotnet/teams","hooks_url":"https://api.github.com/repos/realm/realm-dotnet/hooks","issue_events_url":"https://api.github.com/repos/realm/realm-dotnet/issues/events{/number}","events_url":"https://api.github.com/repos/realm/realm-dotnet/events","assignees_url":"https://api.github.com/repos/realm/realm-dotnet/assignees{/user}","branches_url":"https://api.github.com/repos/realm/realm-dotnet/branches{/branch}","tags_url":"https://api.github.com/repos/realm/realm-dotnet/tags","blobs_url":"https://api.github.com/repos/realm/realm-dotnet/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/realm/realm-dotnet/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/realm/realm-dotnet/git/refs{/sha}","trees_url":"https://api.github.com/repos/realm/realm-dotnet/git/trees{/sha}","statuses_url":"https://api.github.com/repos/realm/realm-dotnet/statuses/{sha}","languages_url":"https://api.github.com/repos/realm/realm-dotnet/languages","stargazers_url":"https://api.github.com/repos/realm/realm-dotnet/stargazers","contributors_url":"https://api.github.com/repos/realm/realm-dotnet/contributors","subscribers_url":"https://api.github.com/repos/realm/realm-dotnet/subscribers","subscription_url":"https://api.github.com/repos/realm/realm-dotnet/subscription","commits_url":"https://api.github.com/repos/realm/realm-dotnet/commits{/sha}","git_commits_url":"https://api.github.com/repos/realm/realm-dotnet/git/commits{/sha}","comments_url":"https://api.github.com/repos/realm/realm-dotnet/comments{/number}","issue_comment_url":"https://api.github.com/repos/realm/realm-dotnet/issues/comments{/number}","contents_url":"https://api.github.com/repos/realm/realm-dotnet/contents/{+path}","compare_url":"https://api.github.com/repos/realm/realm-dotnet/compare/{base}...{head}","merges_url":"https://api.github.com/repos/realm/realm-dotnet/merges","archive_url":"https://api.github.com/repos/realm/realm-dotnet/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/realm/realm-dotnet/downloads","issues_url":"https://api.github.com/repos/realm/realm-dotnet/issues{/number}","pulls_url":"https://api.github.com/repos/realm/realm-dotnet/pulls{/number}","milestones_url":"https://api.github.com/repos/realm/realm-dotnet/milestones{/number}","notifications_url":"https://api.github.com/repos/realm/realm-dotnet/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/realm/realm-dotnet/labels{/name}","releases_url":"https://api.github.com/repos/realm/realm-dotnet/releases{/id}","deployments_url":"https://api.github.com/repos/realm/realm-dotnet/deployments","created_at":"2013-02-28T19:03:12Z","updated_at":"2016-11-23T04:23:27Z","pushed_at":"2016-11-24T08:38:32Z","git_url":"git://github.com/realm/realm-dotnet.git","ssh_url":"git@github.com:realm/realm-dotnet.git","clone_url":"https://github.com/realm/realm-dotnet.git","svn_url":"https://github.com/realm/realm-dotnet","homepage":"https://realm.io","size":70603,"stargazers_count":404,"watchers_count":404,"language":"C#","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":36,"mirror_url":null,"open_issues_count":167,"forks":36,"open_issues":167,"watchers":404,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/realm/realm-dotnet/pulls/941"},"html":{"href":"https://github.com/realm/realm-dotnet/pull/941"},"issue":{"href":"https://api.github.com/repos/realm/realm-dotnet/issues/941"},"comments":{"href":"https://api.github.com/repos/realm/realm-dotnet/issues/941/comments"},"review_comments":{"href":"https://api.github.com/repos/realm/realm-dotnet/pulls/941/comments"},"review_comment":{"href":"https://api.github.com/repos/realm/realm-dotnet/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/realm/realm-dotnet/pulls/941/commits"},"statuses":{"href":"https://api.github.com/repos/realm/realm-dotnet/statuses/140b93dcb0ebbef3355feb3851c1880a52263795"}}}}
{"actor":{"display_login":"nirinchev"}}