Skip to content

Commit

Permalink
Create @config-plugins/apple-settings plugin (#182)
Browse files Browse the repository at this point in the history
* wip

* update repo meta

* working demo

* updates

* add linking

* Updated demo more

* drop assets

* add slider to example

* Updated types and usage

* add multi value page example
  • Loading branch information
EvanBacon committed Aug 5, 2023
1 parent 006142c commit bfb8838
Show file tree
Hide file tree
Showing 29 changed files with 2,297 additions and 5 deletions.
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ body:
label: Config Plugin
options:
- '@config-plugins/android-jsc-intl'
- '@config-plugins/apple-settings'
- '@config-plugins/detox'
- '@config-plugins/ffmpeg-kit-react-native'
- '@config-plugins/ios-stickers'
Expand Down
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ contact_links:
url: https://github.com/wix/Detox/issues
- about: FFmpeg Kit for React Native
name: 📦 ffmpeg-kit-react-native issues
url: https://github.com/tanersener/ffmpeg-kit/issues
url: https://github.com/arthenica/ffmpeg-kit/issues
- about: Adjust React Native SDK
name: 📦 react-native-adjust issues
url: https://github.com/adjust/react_native_sdk/issues
Expand Down
6 changes: 6 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ updates:
interval: daily
time: '09:00'
timezone: America/Los_Angeles
- package-ecosystem: npm
directory: packages/apple-settings
schedule:
interval: daily
time: '09:00'
timezone: America/Los_Angeles
- package-ecosystem: npm
directory: packages/detox
schedule:
Expand Down
137 changes: 137 additions & 0 deletions apps/apple-settings/app.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import withAppleSettings, {
ChildPane,
Group,
RadioGroup,
Slider,
Switch,
TextField,
Title,
} from "@config-plugins/apple-settings";
import { MultiValue } from "@config-plugins/apple-settings/build/models";
import { ConfigContext, ExpoConfig } from "expo/config";
import path from "node:path";

const folderName = path.basename(__dirname);
const cleanName = folderName.replace(/[_\s-]/g, "");
const appId = "dev.bacon." + cleanName;

function withWhiteLabel(config: Partial<ExpoConfig>) {
if (!config.extra) config.extra = {};
// Expose CI env variables to the app
config.extra.CI = process.env.CI;

if (!config.ios) config.ios = {};
config.ios.bundleIdentifier = appId;
if (!config.android) config.android = {};
config.android.package = appId;

return config;
}

module.exports = ({ config }: ConfigContext): Partial<ExpoConfig> => {
config = withWhiteLabel(config);

config = withAppleSettings(config as ExpoConfig, {
// The name of the .plist file to generate. Root is the default and must be provided.
Root: {
// The locales object is optional. If provided, it will be used to generate the localized .strings files.
locales: {
// The Apple locale code. This will be used to generate the .strings file.
en: {
// Name of the localized key.
Name: "Text Field",
},
},
// The page object is required. It will be used to generate the .plist file.
// The contents will be converted directly to plist.
page: {
// The `PreferenceSpecifiers` defines the UI elements to generate.
PreferenceSpecifiers: [
Title({
title: "Title",
value: "Default Title",
key: "title_preference",
}),
// Helper models can be used to generate the UI elements using a syntax that's
// similar to React Native.
TextField({
title: "Name",
key: "name_preference",
value: "",
keyboardType: "Alphabet",
autoCapitalize: "None",
autoCorrect: "No",
}),
Switch({
title: "Enabled",
key: "enabled_preference",
value: true,
}),
Slider({
key: "slider_preference",
value: 0.5,
}),
RadioGroup({
value: "option1",
key: "radio_preference",
items: [
{
title: "Option 1",
value: "option1",
},

{
title: "Option 2",
value: "option2",
},
],
}),
MultiValue({
title: "Multi Value",
key: "multi_value_preference",
value: "alpha",
items: [
{
title: "Alpha",
value: "alpha",
short: "α",
},
{
title: "Beta",
value: "beta",
short: "β",
},
{
title: "Delta",
value: "delta",
short: "Δ",
},
{
title: "Omega",
value: "omega",
short: "Ω",
},
],
}),

// Child panes can be used to create nested pages.
ChildPane({
title: "About",
}),
],
},
},
// About page
About: {
page: {
PreferenceSpecifiers: [
Group({
title: "About Info",
}),
],
},
},
});

return config;
};
6 changes: 6 additions & 0 deletions apps/apple-settings/app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"expo": {
"name": "apple-settings",
"icon": "https://icogen.vercel.app/api/icon?icon=🔌&color_hex=1832BA"
}
}
5 changes: 5 additions & 0 deletions apps/apple-settings/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { registerRootComponent } from "expo";

import App from "./src/App";

