[admob][ios] Implement Rewarded Videos
This commit is contained in:
parent
921eac06a4
commit
9342a0e748
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
29C199451EA7A851007B6BF8 /* RNFirebaseCrash.m in Sources */ = {isa = PBXBuildFile; fileRef = 29C199441EA7A851007B6BF8 /* RNFirebaseCrash.m */; };
|
29C199451EA7A851007B6BF8 /* RNFirebaseCrash.m in Sources */ = {isa = PBXBuildFile; fileRef = 29C199441EA7A851007B6BF8 /* RNFirebaseCrash.m */; };
|
||||||
|
99339B7D1EE8254300511AE3 /* RNFirebaseAdMobRewardedVideo.m in Sources */ = {isa = PBXBuildFile; fileRef = 99339B7C1EE8254300511AE3 /* RNFirebaseAdMobRewardedVideo.m */; };
|
||||||
99EBBD431EE5A4E50087F3CD /* RNFirebaseAdMob.m in Sources */ = {isa = PBXBuildFile; fileRef = 99EBBD3E1EE5A4E50087F3CD /* RNFirebaseAdMob.m */; };
|
99EBBD431EE5A4E50087F3CD /* RNFirebaseAdMob.m in Sources */ = {isa = PBXBuildFile; fileRef = 99EBBD3E1EE5A4E50087F3CD /* RNFirebaseAdMob.m */; };
|
||||||
99EBBD441EE5A4E50087F3CD /* RNFirebaseAdMobInterstitial.m in Sources */ = {isa = PBXBuildFile; fileRef = 99EBBD401EE5A4E50087F3CD /* RNFirebaseAdMobInterstitial.m */; };
|
99EBBD441EE5A4E50087F3CD /* RNFirebaseAdMobInterstitial.m in Sources */ = {isa = PBXBuildFile; fileRef = 99EBBD401EE5A4E50087F3CD /* RNFirebaseAdMobInterstitial.m */; };
|
||||||
99EBBD451EE5A4E50087F3CD /* RNFirebasePerformance.m in Sources */ = {isa = PBXBuildFile; fileRef = 99EBBD421EE5A4E50087F3CD /* RNFirebasePerformance.m */; };
|
99EBBD451EE5A4E50087F3CD /* RNFirebasePerformance.m in Sources */ = {isa = PBXBuildFile; fileRef = 99EBBD421EE5A4E50087F3CD /* RNFirebasePerformance.m */; };
|
||||||
|
@ -37,6 +38,8 @@
|
||||||
134814201AA4EA6300B7C361 /* libRNFirebase.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNFirebase.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
134814201AA4EA6300B7C361 /* libRNFirebase.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNFirebase.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
29C199431EA7A851007B6BF8 /* RNFirebaseCrash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNFirebaseCrash.h; path = RNFirebase/RNFirebaseCrash.h; sourceTree = "<group>"; };
|
29C199431EA7A851007B6BF8 /* RNFirebaseCrash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNFirebaseCrash.h; path = RNFirebase/RNFirebaseCrash.h; sourceTree = "<group>"; };
|
||||||
29C199441EA7A851007B6BF8 /* RNFirebaseCrash.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNFirebaseCrash.m; path = RNFirebase/RNFirebaseCrash.m; sourceTree = "<group>"; };
|
29C199441EA7A851007B6BF8 /* RNFirebaseCrash.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNFirebaseCrash.m; path = RNFirebase/RNFirebaseCrash.m; sourceTree = "<group>"; };
|
||||||
|
99339B7B1EE8254300511AE3 /* RNFirebaseAdMobRewardedVideo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNFirebaseAdMobRewardedVideo.h; path = RNFirebase/RNFirebaseAdMobRewardedVideo.h; sourceTree = "<group>"; };
|
||||||
|
99339B7C1EE8254300511AE3 /* RNFirebaseAdMobRewardedVideo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNFirebaseAdMobRewardedVideo.m; path = RNFirebase/RNFirebaseAdMobRewardedVideo.m; sourceTree = "<group>"; };
|
||||||
99EBBD3D1EE5A4E50087F3CD /* RNFirebaseAdMob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNFirebaseAdMob.h; path = RNFirebase/RNFirebaseAdMob.h; sourceTree = "<group>"; };
|
99EBBD3D1EE5A4E50087F3CD /* RNFirebaseAdMob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNFirebaseAdMob.h; path = RNFirebase/RNFirebaseAdMob.h; sourceTree = "<group>"; };
|
||||||
99EBBD3E1EE5A4E50087F3CD /* RNFirebaseAdMob.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNFirebaseAdMob.m; path = RNFirebase/RNFirebaseAdMob.m; sourceTree = "<group>"; };
|
99EBBD3E1EE5A4E50087F3CD /* RNFirebaseAdMob.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNFirebaseAdMob.m; path = RNFirebase/RNFirebaseAdMob.m; sourceTree = "<group>"; };
|
||||||
99EBBD3F1EE5A4E50087F3CD /* RNFirebaseAdMobInterstitial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNFirebaseAdMobInterstitial.h; path = RNFirebase/RNFirebaseAdMobInterstitial.h; sourceTree = "<group>"; };
|
99EBBD3F1EE5A4E50087F3CD /* RNFirebaseAdMobInterstitial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNFirebaseAdMobInterstitial.h; path = RNFirebase/RNFirebaseAdMobInterstitial.h; sourceTree = "<group>"; };
|
||||||
|
@ -94,6 +97,8 @@
|
||||||
D96290351D6D145F0099A3EC /* Modules */ = {
|
D96290351D6D145F0099A3EC /* Modules */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
99339B7B1EE8254300511AE3 /* RNFirebaseAdMobRewardedVideo.h */,
|
||||||
|
99339B7C1EE8254300511AE3 /* RNFirebaseAdMobRewardedVideo.m */,
|
||||||
99EBBD3D1EE5A4E50087F3CD /* RNFirebaseAdMob.h */,
|
99EBBD3D1EE5A4E50087F3CD /* RNFirebaseAdMob.h */,
|
||||||
99EBBD3E1EE5A4E50087F3CD /* RNFirebaseAdMob.m */,
|
99EBBD3E1EE5A4E50087F3CD /* RNFirebaseAdMob.m */,
|
||||||
99EBBD3F1EE5A4E50087F3CD /* RNFirebaseAdMobInterstitial.h */,
|
99EBBD3F1EE5A4E50087F3CD /* RNFirebaseAdMobInterstitial.h */,
|
||||||
|
@ -187,6 +192,7 @@
|
||||||
99EBBD431EE5A4E50087F3CD /* RNFirebaseAdMob.m in Sources */,
|
99EBBD431EE5A4E50087F3CD /* RNFirebaseAdMob.m in Sources */,
|
||||||
EC841F001ECE79D6001AD3D9 /* RNFirebaseRemoteConfig.m in Sources */,
|
EC841F001ECE79D6001AD3D9 /* RNFirebaseRemoteConfig.m in Sources */,
|
||||||
99EBBD451EE5A4E50087F3CD /* RNFirebasePerformance.m in Sources */,
|
99EBBD451EE5A4E50087F3CD /* RNFirebasePerformance.m in Sources */,
|
||||||
|
99339B7D1EE8254300511AE3 /* RNFirebaseAdMobRewardedVideo.m in Sources */,
|
||||||
29C199451EA7A851007B6BF8 /* RNFirebaseCrash.m in Sources */,
|
29C199451EA7A851007B6BF8 /* RNFirebaseCrash.m in Sources */,
|
||||||
D96290851D6D28B80099A3EC /* RNFirebaseDatabase.m in Sources */,
|
D96290851D6D28B80099A3EC /* RNFirebaseDatabase.m in Sources */,
|
||||||
);
|
);
|
||||||
|
|
|
@ -8,11 +8,13 @@
|
||||||
#import "RNFirebaseEvents.h"
|
#import "RNFirebaseEvents.h"
|
||||||
#import <React/RCTEventEmitter.h>
|
#import <React/RCTEventEmitter.h>
|
||||||
#import "GoogleMobileAds/GADInterstitialDelegate.h"
|
#import "GoogleMobileAds/GADInterstitialDelegate.h"
|
||||||
|
#import "GoogleMobileAds/GADRewardBasedVideoAdDelegate.h"
|
||||||
#import "GoogleMobileAds/GADAdDelegate.h"
|
#import "GoogleMobileAds/GADAdDelegate.h"
|
||||||
|
|
||||||
|
|
||||||
@interface RNFirebaseAdMob : RCTEventEmitter <RCTBridgeModule, GADInterstitialDelegate, GADAdDelegate>
|
@interface RNFirebaseAdMob : RCTEventEmitter <RCTBridgeModule>
|
||||||
@property NSMutableDictionary *interstitials;
|
@property NSMutableDictionary *interstitials;
|
||||||
|
@property NSMutableDictionary *rewardedVideos;
|
||||||
|
|
||||||
- (GADRequest *)buildRequest:(NSDictionary *)request;
|
- (GADRequest *)buildRequest:(NSDictionary *)request;
|
||||||
- (NSDictionary *)errorCodeToDictionary:(NSError *)error;
|
- (NSDictionary *)errorCodeToDictionary:(NSError *)error;
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#import "GoogleMobileAds/GADMobileAds.h"
|
#import "GoogleMobileAds/GADMobileAds.h"
|
||||||
|
|
||||||
#import "RNFirebaseAdMobInterstitial.h"
|
#import "RNFirebaseAdMobInterstitial.h"
|
||||||
|
#import "RNFirebaseAdMobRewardedVideo.h"
|
||||||
|
|
||||||
NSString *MALE = @"male";
|
NSString *MALE = @"male";
|
||||||
NSString *FEMALE = @"female";
|
NSString *FEMALE = @"female";
|
||||||
|
@ -14,6 +15,7 @@ RCT_EXPORT_MODULE();
|
||||||
self = [super init];
|
self = [super init];
|
||||||
if (self != nil) {
|
if (self != nil) {
|
||||||
_interstitials = [[NSMutableDictionary alloc] init];
|
_interstitials = [[NSMutableDictionary alloc] init];
|
||||||
|
_rewardedVideos = [[NSMutableDictionary alloc] init];
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
@ -27,12 +29,6 @@ RCT_EXPORT_METHOD(initialize:
|
||||||
[GADMobileAds configureWithApplicationID:appId];
|
[GADMobileAds configureWithApplicationID:appId];
|
||||||
}
|
}
|
||||||
|
|
||||||
RCT_EXPORT_METHOD(interstitialShowAd:
|
|
||||||
(NSString *) adUnit) {
|
|
||||||
RNFirebaseAdMobInterstitial *interstitial = [self getOrCreateInterstitial:adUnit];
|
|
||||||
[interstitial showAd];
|
|
||||||
}
|
|
||||||
|
|
||||||
RCT_EXPORT_METHOD(interstitialLoadAd:
|
RCT_EXPORT_METHOD(interstitialLoadAd:
|
||||||
(NSString *) adUnit
|
(NSString *) adUnit
|
||||||
request:
|
request:
|
||||||
|
@ -41,6 +37,25 @@ RCT_EXPORT_METHOD(interstitialLoadAd:
|
||||||
[interstitial loadAd:request];
|
[interstitial loadAd:request];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RCT_EXPORT_METHOD(interstitialShowAd:
|
||||||
|
(NSString *) adUnit) {
|
||||||
|
RNFirebaseAdMobInterstitial *interstitial = [self getOrCreateInterstitial:adUnit];
|
||||||
|
[interstitial show];
|
||||||
|
}
|
||||||
|
|
||||||
|
RCT_EXPORT_METHOD(rewardedVideoLoadAd:
|
||||||
|
(NSString *) adUnit
|
||||||
|
request:
|
||||||
|
(NSDictionary *) request) {
|
||||||
|
RNFirebaseAdMobRewardedVideo *rewardedVideo = [self getOrCreateRewardedVideo:adUnit];
|
||||||
|
[rewardedVideo loadAd:request];
|
||||||
|
}
|
||||||
|
|
||||||
|
RCT_EXPORT_METHOD(rewardedVideoShowAd:
|
||||||
|
(NSString *) adUnit) {
|
||||||
|
RNFirebaseAdMobRewardedVideo *rewardedVideo = [self getOrCreateRewardedVideo:adUnit];
|
||||||
|
[rewardedVideo show];
|
||||||
|
}
|
||||||
|
|
||||||
- (RNFirebaseAdMobInterstitial *)getOrCreateInterstitial:(NSString *)adUnit {
|
- (RNFirebaseAdMobInterstitial *)getOrCreateInterstitial:(NSString *)adUnit {
|
||||||
if (_interstitials[adUnit]) {
|
if (_interstitials[adUnit]) {
|
||||||
|
@ -51,6 +66,15 @@ RCT_EXPORT_METHOD(interstitialLoadAd:
|
||||||
return _interstitials[adUnit];
|
return _interstitials[adUnit];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (RNFirebaseAdMobRewardedVideo *)getOrCreateRewardedVideo:(NSString *)adUnit {
|
||||||
|
if (_rewardedVideos[adUnit]) {
|
||||||
|
return _rewardedVideos[adUnit];
|
||||||
|
}
|
||||||
|
|
||||||
|
_rewardedVideos[adUnit] = [[RNFirebaseAdMobRewardedVideo alloc] initWithProps:adUnit delegate:self];
|
||||||
|
return _rewardedVideos[adUnit];
|
||||||
|
}
|
||||||
|
|
||||||
- (NSArray<NSString *> *)supportedEvents {
|
- (NSArray<NSString *> *)supportedEvents {
|
||||||
return @[ADMOB_INTERSTITIAL_EVENT, ADMOB_REWARDED_VIDEO_EVENT];
|
return @[ADMOB_INTERSTITIAL_EVENT, ADMOB_REWARDED_VIDEO_EVENT];
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
- (id)initWithProps:(NSString *)adUnit delegate:(RNFirebaseAdMob *)delegate;
|
- (id)initWithProps:(NSString *)adUnit delegate:(RNFirebaseAdMob *)delegate;
|
||||||
|
|
||||||
- (void)showAd;
|
- (void)show;
|
||||||
- (void)loadAd:(NSDictionary *)request;
|
- (void)loadAd:(NSDictionary *)request;
|
||||||
- (void)sendJSEvent:(NSString *)type payload:(nullable NSDictionary *)payload;
|
- (void)sendJSEvent:(NSString *)type payload:(nullable NSDictionary *)payload;
|
||||||
|
|
||||||
|
|
|
@ -22,17 +22,13 @@
|
||||||
[_interstitial loadRequest:[_delegate buildRequest:request]];
|
[_interstitial loadRequest:[_delegate buildRequest:request]];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)showAd {
|
- (void)show {
|
||||||
if (_interstitial.isReady) {
|
if (_interstitial.isReady) {
|
||||||
[_interstitial presentFromRootViewController:[UIApplication sharedApplication].delegate.window.rootViewController];
|
[_interstitial presentFromRootViewController:[UIApplication sharedApplication].delegate.window.rootViewController];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)sendJSEvent:(NSString *)type payload:(NSDictionary *)payload {
|
- (void)sendJSEvent:(NSString *)type payload:(NSDictionary *)payload {
|
||||||
if (payload == nil) {
|
|
||||||
payload = @{};
|
|
||||||
}
|
|
||||||
|
|
||||||
[_delegate sendEventWithName:ADMOB_INTERSTITIAL_EVENT body:@{
|
[_delegate sendEventWithName:ADMOB_INTERSTITIAL_EVENT body:@{
|
||||||
@"type": type,
|
@"type": type,
|
||||||
@"adUnit": _adUnitID,
|
@"adUnit": _adUnitID,
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef RNFirebaseAdMobRewardedVideo_h
|
||||||
|
#define RNFirebaseAdMobRewardedVideo_h
|
||||||
|
|
||||||
|
#import <React/RCTBridgeModule.h>
|
||||||
|
#import "RNFirebaseEvents.h"
|
||||||
|
|
||||||
|
#if __has_include(<GoogleMobileAds/GADMobileAds.h>)
|
||||||
|
|
||||||
|
#import "GoogleMobileAds/GADRewardBasedVideoAdDelegate.h"
|
||||||
|
#import "GoogleMobileAds/GADRewardBasedVideoAd.h"
|
||||||
|
#import <React/RCTEventEmitter.h>
|
||||||
|
#import "RNFirebaseAdMob.h"
|
||||||
|
|
||||||
|
@interface RNFirebaseAdMobRewardedVideo : NSObject <GADRewardBasedVideoAdDelegate>
|
||||||
|
@property NSString *adUnitID;
|
||||||
|
@property RNFirebaseAdMob *delegate;
|
||||||
|
@property GADRewardBasedVideoAd *videoAd;
|
||||||
|
|
||||||
|
- (id)initWithProps:(NSString *)adUnit delegate:(RNFirebaseAdMob *)delegate;
|
||||||
|
|
||||||
|
- (void)show;
|
||||||
|
- (void)loadAd:(NSDictionary *)request;
|
||||||
|
- (void)sendJSEvent:(NSString *)type payload:(nullable NSDictionary *)payload;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#else
|
||||||
|
@interface RNFirebaseAdMobRewardedVideo : NSObject <RCTBridgeModule> {
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
#import "RNFirebaseAdMobRewardedVideo.h"
|
||||||
|
|
||||||
|
|
||||||
|
@implementation RNFirebaseAdMobRewardedVideo
|
||||||
|
|
||||||
|
- (id)initWithProps:(NSString *)adUnit delegate:(RNFirebaseAdMob *)delegate {
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
_adUnitID = adUnit;
|
||||||
|
_delegate = delegate;
|
||||||
|
_videoAd = [self createRewardedVideo];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (GADRewardBasedVideoAd *)createRewardedVideo {
|
||||||
|
GADRewardBasedVideoAd *videoAd = [GADRewardBasedVideoAd sharedInstance];
|
||||||
|
videoAd.delegate = self;
|
||||||
|
return videoAd;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)loadAd:(NSDictionary *)request {
|
||||||
|
[_videoAd loadRequest:[_delegate buildRequest:request] withAdUnitID:_adUnitID];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)show {
|
||||||
|
if (_videoAd.isReady) {
|
||||||
|
[_videoAd presentFromRootViewController:[UIApplication sharedApplication].delegate.window.rootViewController];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)sendJSEvent:(NSString *)type payload:(NSDictionary *)payload {
|
||||||
|
[_delegate sendEventWithName:ADMOB_REWARDED_VIDEO_EVENT body:@{
|
||||||
|
@"type": type,
|
||||||
|
@"adUnit": _adUnitID,
|
||||||
|
@"payload": payload
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)rewardBasedVideoAd:(GADRewardBasedVideoAd *)rewardBasedVideoAd
|
||||||
|
didRewardUserWithReward:(GADAdReward *)reward {
|
||||||
|
[self sendJSEvent:@"onRewarded" payload:@{
|
||||||
|
@"type":reward.type,
|
||||||
|
@"amount":reward.amount,
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)rewardBasedVideoAdDidReceiveAd:(GADRewardBasedVideoAd *)rewardBasedVideoAd {
|
||||||
|
[self sendJSEvent:@"onAdLoaded" payload:@{}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)rewardBasedVideoAdDidOpen:(GADRewardBasedVideoAd *)rewardBasedVideoAd {
|
||||||
|
[self sendJSEvent:@"onAdOpened" payload:@{}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)rewardBasedVideoAdDidStartPlaying:(GADRewardBasedVideoAd *)rewardBasedVideoAd {
|
||||||
|
[self sendJSEvent:@"onRewardedVideoStarted" payload:@{}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)rewardBasedVideoAdDidClose:(GADRewardBasedVideoAd *)rewardBasedVideoAd {
|
||||||
|
[self sendJSEvent:@"onAdClosed" payload:@{}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)rewardBasedVideoAdWillLeaveApplication:(GADRewardBasedVideoAd *)rewardBasedVideoAd {
|
||||||
|
[self sendJSEvent:@"onAdLeftApplication" payload:@{}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)rewardBasedVideoAd:(GADRewardBasedVideoAd *)rewardBasedVideoAd
|
||||||
|
didFailToLoadWithError:(NSError *)error {
|
||||||
|
[self sendJSEvent:@"onAdFailedToLoad" payload:[_delegate errorCodeToDictionary:error]];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -3,7 +3,7 @@ import { statics } from './';
|
||||||
import AdRequest from './AdRequest';
|
import AdRequest from './AdRequest';
|
||||||
import { nativeToJSError } from '../../utils';
|
import { nativeToJSError } from '../../utils';
|
||||||
|
|
||||||
const FirebaseAdMob = NativeModules.RNFirebaseAdmob;
|
const FirebaseAdMob = NativeModules.RNFirebaseAdMob;
|
||||||
|
|
||||||
export default class RewardedVideo {
|
export default class RewardedVideo {
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue