Skip to content
This repository was archived by the owner on Jul 2, 2022. It is now read-only.

Commit 9763738

Browse files
committed
Allow Addition of Files
Fixes #429
1 parent d0d3d53 commit 9763738

File tree

6 files changed

+383
-12
lines changed

6 files changed

+383
-12
lines changed

CodeHub.iOS/CodeHub.iOS.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,9 @@
317317
<Compile Include="ViewControllers\Settings\SyntaxHighlighterViewController.cs" />
318318
<Compile Include="ViewControllers\Settings\DefaultStartupViewController.cs" />
319319
<Compile Include="ViewControllers\Application\UpgradeViewController.cs" />
320+
<Compile Include="ViewControllers\Source\AddSourceViewController.cs" />
321+
<Compile Include="ViewControllers\MessageComposerViewController.cs" />
322+
<Compile Include="Utilities\ReactiveCommandExtensions.cs" />
320323
</ItemGroup>
321324
<ItemGroup>
322325
<Reference Include="System" />
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using System;
2+
using UIKit;
3+
using System.Reactive.Linq;
4+
using System.Reactive.Disposables;
5+
using System.Reactive;
6+
7+
// Analysis disable once CheckNamespace
8+
namespace ReactiveUI
9+
{
10+
public static class ReactiveCommandExtensions
11+
{
12+
public static IDisposable ToBarButtonItem(this IObservable<ReactiveCommand> @this, UIImage image, Action<UIBarButtonItem> assignment)
13+
{
14+
return ToBarButtonItem(@this, () => new UIBarButtonItem { Image = image }, assignment);
15+
}
16+
17+
public static IDisposable ToBarButtonItem(this IObservable<ReactiveCommand> @this, UIBarButtonSystemItem systemItem, Action<UIBarButtonItem> assignment)
18+
{
19+
return ToBarButtonItem(@this, () => new UIBarButtonItem(systemItem), assignment);
20+
}
21+
22+
public static IDisposable ToBarButtonItem(this IObservable<ReactiveCommand> @this, Func<UIBarButtonItem> creator, Action<UIBarButtonItem> assignment)
23+
{
24+
var unassignDisposable = Disposable.Create(() => assignment(null));
25+
IDisposable recentEventDisposable = Disposable.Empty;
26+
27+
var mainDisposable = @this.Subscribe(x => {
28+
recentEventDisposable?.Dispose();
29+
30+
var button = creator();
31+
var canExecuteDisposable = x.CanExecute.Subscribe(t => button.Enabled = t);
32+
var clickDisposable = Observable.FromEventPattern(t => button.Clicked += t, t => button.Clicked -= t)
33+
.Select(_ => Unit.Default)
34+
.InvokeCommand(x);
35+
36+
recentEventDisposable = new CompositeDisposable(clickDisposable, canExecuteDisposable);
37+
assignment(button);
38+
});
39+
40+
return new CompositeDisposable(mainDisposable, unassignDisposable, Disposable.Create(() => recentEventDisposable.Dispose()));
41+
}
42+
}
43+
}
44+

