Skip to content

Commit 79146c9

Browse files
Kudorozele
authored andcommitted
Add WPF Linking module support (#1192)
* Add WPF Linking module support * Align coding style
1 parent 553ebfd commit 79146c9

File tree

5 files changed

+176
-4
lines changed

5 files changed

+176
-4
lines changed

ReactWindows/Playground.Net46/App.xaml.cs

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System;
1+
using ReactNative.Modules.Launch;
2+
using System;
23
using System.Windows;
34
using System.Windows.Controls;
45
using System.Windows.Navigation;
@@ -38,6 +39,8 @@ private void OnCreate(string[] arguments)
3839
{
3940
_reactPage.OnResume(Shutdown);
4041

42+
LauncherModule.SetActivatedUrl(String.Join(" ", arguments));
43+
4144
var shellWindow = Application.Current.MainWindow;
4245

4346
if (shellWindow == null)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
using Newtonsoft.Json.Linq;
2+
using ReactNative.Bridge;
3+
using ReactNative.Modules.Core;
4+
using System;
5+
using System.Reactive.Subjects;
6+
using static System.FormattableString;
7+
8+
namespace ReactNative.Modules.Launch
9+
{
10+
/// <summary>
11+
/// The module responsible for activating URIs and managing the initial launch URL.
12+
/// </summary>
13+
public class LauncherModule : ReactContextNativeModuleBase, ILifecycleEventListener
14+
{
15+
private static readonly Subject<string> s_urlSubject = new Subject<string>();
16+
private static string s_activatedUrl;
17+
18+
private bool _initialized;
19+
private IDisposable _subscription;
20+
21+
/// <summary>
22+
/// Instantiates the <see cref="LauncherModule"/>.
23+
/// </summary>
24+
/// <param name="reactContext">The React context.</param>
25+
public LauncherModule(ReactContext reactContext)
26+
: base(reactContext)
27+
{
28+
}
29+
30+
/// <summary>
31+
/// The name of the module.
32+
/// </summary>
33+
public override string Name
34+
{
35+
get
36+
{
37+
return "LinkingManager";
38+
}
39+
}
40+
41+
/// <summary>
42+
/// Opens the given URL.
43+
/// </summary>
44+
/// <param name="url">The URL.</param>
45+
/// <param name="promise">
46+
/// The promise that should be resolved after the URL is opened.
47+
/// </param>
48+
[ReactMethod]
49+
public async void openURL(string url, IPromise promise)
50+
{
51+
if (url == null)
52+
{
53+
promise.Reject(new ArgumentNullException(nameof(url)));
54+
return;
55+
}
56+
57+
var uri = default(Uri);
58+
if (!Uri.TryCreate(url, UriKind.Absolute, out uri))
59+
{
60+
promise.Reject(new ArgumentException(Invariant($"URL argument '{uri}' is not valid.")));
61+
return;
62+
}
63+
64+
System.Diagnostics.Process.Start(uri.AbsoluteUri);
65+
promise.Resolve(true);
66+
}
67+
68+
/// <summary>
69+
/// Checks if the application can open the given URL.
70+
/// </summary>
71+
/// <param name="url">The URL.</param>
72+
/// <param name="promise">
73+
/// The promise used to return the result of the check.
74+
/// </param>
75+
[ReactMethod]
76+
public async void canOpenURL(string url, IPromise promise)
77+
{
78+
if (url == null)
79+
{
80+
promise.Reject(new ArgumentNullException(nameof(url)));
81+
return;
82+
}
83+
84+
var uri = default(Uri);
85+
if (!Uri.TryCreate(url, UriKind.Absolute, out uri))
86+
{
87+
promise.Reject(new ArgumentException(Invariant($"URL argument '{uri}' is not valid.")));
88+
return;
89+
}
90+
91+
promise.Resolve(true);
92+
}
93+
94+
/// <summary>
95+
/// Gets the URL the application was launched with, or <code>null</code>
96+
/// if the application was started normally.
97+
/// </summary>
98+
/// <param name="promise">
99+
/// The promise used to return the initial URL.
100+
/// </param>
101+
[ReactMethod]
102+
public void getInitialURL(IPromise promise)
103+
{
104+
promise.Resolve(s_activatedUrl);
105+
}
106+
107+
/// <summary>
108+
/// Called when the app is initialized.
109+
/// </summary>
110+
public override void Initialize()
111+
{
112+
_subscription = CreateUrlSubscription();
113+
_initialized = true;
114+
}
115+
116+
/// <summary>
117+
/// Called when the app is being suspended.
118+
/// </summary>
119+
public void OnSuspend()
120+
{
121+
_subscription.Dispose();
122+
}
123+
124+
/// <summary>
125+
/// Called when the app is resumed.
126+
/// </summary>
127+
public void OnResume()
128+
{
129+
// Wait for initialization to subscribe to protect against sending
130+
// events to a context that has not been fully initialized.
131+
if (_initialized)
132+
{
133+
_subscription = CreateUrlSubscription();
134+
}
135+
}
136+
137+
/// <summary>
138+
/// Called when the app is destroyed.
139+
/// </summary>
140+
public void OnDestroy()
141+
{
142+
_subscription.Dispose();
143+
}
144+
145+
/// <summary>
146+
/// The initial URL used to activate the application.
147+
/// </summary>
148+
public static void SetActivatedUrl(string url)
149+
{
150+
s_activatedUrl = url;
151+
s_urlSubject.OnNext(url);
152+
}
153+
154+
private IDisposable CreateUrlSubscription()
155+
{
156+
return s_urlSubject.Subscribe(url =>
157+
Context.GetJavaScriptModule<RCTDeviceEventEmitter>()
158+
.emit("url", new JObject
159+
{
160+
{ "url", url },
161+
}));
162+
}
163+
}
164+
}

ReactWindows/ReactNative.Net46/ReactNative.Net46.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@
142142
<Compile Include="Modules\Dialog\DialogModule.cs" />
143143
<Compile Include="Modules\Image\BitmapImageHelpers.cs" />
144144
<Compile Include="Modules\Image\ImageLoaderModule.cs" />
145+
<Compile Include="Modules\Launch\LauncherModule.cs" />
145146
<Compile Include="Modules\Storage\AsyncStorageModule.cs" />
146147
<Compile Include="Modules\Clipboard\ClipboardModule.cs" />
147148
<Compile Include="Modules\Clipboard\ClipboardInstance.cs" />

ReactWindows/ReactNative.Net46/Shell/MainReactPackage.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using ReactNative.Modules.Dialog;
77
using ReactNative.Modules.Image;
88
using ReactNative.Modules.I18N;
9+
using ReactNative.Modules.Launch;
910
using ReactNative.Modules.NetInfo;
1011
using ReactNative.Modules.Network;
1112
using ReactNative.Modules.Storage;
@@ -48,7 +49,7 @@ public IReadOnlyList<INativeModule> CreateNativeModules(ReactContext reactContex
4849
new DialogModule(reactContext),
4950
new ImageLoaderModule(),
5051
new I18NModule(),
51-
//new LauncherModule(reactContext),
52+
new LauncherModule(reactContext),
5253
//new LocationModule(reactContext),
5354
new NativeAnimatedModule(reactContext),
5455
new NetworkingModule(reactContext),

local-cli/generator-wpf/templates/src/App.xaml.cs

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System;
1+
using ReactNative.Modules.Launch;
2+
using System;
23
using System.Windows;
34
using System.Windows.Controls;
45
using System.Windows.Navigation;
@@ -37,7 +38,9 @@ protected override void OnStartup(StartupEventArgs e)
3738
private void OnCreate(string[] arguments)
3839
{
3940
_reactPage.OnResume(Shutdown);
40-
41+
42+
LauncherModule.SetActivatedUrl(String.Join(" ", arguments));
43+
4144
var shellWindow = Application.Current.MainWindow;
4245

4346
if (shellWindow == null)

0 commit comments

Comments
 (0)