registerRootComponent(App);
24 changes: 24 additions & 0 deletions apps/apple-settings/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "@config-plugins-app/apple-settings",
"main": "./index.js",
"version": "1.0.0",
"scripts": {
"start": "expo start --dev-client",
"ios": "expo run:ios",
"android": "expo run:android"
},
"dependencies": {
"@config-plugins/ios-stickers": "*",
"@react-native-community/slider": "^4.4.2",
"@react-native-picker/picker": "2.4.10",
"expo": "^49.0.3",
"expo-splash-screen": "~0.20.4",
"expo-status-bar": "~1.6.0",
"react": "18.2.0",
"react-native": "0.72.3"
},
"devDependencies": {
"@babel/core": "^7.20.0"
},
"private": true
}
57 changes: 57 additions & 0 deletions apps/apple-settings/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React from "react";
import { Button, Linking, StyleSheet, View } from "react-native";

import {
SettingsRadioGroup,
SettingsSlider,
SettingsSwitch,
SettingsTextInput,
SettingsTitle,
} from "./SettingsViews";

const App = () => {
return (
<View style={styles.container}>
{/* <SettingsTitle settingsKey="title_preference" /> */}
<SettingsTextInput
style={{
height: 40,
borderColor: "gray",
borderWidth: 1,
padding: 8,
}}
settingsKey="name_preference"
/>
<SettingsSwitch settingsKey="enabled_preference" />
<SettingsSlider settingsKey="slider_preference" />
<View />
<SettingsRadioGroup settingsKey="radio_preference">
<SettingsRadioGroup.Item label="Option 1" value="option1" />
<SettingsRadioGroup.Item label="Option 2" value="option2" />
</SettingsRadioGroup>
<View />
<Button
title="Launch Settings"
onPress={() => {
Linking.openSettings();
}}
/>
</View>
);
};

const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "stretch",
paddingHorizontal: 56,
gap: 12,
},
value: {
fontSize: 24,
marginVertical: 12,
},
});

export default App;
91 changes: 91 additions & 0 deletions apps/apple-settings/src/SettingsViews.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import Slider from "@react-native-community/slider";
import { Picker } from "@react-native-picker/picker";
import React from "react";
import { Settings, Switch, Text, TextInput } from "react-native";

export function useSetting<T = string>(key: string): [T, (value: T) => void] {
const [value, setValue] = React.useState<T>(() => Settings.get(key));
React.useEffect(() => {
let isMounted = true;
const callback = Settings.watchKeys(key, () => {
if (isMounted) {
setValue(Settings.get(key));
}
});
return () => {
Settings.clearWatch(callback);
isMounted = false;
};
}, [key]);

return [
value,
(value) => {
Settings.set({ [key]: value });
setValue(value);
},
];
}

export function SettingsSlider({
settingsKey,
...props
}: { settingsKey: string } & Omit<
React.ComponentProps<typeof Slider>,
"selectedValue" | "onValueChange"
>) {
const [value, setValue] = useSetting<number>(settingsKey);
return <Slider {...props} value={value ?? 0} onValueChange={setValue} />;
}

export function SettingsRadioGroup({
settingsKey,
...props
}: { settingsKey: string } & Omit<
React.ComponentProps<typeof Picker>,
"selectedValue" | "onValueChange"
>) {
const [value, setValue] = useSetting<string>(settingsKey);
return (
<Picker
{...props}
selectedValue={value ?? "option1"}
onValueChange={setValue}
/>
);
}

SettingsRadioGroup.Item = Picker.Item;

export function SettingsSwitch({
settingsKey,
...props
}: { settingsKey: string } & Omit<
React.ComponentProps<typeof Switch>,
"value" | "onValueChange"
>) {
const [value, setValue] = useSetting<boolean>(settingsKey);
return <Switch {...props} value={!!value} onValueChange={setValue} />;
}

export function SettingsTitle({
settingsKey,
...props
}: { settingsKey: string } & Omit<
React.ComponentProps<typeof Text>,
"children"
>) {
const [value] = useSetting<string>(settingsKey);
return <Text {...props} children={value} />;
}

export function SettingsTextInput({
settingsKey,
...props
}: { settingsKey: string } & Omit<
React.ComponentProps<typeof TextInput>,
"value" | "onChangeText"
>) {
const [value, setValue] = useSetting<string>(settingsKey);
return <TextInput {...props} value={value} onChangeText={setValue} />;
}
4 changes: 4 additions & 0 deletions apps/apple-settings/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"compilerOptions": {},
"extends": "expo/tsconfig.base"
}
2 changes: 2 additions & 0 deletions packages/apple-settings/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// @generated by expo-module-scripts
module.exports = require('expo-module-scripts/eslintrc.base.js');
Loading

0 comments on commit bfb8838

Please sign in to comment.