CodeHub.iOS/ViewControllers/Composer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace CodeHub.iOS.ViewControllers
99
{
1010
public class Composer : BaseViewController
1111
{
12-
protected UIBarButtonItem SendItem;
12+
public UIBarButtonItem SendItem;
1313
public Action<string> ReturnAction;
1414
protected readonly UITextView TextView;
1515
protected UIView ScrollingToolbarView;
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
using CoreGraphics;
2+
using Foundation;
3+
using UIKit;
4+
using ReactiveUI;
5+
using CodeHub.iOS.Views;
6+
7+
namespace CodeHub.iOS.ViewControllers
8+
{
9+
public class MessageComposerViewController : BaseViewController, IActivatable
10+
{
11+
private CGRect _keyboardBounds = CGRect.Empty;
12+
private NSObject _keyboardHideObserver;
13+
private NSObject _keyboardShowObserver;
14+
15+
public ExtendedUITextView TextView { get; }
16+
17+
public MessageComposerViewController()
18+
{
19+
EdgesForExtendedLayout = UIRectEdge.None;
20+
TextView = new ExtendedUITextView();
21+
TextView.Font = UIFont.PreferredBody;
22+
23+
// Work around an Apple bug in the UITextView that crashes
24+
if (ObjCRuntime.Runtime.Arch == ObjCRuntime.Arch.SIMULATOR)
25+
TextView.AutocorrectionType = UITextAutocorrectionType.No;
26+
27+
NavigationItem.BackBarButtonItem = new UIBarButtonItem { Title = string.Empty };
28+
}
29+
30+
public string Text
31+
{
32+
get { return TextView.Text; }
33+
set
34+
{
35+
if (string.Equals(Text, value))
36+
return;
37+
38+
TextView.Text = value;
39+
TextView.SelectedRange = new NSRange(0, 0);
40+
}
41+
}
42+
43+
public override void ViewDidLoad()
44+
{
45+
base.ViewDidLoad();
46+
View.BackgroundColor = UIColor.White;
47+
}
48+
49+
public override void ViewDidLayoutSubviews()
50+
{
51+
base.ViewDidLayoutSubviews();
52+
ResizeTextView();
53+
}
54+
55+
void KeyboardWillShow (NSNotification notification)
56+
{
57+
var nsValue = notification.UserInfo.ObjectForKey (UIKeyboard.BoundsUserInfoKey) as NSValue;
58+
if (nsValue == null) return;
59+
_keyboardBounds = nsValue.RectangleFValue;
60+
UIView.Animate(0.25f, 0, UIViewAnimationOptions.BeginFromCurrentState | UIViewAnimationOptions.CurveEaseIn, ResizeTextView, null);
61+
}
62+
63+
void KeyboardWillHide (NSNotification notification)
64+
{
65+
_keyboardBounds = CGRect.Empty;
66+
UIView.Animate(0.2, 0, UIViewAnimationOptions.BeginFromCurrentState | UIViewAnimationOptions.CurveEaseIn, ResizeTextView, null);
67+
}
68+
69+
private void ResizeTextView()
70+
{
71+
TextView.Frame = new CGRect(0, 0, View.Bounds.Width, View.Bounds.Height - _keyboardBounds.Height);
72+
}
73+
74+
public override void ViewWillAppear(bool animated)
75+
{
76+
base.ViewWillAppear(animated);
77+
_keyboardShowObserver = NSNotificationCenter.DefaultCenter.AddObserver(new NSString("UIKeyboardWillShowNotification"), KeyboardWillShow);
78+
_keyboardHideObserver = NSNotificationCenter.DefaultCenter.AddObserver(new NSString("UIKeyboardWillHideNotification"), KeyboardWillHide);
79+
ResizeTextView();
80+
Add(TextView);
81+
}
82+
83+
public override void ViewWillDisappear(bool animated)
84+
{
85+
base.ViewWillDisappear(animated);
86+
TextView.ResignFirstResponder();
87+
}
88+
89+
public override void ViewDidDisappear(bool animated)
90+
{
91+
base.ViewDidDisappear(animated);
92+
NSNotificationCenter.DefaultCenter.RemoveObserver(_keyboardHideObserver);
93+
NSNotificationCenter.DefaultCenter.RemoveObserver(_keyboardShowObserver);
94+
TextView.RemoveFromSuperview();
95+
}
96+
97+
public override void ViewDidAppear(bool animated)
98+
{
99+
base.ViewDidAppear(animated);
100+
TextView.BecomeFirstResponder();
101+
}
102+
103+
private static float CalculateHeight(UIInterfaceOrientation orientation)
104+
{
105+
if (UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Phone)
106+
return 44;
107+
if (orientation == UIInterfaceOrientation.Portrait || orientation == UIInterfaceOrientation.PortraitUpsideDown)
108+
return 64;
109+
return 88f;
110+
}
111+
112+
public override void WillRotate(UIInterfaceOrientation toInterfaceOrientation, double duration)
113+
{
114+
base.WillRotate(toInterfaceOrientation, duration);
115+
116+
if (TextView.InputAccessoryView != null)
117+
{
118+
UIView.Animate(duration, 0, UIViewAnimationOptions.BeginFromCurrentState, () =>
119+
{
120+
var frame = TextView.InputAccessoryView.Frame;
121+
frame.Height = CalculateHeight(toInterfaceOrientation);
122+
TextView.InputAccessoryView.Frame = frame;
123+
}, null);
124+
}
125+
}
126+
}
127+
}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
using System;
2+
using UIKit;
3+
using CodeHub.iOS.Utilities;
4+
using System.Threading.Tasks;
5+
using CodeHub.iOS.Services;
6+
using CodeHub.iOS.DialogElements;
7+
using CodeHub.iOS.TableViewSources;
8+
using CodeHub.Core.Services;
9+
using Splat;
10+
using System.Reactive.Subjects;
11+
using System.Reactive.Linq;
12+
13+
namespace CodeHub.iOS.ViewControllers.Source
14+
{
15+
public class AddSourceViewController : TableViewController
16+
{
17+
private readonly IApplicationService _applicationService;
18+
private readonly string _username;
19+
private readonly string _repository;
20+
private readonly string _path;
21+
private readonly string _branch;
22+
23+
private readonly DummyInputElement _titleElement;
24+
private readonly ExpandingInputElement _descriptionElement;
25+
26+
private readonly ISubject<Octokit.RepositoryContentChangeSet> _successSubject
27+
= new Subject<Octokit.RepositoryContentChangeSet>();
28+
29+
public IObservable<Octokit.RepositoryContentChangeSet> Success => _successSubject.AsObservable();
30+
31+
public AddSourceViewController(
32+
string username,
33+
string repository,
34+
string path,
35+
string branch,
36+
IApplicationService applicationService = null)
37+
: base(UITableViewStyle.Plain)
38+
{
39+
_username = username;
40+
_repository = repository;
41+
_path = path;
42+
_branch = branch;
43+
_applicationService = applicationService ?? Locator.Current.GetService<IApplicationService>();
44+
45+
_titleElement = new DummyInputElement("Name") { SpellChecking = false };
46+
_descriptionElement = new ExpandingInputElement("Content")
47+
{
48+
SpellChecking = false,
49+
Font = UIFont.FromName("Courier", UIFont.PreferredBody.PointSize)
50+
};
51+
52+
EdgesForExtendedLayout = UIRectEdge.None;
53+
Title = "Add File";
54+
55+
var commitButton = new UIBarButtonItem { Title = "Commit" };
56+
NavigationItem.RightBarButtonItem = commitButton;
57+
58+
this.OnActivation(d =>
59+
{
60+
d(commitButton
61+
.GetClickedObservable()
62+
.Subscribe(_ => Commit()));
63+
64+
d(_titleElement
65+
.Changed
66+
.Select(x => x.Length != 0)
67+
.Subscribe(x => commitButton.Enabled = x));
68+
});
69+
}
70+
71+
public override void ViewDidLoad()
72+
{
73+
base.ViewDidLoad();
74+
75+
var source = new DialogTableViewSource(TableView);
76+
source.Root.Add(new Section { _titleElement, _descriptionElement });
77+
TableView.Source = source;
78+
TableView.TableFooterView = new UIView();
79+
}
80+
81+
private void Commit()
82+
{
83+
var content = _descriptionElement.Value;
84+
85+
var composer = new Composer
86+
{
87+
Title = "Commit Message",
88+
Text = "Create " + _titleElement.Value
89+
};
90+
91+
composer
92+
.SendItem
93+
.GetClickedObservable()
94+
.Subscribe(_ => CommitThis(composer.Text).ToBackground());
95+
96+
this.PushViewController(composer);
97+
}
98+
99+
private async Task CommitThis(string message)
100+
{
101+
var content = _descriptionElement.Value;
102+
var name = _titleElement.Value;
103+
var path = string.IsNullOrEmpty(_path) ? name : $"{_path.TrimEnd('/')}/{name}";
104+
var hud = this.CreateHud();
105+
106+
try
107+
{
108+
hud.Show("Committing...");
109+
UIApplication.SharedApplication.BeginIgnoringInteractionEvents();
110+
NetworkActivity.PushNetworkActive();
111+
112+
var result = await _applicationService.GitHubClient.Repository.Content.CreateFile(
113+
_username, _repository, path, new Octokit.CreateFileRequest(message, content, _branch));
114+
115+
this.PresentingViewController?.DismissViewController(true, null);
116+
117+
_successSubject.OnNext(result);
118+
}
119+
catch (Octokit.ApiException ex)
120+
{
121+
var errorMessage = ex.Message;
122+
if (ex.ApiError?.DocumentationUrl == "https://developer.github.com/v3/repos/contents/#update-a-file")
123+
errorMessage = "A file with that name already exists!";
124+
125+
AlertDialogService.ShowAlert("Error", errorMessage);
126+
}
127+
catch (Exception ex)
128+
{
129+
AlertDialogService.ShowAlert("Error", ex.Message);
130+
}
131+
finally
132+
{
133+
UIApplication.SharedApplication.EndIgnoringInteractionEvents();
134+
NetworkActivity.PopNetworkActive();
135+
hud.Hide();
136+
}
137+
}
138+
}
139+
}
140+

0 commit comments

Comments
 (0)