mirror of
https://github.com/movie-web/native-app.git
synced 2025-09-13 14:43:25 +00:00
feat: gate download behind development certificate on iOS
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -19,6 +19,8 @@ expo-env.d.ts
|
||||
apps/expo/.gitignore
|
||||
ios/
|
||||
android/
|
||||
!modules/*/ios/
|
||||
!modules/*/android/
|
||||
|
||||
# production
|
||||
build
|
||||
|
60
.vscode/launch.json
vendored
Normal file
60
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Run iOS",
|
||||
"request": "launch",
|
||||
"runtimeArgs": [
|
||||
"ios",
|
||||
],
|
||||
"cwd": "${workspaceFolder}/apps/expo",
|
||||
"runtimeExecutable": "pnpm",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"type": "node"
|
||||
},
|
||||
{
|
||||
"name": "Run Android",
|
||||
"request": "launch",
|
||||
"runtimeArgs": [
|
||||
"android",
|
||||
],
|
||||
"cwd": "${workspaceFolder}/apps/expo",
|
||||
"runtimeExecutable": "pnpm",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"type": "node"
|
||||
},
|
||||
{
|
||||
"name": "Build IPA",
|
||||
"request": "launch",
|
||||
"runtimeArgs": [
|
||||
"ipa",
|
||||
],
|
||||
"cwd": "${workspaceFolder}/apps/expo",
|
||||
"runtimeExecutable": "pnpm",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"type": "node"
|
||||
},
|
||||
{
|
||||
"name": "Build APK",
|
||||
"request": "launch",
|
||||
"runtimeArgs": [
|
||||
"apk",
|
||||
],
|
||||
"cwd": "${workspaceFolder}/apps/expo",
|
||||
"runtimeExecutable": "pnpm",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"type": "node"
|
||||
},
|
||||
]
|
||||
}
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -24,5 +24,8 @@
|
||||
],
|
||||
"[github-actions-workflow]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[jsonc]": {
|
||||
"editor.defaultFormatter": "vscode.json-language-features"
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"platforms": ["ios"],
|
||||
"ios": {
|
||||
"modules": ["CheckIosCertificateModule"]
|
||||
}
|
||||
}
|
11
apps/expo/modules/check-ios-certificate/index.ts
Normal file
11
apps/expo/modules/check-ios-certificate/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
// Import the native module. On web, it will be resolved to CheckIosCertificate.web.ts
|
||||
// and on native platforms to CheckIosCertificate.ts
|
||||
import CheckIosCertificateModule from "./src/CheckIosCertificateModule";
|
||||
|
||||
interface CheckIosCertificateModule {
|
||||
isDevelopmentProvisioningProfile(): boolean;
|
||||
}
|
||||
|
||||
export function isDevelopmentProvisioningProfile(): boolean {
|
||||
return (CheckIosCertificateModule as CheckIosCertificateModule).isDevelopmentProvisioningProfile();
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'CheckIosCertificate'
|
||||
s.version = '1.0.0'
|
||||
s.summary = 'A sample project summary'
|
||||
s.description = 'A sample project description'
|
||||
s.author = ''
|
||||
s.homepage = 'https://docs.expo.dev/modules/'
|
||||
s.platforms = { :ios => '13.4', :tvos => '13.4' }
|
||||
s.source = { git: '' }
|
||||
s.static_framework = true
|
||||
|
||||
s.dependency 'ExpoModulesCore'
|
||||
|
||||
# Swift/Objective-C compatibility
|
||||
s.pod_target_xcconfig = {
|
||||
'DEFINES_MODULE' => 'YES',
|
||||
'SWIFT_COMPILATION_MODE' => 'wholemodule'
|
||||
}
|
||||
|
||||
s.source_files = "**/*.{h,m,mm,swift,hpp,cpp}"
|
||||
end
|
@@ -0,0 +1,37 @@
|
||||
import ExpoModulesCore
|
||||
|
||||
public class CheckIosCertificateModule: Module {
|
||||
// Each module class must implement the definition function. The definition consists of components
|
||||
// that describes the module's functionality and behavior.
|
||||
// See https://docs.expo.dev/modules/module-api for more details about available components.
|
||||
public func definition() -> ModuleDefinition {
|
||||
// Sets the name of the module that JavaScript code will use to refer to the module. Takes a string as an argument.
|
||||
// Can be inferred from module's class name, but it's recommended to set it explicitly for clarity.
|
||||
// The module will be accessible from `requireNativeModule('CheckIosCertificate')` in JavaScript.
|
||||
Name("CheckIosCertificate")
|
||||
|
||||
// Defines a JavaScript synchronous function that runs the native code on the JavaScript thread.
|
||||
Function("isDevelopmentProvisioningProfile") {
|
||||
#if targetEnvironment(simulator)
|
||||
// Running on the Simulator
|
||||
return true
|
||||
#else
|
||||
// Check for provisioning profile for non-Simulator execution
|
||||
guard let filePath = Bundle.main.path(forResource: "embedded", ofType: "mobileprovision") else {
|
||||
return false
|
||||
}
|
||||
|
||||
let fileURL = URL(fileURLWithPath: filePath)
|
||||
do {
|
||||
let data = try String(contentsOf: fileURL, encoding: .ascii)
|
||||
let cleared = data.components(separatedBy: .whitespacesAndNewlines).joined()
|
||||
return cleared.contains("<key>get-task-allow</key><true/>")
|
||||
} catch {
|
||||
// Handling error if the file read fails
|
||||
print("Error reading provisioning profile: \(error)")
|
||||
return false
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
import { requireNativeModule } from "expo-modules-core";
|
||||
|
||||
// It loads the native module object from the JSI or falls back to
|
||||
// the bridge module (from NativeModulesProxy) if the remote debugger is on.
|
||||
export default requireNativeModule("CheckIosCertificate");
|
@@ -1,18 +1,25 @@
|
||||
import type { Asset } from "expo-media-library";
|
||||
import React from "react";
|
||||
import { Alert, Platform } from "react-native";
|
||||
import { ScrollView } from "react-native-gesture-handler";
|
||||
import { useRouter } from "expo-router";
|
||||
import { useFocusEffect, useRouter } from "expo-router";
|
||||
import { MaterialCommunityIcons } from "@expo/vector-icons";
|
||||
import { isDevelopmentProvisioningProfile } from "modules/check-ios-certificate";
|
||||
import { useTheme, YStack } from "tamagui";
|
||||
|
||||
|
||||
|
||||
import type { ScrapeMedia } from "@movie-web/provider-utils";
|
||||
|
||||
|
||||
|
||||
import { DownloadItem } from "~/components/DownloadItem";
|
||||
import ScreenLayout from "~/components/layout/ScreenLayout";
|
||||
import { MWButton } from "~/components/ui/Button";
|
||||
import { useDownloadManager } from "~/hooks/DownloadManagerContext";
|
||||
import { usePlayerStore } from "~/stores/player/store";
|
||||
|
||||
|
||||
const DownloadsScreen: React.FC = () => {
|
||||
const { startDownload, downloads } = useDownloadManager();
|
||||
const resetVideo = usePlayerStore((state) => state.resetVideo);
|
||||
@@ -20,6 +27,23 @@ const DownloadsScreen: React.FC = () => {
|
||||
const router = useRouter();
|
||||
const theme = useTheme();
|
||||
|
||||
useFocusEffect(
|
||||
React.useCallback(() => {
|
||||
if (Platform.OS === "ios" && !isDevelopmentProvisioningProfile()) {
|
||||
Alert.alert(
|
||||
"Production Certificate",
|
||||
"Download functionality is not available when the application is signed with a distribution certificate.",
|
||||
[
|
||||
{
|
||||
text: "OK",
|
||||
onPress: () => router.back(),
|
||||
},
|
||||
],
|
||||
);
|
||||
}
|
||||
}, [router]),
|
||||
);
|
||||
|
||||
const handlePress = (asset?: Asset) => {
|
||||
if (!asset) return;
|
||||
resetVideo();
|
||||
@@ -44,6 +68,9 @@ const DownloadsScreen: React.FC = () => {
|
||||
tmdbId: "98765",
|
||||
},
|
||||
};
|
||||
|
||||
console.log(isDevelopmentProvisioningProfile());
|
||||
|
||||
return (
|
||||
<ScreenLayout>
|
||||
<YStack gap={2} style={{ padding: 10 }}>
|
||||
@@ -101,4 +128,4 @@ const DownloadsScreen: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default DownloadsScreen;
|
||||
export default DownloadsScreen;
|
@@ -1,5 +1,6 @@
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { TouchableOpacity } from "react-native";
|
||||
import { Platform, TouchableOpacity } from "react-native";
|
||||
import { isDevelopmentProvisioningProfile } from "modules/check-ios-certificate";
|
||||
import { Text, View } from "tamagui";
|
||||
|
||||
import { usePlayerStore } from "~/stores/player/store";
|
||||
@@ -84,7 +85,10 @@ export const BottomControls = ({ isLocalAsset }: { isLocalAsset: boolean }) => {
|
||||
<AudioTrackSelector />
|
||||
<PlaybackSpeedSelector />
|
||||
<QualitySelector />
|
||||
<DownloadButton />
|
||||
{Platform.OS === "android" ||
|
||||
(Platform.OS === "ios" && isDevelopmentProvisioningProfile()) ? (
|
||||
<DownloadButton />
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
|
@@ -7,6 +7,7 @@
|
||||
"paths": {
|
||||
"~/*": ["./src/*"],
|
||||
"~/components/*": ["./src/components/*"],
|
||||
"modules/*": ["./modules/*"],
|
||||
},
|
||||
"jsx": "react-native",
|
||||
"tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json",
|
||||
|
Reference in New Issue
Block a user