[
  {
    "path": ".expo-shared/assets.json",
    "content": "{\n  \"12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb\": true,\n  \"40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd\": true\n}\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules/\n.expo/\ndist/\nnpm-debug.*\n*.jks\n*.p8\n*.p12\n*.key\n*.mobileprovision\n*.orig.*\nweb-build/\n\n# macOS\n.DS_Store\n"
  },
  {
    "path": ".svgrrc",
    "content": "{\n  \"memo\": true,\n  \"native\": true,\n  \"replaceAttrValues\": {\n    \"fill\": \"{props.fill}\",\n    \"stroke\": \"{props.stroke}\"\n  }\n}\n"
  },
  {
    "path": "App.tsx",
    "content": "import { StyleSheet } from \"react-native\";\n\nimport { StatusBar } from \"expo-status-bar\";\nimport { SafeAreaProvider } from \"react-native-safe-area-context\";\n\nimport Wallpaper from \"@react-native-ios/components/Wallpaper\";\nimport Footer from \"@react-native-ios/components/Footer/Footer\";\nimport SwipeableProvider from \"@react-native-ios/components/SwipeableProvider\";\nimport { GestureHandlerRootView } from \"react-native-gesture-handler\";\nimport { Page1, Page2 } from \"@react-native-ios/screens/Home\";\n\nexport default function App() {\n  return (\n    <GestureHandlerRootView style={styles.gestureHandler}>\n      <SafeAreaProvider>\n        <StatusBar style=\"light\" />\n        <Wallpaper />\n        <SwipeableProvider pages={[<Page1 />, <Page2 />]} />\n        <Footer />\n      </SafeAreaProvider>\n    </GestureHandlerRootView>\n  );\n}\n\nconst styles = StyleSheet.create({\n  gestureHandler: {\n    flex: 1,\n  },\n});\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 Enes Ozturk\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<!-- Banner Image -->\n\n<p align=\"center\">\n    <img alt=\"app icon\" height=\"128\" src=\"./assets/icon.png\">\n    <h1 align=\"center\">React Native iOS</h1>\n</p>\n\nThis is a fun project. I have always been impressed with iOS's UI gestures & animations. Previously I developed [React Native Hold Menu](https://github.com/enesozturk/react-native-hold-menu) which I inspired by the iOS messages, applications hold to open menus. This time, I tried to do the whole iOS itself for fun, and the result is awesome!\n\n## Stack\n\n- [Expo](https://expo.dev/) (SDK 44)\n- TypeScript\n- [React Native Reanimated](https://docs.swmansion.com/react-native-reanimated/)\n- [React Native Gesture Handler](https://docs.swmansion.com/react-native-gesture-handler/docs/)\n- [Expo Blur](https://github.com/expo/expo/tree/master/packages/expo-blur) for animatable blur component\n\n## Want to try?\n\nIf you want to try this on your device, you can install and run the app in a few seconds with the following commands;\n\nInstall the packages:\n\n```\nyarn install\n```\n\nStart the server\n\n```bash\nyarn start\n```\n\nYou will see a QR code on the terminal, you can scan this QR code with the device which you are on the same network. Or you can start the simulator by pressing `i`, on the terminal.\n\n> Currently app is not working the same with iOS on Android due to some issues with blur view animations and horizontal gesture animations. I may fix it later.\n\nThat's it, enjoy 🤞🏽\n\n## License\n\nThe source code is made available under the [MIT license](./LICENSE).\n\n## Show Your Support\n\nIf you like this project, please give a star and follow me on Github for more 🤩\n"
  },
  {
    "path": "app.json",
    "content": "{\n  \"expo\": {\n    \"name\": \"react-native-ios\",\n    \"slug\": \"react-native-ios\",\n    \"privacy\": \"public\",\n    \"platforms\": [\"ios\"],\n    \"sdkVersion\": \"44.0.0\",\n    \"githubUrl\": \"https://github.com/enesozturk/react-native-ios\",\n    \"version\": \"1.0.2\",\n    \"orientation\": \"portrait\",\n    \"owner\": \"enesozturk\",\n    \"icon\": \"./assets/icon.png\",\n    \"splash\": {\n      \"image\": \"./assets/splash.png\",\n      \"resizeMode\": \"cover\",\n      \"backgroundColor\": \"#000000\"\n    },\n    \"updates\": {\n      \"fallbackToCacheTimeout\": 0\n    },\n    \"assetBundlePatterns\": [\"**/*\"],\n    \"ios\": {\n      \"supportsTablet\": true,\n      \"bundleIdentifier\": \"com.enesozturk.react-native-ios\"\n    },\n    \"android\": {\n      \"adaptiveIcon\": {\n        \"foregroundImage\": \"./assets/adaptive-icon.png\",\n        \"backgroundColor\": \"#FFFFFF\"\n      }\n    },\n    \"web\": {\n      \"favicon\": \"./assets/favicon.png\"\n    }\n  }\n}\n"
  },
  {
    "path": "babel.config.aliases.js",
    "content": "module.exports = {\n  \"@react-native-ios/api\": \"./src/api\",\n  \"@react-native-ios/assets\": \"./src/assets\",\n  \"@react-native-ios/components\": \"./src/components\",\n  \"@react-native-ios/configs\": \"./src/configs\",\n  \"@react-native-ios/constants\": \"./src/constants\",\n  \"@react-native-ios/helpers\": \"./src/helpers\",\n  \"@react-native-ios/hooks\": \"./src/hooks\",\n  \"@react-native-ios/navigators\": \"./src/navigators\",\n  \"@react-native-ios/providers\": \"./src/providers\",\n  \"@react-native-ios/screens\": \"./src/screens\",\n  \"@react-native-ios/store\": \"./src/store\",\n  \"@react-native-ios/storybook\": \"./src/storybook\",\n  \"@react-native-ios/translations\": \"./src/translations\",\n  \"@react-native-ios/utils\": \"./src/utils\",\n};\n"
  },
  {
    "path": "babel.config.js",
    "content": "const aliases = require(\"./babel.config.aliases\");\n\nmodule.exports = function (api) {\n  api.cache(true);\n  return {\n    presets: [\"babel-preset-expo\"],\n    plugins: [\n      [\"module-resolver\", { root: [\"./\"], alias: aliases }],\n      \"react-native-reanimated/plugin\",\n    ],\n  };\n};\n"
  },
  {
    "path": "declarations.d.ts",
    "content": "declare module \"*.svg\" {\n  import React from \"react\";\n  import { SvgProps } from \"react-native-svg\";\n  const content: React.FC<SvgProps>;\n  export default content;\n}\n\ndeclare module \"*.jpg\";\n"
  },
  {
    "path": "metro.config.js",
    "content": "const { getDefaultConfig } = require(\"expo/metro-config\");\n\nmodule.exports = (async () => {\n  const {\n    resolver: { sourceExts, assetExts },\n  } = await getDefaultConfig(__dirname);\n  return {\n    ...getDefaultConfig,\n    transformer: {\n      babelTransformerPath: require.resolve(\"react-native-svg-transformer\"),\n      getTransformOptions: async () => ({\n        transform: {\n          inlineRequires: true,\n        },\n      }),\n    },\n    resolver: {\n      assetExts: assetExts.filter((ext) => ext !== \"svg\"),\n      sourceExts: [...sourceExts, \"svg\"],\n    },\n  };\n})();\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"react-native-ios\",\n  \"version\": \"1.0.0\",\n  \"main\": \"node_modules/expo/AppEntry.js\",\n  \"scripts\": {\n    \"start\": \"expo start\",\n    \"android\": \"expo start --android\",\n    \"ios\": \"expo start --ios\",\n    \"web\": \"expo start --web\",\n    \"eject\": \"expo eject\"\n  },\n  \"dependencies\": {\n    \"expo\": \"~44.0.0\",\n    \"expo-blur\": \"~11.0.0\",\n    \"expo-status-bar\": \"~1.2.0\",\n    \"expo-web-browser\": \"~10.1.0\",\n    \"react\": \"17.0.1\",\n    \"react-dom\": \"17.0.1\",\n    \"react-native\": \"0.64.3\",\n    \"react-native-gesture-handler\": \"~2.1.0\",\n    \"react-native-reanimated\": \"~2.3.1\",\n    \"react-native-safe-area-context\": \"3.3.2\",\n    \"react-native-svg\": \"^12.1.1\",\n    \"react-native-web\": \"0.17.1\"\n  },\n  \"devDependencies\": {\n    \"@babel/core\": \"^7.12.9\",\n    \"@svgr/cli\": \"^6.2.0\",\n    \"@types/react\": \"~17.0.21\",\n    \"@types/react-native\": \"~0.64.12\",\n    \"babel-plugin-module-resolver\": \"^4.1.0\",\n    \"react-native-svg-transformer\": \"^1.0.0\",\n    \"typescript\": \"~4.3.5\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "src/components/AnimatedInput/AnimatedInput.hooks.ts",
    "content": "import {\n  Extrapolate,\n  interpolate,\n  useAnimatedStyle,\n  useSharedValue,\n} from \"react-native-reanimated\";\n\nimport theme from \"@react-native-ios/constants/theme\";\nimport { SCREEN_WIDTH } from \"@react-native-ios/constants/ui\";\n\nconst INPUT_WIDTH = SCREEN_WIDTH - 54;\n\nconst useAnimatedInput = () => {\n  const containerWidth = useSharedValue(0);\n  const cancelTextWidth = useSharedValue(0);\n  const active = useSharedValue(0);\n\n  const animatedContainerStyles = useAnimatedStyle(\n    () => ({\n      width: \"100%\",\n    }),\n    []\n  );\n\n  const animatedContentContainerStyles = useAnimatedStyle(\n    () => ({\n      height: interpolate(\n        active.value,\n        [0, 1],\n        [22 + 26, 22 + 12], // 48 - 24 24/2 14, 38-20 18/2 7\n        Extrapolate.CLAMP\n      ),\n      width: interpolate(\n        active.value,\n        [0, 1],\n        [INPUT_WIDTH, INPUT_WIDTH - cancelTextWidth.value],\n        Extrapolate.CLAMP\n      ),\n      borderRadius: interpolate(\n        active.value,\n        [0, 1],\n        [theme.spacing.md, 12],\n        Extrapolate.CLAMP\n      ),\n    }),\n    [cancelTextWidth]\n  );\n\n  const animatedInputContainerStyles = useAnimatedStyle(\n    () => ({\n      position: \"absolute\",\n      left: interpolate(\n        active.value,\n        [0, 1],\n        [containerWidth.value / 2, 0],\n        Extrapolate.CLAMP\n      ),\n    }),\n    []\n  );\n\n  const animatedInputStyles = useAnimatedStyle(\n    () => ({\n      marginLeft: interpolate(\n        active.value,\n        [0, 1],\n        [INPUT_WIDTH / 2 - 30, (theme.spacing.sm + theme.spacing.xs) * 2 - 2],\n        Extrapolate.CLAMP\n      ),\n      marginBottom: interpolate(\n        active.value,\n        [0, 1],\n        [4, 2],\n        Extrapolate.CLAMP\n      ),\n      transform: [\n        {\n          scale: interpolate(\n            active.value,\n            [0, 1],\n            [1, 0.84],\n            Extrapolate.CLAMP\n          ),\n        },\n      ],\n    }),\n    []\n  );\n\n  const animatedCancelTextStyles = useAnimatedStyle(\n    () => ({\n      opacity: active.value,\n    }),\n    []\n  );\n\n  const animatedSearchIconStyles = useAnimatedStyle(\n    () => ({\n      opacity: 1,\n      top: interpolate(active.value, [0, 1], [12, 8], Extrapolate.CLAMP),\n      left: interpolate(\n        active.value,\n        [0, 1],\n        [INPUT_WIDTH / 2 - 30 - 32, 8],\n        Extrapolate.CLAMP\n      ),\n    }),\n    []\n  );\n\n  const animatedSearchIconProps = useAnimatedStyle(\n    () => ({\n      width: interpolate(active.value, [0, 1], [20, 18], Extrapolate.CLAMP),\n      height: interpolate(active.value, [0, 1], [20, 18], Extrapolate.CLAMP),\n    }),\n    []\n  );\n\n  const animatedMicIconStyles = useAnimatedStyle(\n    () => ({\n      opacity: active.value,\n      top: interpolate(active.value, [0, 1], [14, 8], Extrapolate.CLAMP),\n    }),\n    []\n  );\n\n  return {\n    active,\n    cancelTextWidth,\n    containerWidth,\n    animatedSearchIconStyles,\n    animatedSearchIconProps,\n    animatedMicIconStyles,\n    animatedContainerStyles,\n    animatedContentContainerStyles,\n    animatedInputContainerStyles,\n    animatedInputStyles,\n    animatedCancelTextStyles,\n  };\n};\n\nexport default useAnimatedInput;\n"
  },
  {
    "path": "src/components/AnimatedInput/AnimatedInput.styles.ts",
    "content": "import { StyleSheet } from \"react-native\";\n\nimport theme from \"@react-native-ios/constants/theme\";\nimport { SCREEN_WIDTH } from \"@react-native-ios/constants/ui\";\n\nexport default StyleSheet.create({\n  container: {\n    display: \"flex\",\n    flexDirection: \"row\",\n    justifyContent: \"flex-start\",\n    alignItems: \"center\",\n    marginBottom: theme.spacing.lg,\n    paddingHorizontal: 56 / 2 - 8,\n  },\n  contentContainer: {\n    width: SCREEN_WIDTH - theme.spacing.lg,\n    display: \"flex\",\n    flexDirection: \"row\",\n    position: \"relative\",\n    backgroundColor: theme.colors.white.white25,\n  },\n  input: {\n    paddingHorizontal: theme.spacing.sm,\n    paddingRight: 36,\n    color: theme.colors.white.white75,\n    ...theme.font.title2,\n  },\n  searchIconContainer: {\n    position: \"absolute\",\n  },\n  michrophoneIconContainer: {\n    position: \"absolute\",\n    right: 0,\n  },\n  cancelText: {\n    ...theme.font.body,\n    textAlignVertical: \"center\",\n    color: theme.colors.white.white50,\n    height: \"100%\",\n    paddingVertical: theme.spacing.sm13,\n  },\n});\n"
  },
  {
    "path": "src/components/AnimatedInput/AnimatedIntput.tsx",
    "content": "import React from \"react\";\nimport { TextInput, View } from \"react-native\";\n\nimport Animated, { withTiming } from \"react-native-reanimated\";\nimport { TapGestureHandler } from \"react-native-gesture-handler\";\n\nimport theme from \"@react-native-ios/constants/theme\";\nimport SearchSVG from \"@react-native-ios/assets/svg/search.svg\";\nimport MichrophoneSVG from \"@react-native-ios/assets/svg/michrophone.svg\";\n\nimport styles from \"./AnimatedInput.styles\";\nimport useAnimatedInput from \"./AnimatedInput.hooks\";\nimport { useRef } from \"react\";\nimport CancelButton from \"./CancelButton\";\n\nconst AnimatedTextInput = Animated.createAnimatedComponent(TextInput);\nconst AnimatedSearchIcon = Animated.createAnimatedComponent(SearchSVG);\n\nexport default function AnimatedIntput() {\n  const {\n    active,\n    cancelTextWidth,\n    containerWidth,\n    animatedSearchIconStyles,\n    animatedMicIconStyles,\n    animatedSearchIconProps,\n    animatedContainerStyles,\n    animatedContentContainerStyles,\n    animatedInputStyles,\n    animatedCancelTextStyles,\n  } = useAnimatedInput();\n  const inputRef = useRef(null);\n\n  return (\n    <TapGestureHandler onEnded={() => {}}>\n      <Animated.View style={[styles.container, animatedContainerStyles]}>\n        <TapGestureHandler\n          onEnded={() => {\n            inputRef?.current?.focus();\n            active.value = withTiming(1, { duration: 300 });\n          }}\n        >\n          <Animated.View\n            onLayout={(e) => {\n              containerWidth.value = e.nativeEvent.layout.width;\n            }}\n            style={[styles.contentContainer, animatedContentContainerStyles]}\n          >\n            <Animated.View\n              style={[styles.searchIconContainer, animatedSearchIconStyles]}\n            >\n              <AnimatedSearchIcon\n                animatedProps={animatedSearchIconProps}\n                fill={theme.colors.white.white50}\n              />\n            </Animated.View>\n            <Animated.View\n              style={[styles.michrophoneIconContainer, animatedMicIconStyles]}\n            >\n              <MichrophoneSVG height={18} fill={theme.colors.white.white50} />\n            </Animated.View>\n            <AnimatedTextInput\n              ref={inputRef}\n              placeholder=\"Search\"\n              placeholderTextColor={theme.colors.white.white75}\n              autoCorrect={false}\n              autoCompleteType=\"off\"\n              keyboardAppearance=\"dark\"\n              style={[styles.input, animatedInputStyles]}\n              onFocus={() => {\n                active.value = withTiming(1, { duration: 300 });\n              }}\n              onBlur={() => {\n                active.value = withTiming(0, { duration: 300 });\n              }}\n            />\n          </Animated.View>\n        </TapGestureHandler>\n        <CancelButton\n          active={active}\n          animatedCancelTextStyles={animatedCancelTextStyles}\n          onClose={() => {\n            inputRef?.current?.blur();\n          }}\n          cancelTextWidth={cancelTextWidth}\n        />\n      </Animated.View>\n    </TapGestureHandler>\n  );\n}\n"
  },
  {
    "path": "src/components/AnimatedInput/CancelButton.tsx",
    "content": "import React from \"react\";\nimport { StyleSheet, View } from \"react-native\";\n\nimport { TapGestureHandler } from \"react-native-gesture-handler\";\nimport Animated, { SharedValue, withSpring } from \"react-native-reanimated\";\n\nimport { SPRING_CONFIG } from \"@react-native-ios/constants/animation\";\nimport theme from \"@react-native-ios/constants/theme\";\n\ntype CancelButtonProps = {\n  active: SharedValue<number>;\n  cancelTextWidth: SharedValue<number>;\n  animatedCancelTextStyles: any;\n  onClose: () => void;\n};\n\nexport default function CancelButton({\n  active,\n  cancelTextWidth,\n  animatedCancelTextStyles,\n  onClose,\n}: CancelButtonProps) {\n  return (\n    <View\n      style={styles.container}\n      onLayout={(e) => {\n        cancelTextWidth.value = e.nativeEvent.layout.width;\n      }}\n    >\n      <TapGestureHandler\n        onEnded={() => {\n          onClose?.();\n          active.value = withSpring(0, SPRING_CONFIG);\n        }}\n      >\n        <Animated.Text style={[styles.cancelText, animatedCancelTextStyles]}>\n          Cancel\n        </Animated.Text>\n      </TapGestureHandler>\n    </View>\n  );\n}\n\nconst styles = StyleSheet.create({\n  container: {\n    paddingVertical: theme.spacing.sm13,\n    display: \"flex\",\n    flexDirection: \"row\",\n  },\n  cancelText: {\n    ...theme.font.body,\n    color: theme.colors.white.white50,\n    height: \"100%\",\n    marginLeft: 4,\n  },\n});\n"
  },
  {
    "path": "src/components/AnimatedInput/index.ts",
    "content": "export { default } from \"./AnimatedIntput\";\n"
  },
  {
    "path": "src/components/AppItem/AppItem.constants.ts",
    "content": "import { SCREEN_WIDTH } from \"@react-native-ios/constants/ui\";\n\nexport const APP_ICON_WIDTH_RATIO = 828 / 128;\nexport const APP_ICON_SIZE = SCREEN_WIDTH / APP_ICON_WIDTH_RATIO;\n"
  },
  {
    "path": "src/components/AppItem/AppItem.styles.ts",
    "content": "import { StyleSheet } from \"react-native\";\n\nimport theme from \"@react-native-ios/constants/theme\";\n\nimport { APP_ICON_SIZE } from \"./AppItem.constants\";\n\nexport default StyleSheet.create({\n  container: {},\n  image: {\n    width: \"100%\",\n    height: \"100%\",\n    borderRadius: 17,\n  },\n  title: {\n    position: \"absolute\",\n    bottom: -20,\n    left: -8,\n    textAlign: \"center\",\n    color: \"white\",\n    width: APP_ICON_SIZE + 16,\n    ...theme.font.caption1,\n  },\n});\n"
  },
  {
    "path": "src/components/AppItem/AppItem.tsx",
    "content": "import { Image, Text, View } from \"react-native\";\nimport { APP_ICON_SIZE } from \"./AppItem.constants\";\n\nimport styles from \"./AppItem.styles\";\n\ntype AppItemProps = {\n  size?: \"small\" | undefined;\n  noTitle?: boolean;\n  title?: string;\n  icon: any;\n};\n\nexport default function AppItem({ icon, title, size, noTitle }: AppItemProps) {\n  return (\n    <View\n      style={[\n        styles.container,\n        {\n          width: size === \"small\" ? 16 : APP_ICON_SIZE,\n          height: size === \"small\" ? 16 : APP_ICON_SIZE,\n          borderRadius: size === \"small\" ? 4 : 17,\n        },\n      ]}\n    >\n      <Image style={styles.image} source={icon} />\n      {noTitle || size === \"small\" ? null : (\n        <Text\n          style={styles.title}\n          numberOfLines={1}\n          adjustsFontSizeToFit={false}\n        >\n          {title ? title : \"Square\"}\n        </Text>\n      )}\n    </View>\n  );\n}\n"
  },
  {
    "path": "src/components/AppItem/index.ts",
    "content": "export { default } from \"./AppItem\";\n"
  },
  {
    "path": "src/components/Footer/Footer.tsx",
    "content": "import { StyleSheet, View } from \"react-native\";\n\nimport { BlurView } from \"expo-blur\";\n\nimport AppItem from \"@react-native-ios/components/AppItem\";\nimport apps from \"@react-native-ios/constants/apps\";\nimport { SCREEN_WIDTH } from \"@react-native-ios/constants/ui\";\n\nexport default function Footer() {\n  return (\n    <View style={styles.container}>\n      <BlurView intensity={100} style={styles.contentContainer}>\n        {Object.keys(apps.footer).map((item, index) => {\n          return (\n            <AppItem\n              key={`footer-app-${item.title}-${index}`}\n              noTitle\n              icon={apps.footer[item].icon}\n            />\n          );\n        })}\n      </BlurView>\n    </View>\n  );\n}\n\nconst styles = StyleSheet.create({\n  container: {\n    width: \"100%\",\n    display: \"flex\",\n    flexDirection: \"column\",\n    alignItems: \"center\",\n  },\n  contentContainer: {\n    display: \"flex\",\n    flexDirection: \"row\",\n    justifyContent: \"space-between\",\n    paddingVertical: 20,\n    paddingHorizontal: 20,\n    position: \"absolute\",\n    bottom: 24,\n    width: SCREEN_WIDTH - 24,\n    borderRadius: 36,\n    overflow: \"hidden\",\n  },\n});\n"
  },
  {
    "path": "src/components/Footer/index.ts",
    "content": "export { default } from \"./Footer\";\n"
  },
  {
    "path": "src/components/SwipeableProvider/SwipeableProvider.styles.ts",
    "content": "import { StyleSheet } from \"react-native\";\n\nimport { SCREEN_HEIGHT, SCREEN_WIDTH } from \"@react-native-ios/constants/ui\";\n\nexport default StyleSheet.create({\n  pagesContainer: {\n    width: SCREEN_WIDTH * 2,\n    height: SCREEN_HEIGHT,\n    display: \"flex\",\n    flexDirection: \"row\",\n  },\n  pageContainer: {\n    width: SCREEN_WIDTH,\n    height: SCREEN_HEIGHT,\n    display: \"flex\",\n  },\n  container: {\n    width: SCREEN_WIDTH,\n    height: SCREEN_HEIGHT,\n  },\n  searchContainer: {\n    width: SCREEN_WIDTH,\n    height: SCREEN_HEIGHT,\n    ...StyleSheet.absoluteFillObject,\n  },\n});\n"
  },
  {
    "path": "src/components/SwipeableProvider/SwipeableProvider.tsx",
    "content": "import React from \"react\";\n\nimport { GestureDetector } from \"react-native-gesture-handler\";\nimport Animated from \"react-native-reanimated\";\n\nimport Search from \"@react-native-ios/screens/Search\";\nimport RightSearch from \"@react-native-ios/screens/Search/RightSearch\";\nimport LeftSearch from \"@react-native-ios/screens/Search/LeftSearch\";\nimport useSwipeableProvider from \"@react-native-ios/hooks/useSwipeableProvider\";\n\nimport styles from \"./SwipeableProvider.styles\";\n\ntype SwipeableProviderProps = {\n  pages: React.ReactNode[];\n};\n\nexport default function SwipeableProvider({ pages }: SwipeableProviderProps) {\n  const {\n    offsetY,\n    offsetX,\n    startX,\n    animatedStyles,\n    animatedPageContainerStyles,\n    animatedPagesContainerStyles,\n    swipeableProviderGesture,\n  } = useSwipeableProvider();\n\n  return (\n    <>\n      <Search {...{ offsetY }} />\n      <LeftSearch {...{ offsetX, startX }} />\n      <RightSearch {...{ offsetX, startX }} />\n      <GestureDetector gesture={swipeableProviderGesture}>\n        <Animated.View style={[styles.container, animatedStyles]}>\n          <Animated.View\n            style={[styles.pagesContainer, animatedPagesContainerStyles]}\n          >\n            {pages.map((item) => (\n              <Animated.View\n                style={[styles.pageContainer, animatedPageContainerStyles]}\n              >\n                {item}\n              </Animated.View>\n            ))}\n          </Animated.View>\n        </Animated.View>\n      </GestureDetector>\n    </>\n  );\n}\n"
  },
  {
    "path": "src/components/SwipeableProvider/index.ts",
    "content": "export { default } from \"./SwipeableProvider\";\n"
  },
  {
    "path": "src/components/Text.tsx",
    "content": "import React from \"react\";\n\nimport { Text as RNText, TextProps } from \"react-native\";\n\nexport default function Text(props: TextProps) {\n  return (\n    <RNText numberOfLines={1} adjustsFontSizeToFit={false} {...props}>\n      {props.children}\n    </RNText>\n  );\n}\n"
  },
  {
    "path": "src/components/Wallpaper.tsx",
    "content": "import { useLayoutEffect, useState } from \"react\";\nimport { Image, StyleSheet, View } from \"react-native\";\nimport { SCREEN_WIDTH } from \"../constants/ui\";\n\nimport WalpaperImage from \"../assets/img/wallpaper.jpg\";\n\nexport default function Wallpaper() {\n  const [imageWidth, setImageWidth] = useState(0);\n  const [imageHeight, setImageHeight] = useState(0);\n\n  useLayoutEffect(() => {\n    const { width, height } = Image.resolveAssetSource(WalpaperImage);\n    setImageWidth(SCREEN_WIDTH);\n    setImageHeight((SCREEN_WIDTH * height) / width);\n  }, []);\n\n  return (\n    <View\n      style={{\n        ...StyleSheet.absoluteFillObject,\n        zIndex: -1,\n      }}\n    >\n      <Image\n        resizeMode=\"contain\"\n        source={WalpaperImage}\n        style={{\n          height: imageHeight,\n          width: imageWidth,\n        }}\n      />\n    </View>\n  );\n}\n"
  },
  {
    "path": "src/components/WidgetItem/WidgetItem.constants.ts",
    "content": "import theme from \"@react-native-ios/constants/theme\";\nimport { SCREEN_WIDTH } from \"@react-native-ios/constants/ui\";\n\nexport const WIDGET_SQUARE_SIZE =\n  (SCREEN_WIDTH - (theme.spacing.lg + 4) * 2 - 21) / 2;\nexport const WIDGET_WIDE_SIZE = SCREEN_WIDTH - theme.spacing.lg * 2;\n"
  },
  {
    "path": "src/components/WidgetItem/WidgetItem.styles.ts",
    "content": "import { StyleSheet } from \"react-native\";\n\nimport theme from \"@react-native-ios/constants/theme\";\n\nimport { WIDGET_SQUARE_SIZE } from \"./WidgetItem.constants\";\n\nexport default StyleSheet.create({\n  container: {\n    width: WIDGET_SQUARE_SIZE,\n    height: WIDGET_SQUARE_SIZE,\n    borderRadius: theme.spacing.lg,\n    backgroundColor: theme.colors.white.white25,\n    overflow: \"hidden\",\n    marginBottom: theme.spacing.lg,\n  },\n});\n"
  },
  {
    "path": "src/components/WidgetItem/WidgetItem.tsx",
    "content": "import React from \"react\";\nimport { View } from \"react-native\";\n\nimport { BlurView } from \"expo-blur\";\nimport { TapGestureHandler } from \"react-native-gesture-handler\";\n\nimport { WIDGET_SQUARE_SIZE } from \"./WidgetItem.constants\";\nimport styles from \"./WidgetItem.styles\";\n\ntype WidgetItemProps = {\n  children?: React.ReactNode;\n  containerStyles?: object;\n  onPress?: () => void;\n  wide?: boolean;\n};\n\nexport default function WidgetItem({\n  children,\n  containerStyles,\n  wide,\n  onPress,\n}: WidgetItemProps) {\n  return (\n    <TapGestureHandler onEnded={onPress}>\n      <View\n        style={[\n          styles.container,\n          containerStyles,\n          {\n            width: wide ? \"100%\" : WIDGET_SQUARE_SIZE,\n          },\n        ]}\n      >\n        <BlurView\n          intensity={80}\n          style={{\n            width: wide ? \"100%\" : WIDGET_SQUARE_SIZE,\n            height: WIDGET_SQUARE_SIZE,\n          }}\n        >\n          {children}\n        </BlurView>\n      </View>\n    </TapGestureHandler>\n  );\n}\n"
  },
  {
    "path": "src/components/WidgetItem/index.ts",
    "content": "export { default } from \"./WidgetItem\";\n"
  },
  {
    "path": "src/configs/horizontalGestureCalculations.ts",
    "content": "import { SharedValue, withSpring } from \"react-native-reanimated\";\nimport { PanGestureHandlerEventPayload } from \"react-native-gesture-handler\";\n\nimport {\n  MIN_VELOCITY_Y_TO_ACTIVATE,\n  SNAP_POINTS_HORIZONTAL_AS_ARRAY,\n  SPRING_CONFIG,\n} from \"@react-native-ios/constants/animation\";\nimport { SCREEN_WIDTH } from \"@react-native-ios/constants/ui\";\n\ntype handleOnUpdateHorizontalProps = {\n  e: PanGestureHandlerEventPayload;\n  startX: SharedValue<number>;\n  offsetX: SharedValue<number>;\n};\n\nconst getNextSnapPoint = (offset: number) => {\n  \"worklet\";\n\n  let nextSnapPointAvailable = false;\n  let snapPoint = 0;\n\n  for (let i = 2; i < 10; i += 2) {\n    if (\n      offset <= SNAP_POINTS_HORIZONTAL_AS_ARRAY[i - 1] &&\n      offset >= SNAP_POINTS_HORIZONTAL_AS_ARRAY[i + 1]\n    ) {\n      snapPoint = SNAP_POINTS_HORIZONTAL_AS_ARRAY[i];\n      nextSnapPointAvailable = true;\n    }\n  }\n\n  return nextSnapPointAvailable ? snapPoint : -1;\n};\n\nexport const handleOnEndHorizontal = ({\n  e,\n  startX,\n  offsetX,\n}: handleOnUpdateHorizontalProps & {\n  destination: SharedValue<number>;\n}) => {\n  \"worklet\";\n\n  const velocity = Math.abs(e.velocityX);\n  const direction = e.translationX < 0 ? \"right\" : \"left\";\n  const nextXValue =\n    direction === \"right\"\n      ? offsetX.value - SCREEN_WIDTH\n      : offsetX.value + SCREEN_WIDTH;\n\n  startX.value = startX.value + e.translationX;\n\n  if (velocity > MIN_VELOCITY_Y_TO_ACTIVATE) {\n    const nextSnapPoint = getNextSnapPoint(nextXValue);\n    if (nextSnapPoint !== -1) {\n      offsetX.value = withSpring(nextSnapPoint, {\n        ...SPRING_CONFIG,\n        velocity,\n      });\n      startX.value = withSpring(nextSnapPoint, {\n        ...SPRING_CONFIG,\n        velocity,\n      });\n    }\n  } else {\n    const nextSnapPoint2 = getNextSnapPoint(offsetX.value + e.translationX);\n    console.log(\"else\", nextSnapPoint2);\n    offsetX.value = withSpring(nextSnapPoint2, {\n      ...SPRING_CONFIG,\n      velocity,\n    });\n    startX.value = withSpring(nextSnapPoint2, {\n      ...SPRING_CONFIG,\n      velocity,\n    });\n  }\n};\n"
  },
  {
    "path": "src/configs/verticalGestureCalculations.ts",
    "content": "import { SharedValue, withSpring } from \"react-native-reanimated\";\nimport { PanGestureHandlerEventPayload } from \"react-native-gesture-handler\";\n\nimport {\n  DISTANCE_TO_ACTIVATE,\n  MAX_OFFSET_TO_ANIMATE,\n  MIN_VELOCITY_Y_TO_ACTIVATE,\n  SPRING_CONFIG,\n} from \"@react-native-ios/constants/animation\";\n\ntype handleOnUpdateVerticalProps = {\n  e: PanGestureHandlerEventPayload;\n  offsetY: SharedValue<number>;\n};\n\nexport const handleOnEndVertical = ({\n  e,\n  offsetY,\n}: handleOnUpdateVerticalProps) => {\n  \"worklet\";\n\n  const velocity = Math.abs(e.velocityY);\n  const translation = Math.abs(e.translationY);\n\n  if (\n    translation > DISTANCE_TO_ACTIVATE ||\n    velocity > MIN_VELOCITY_Y_TO_ACTIVATE\n  ) {\n    offsetY.value = withSpring(MAX_OFFSET_TO_ANIMATE, {\n      ...SPRING_CONFIG,\n      overshootClamping: false,\n      velocity,\n    });\n  } else {\n    offsetY.value = withSpring(0, SPRING_CONFIG);\n  }\n};\n"
  },
  {
    "path": "src/constants/animation.ts",
    "content": "import { SCREEN_WIDTH } from \"./ui\";\n\nexport const BLUR_VIEW_MAX_INTENSITY = 75;\nexport const MAX_OFFSET_TO_ANIMATE = 90;\nexport const DISTANCE_TO_ACTIVATE = MAX_OFFSET_TO_ANIMATE / 2;\nexport const MIN_VELOCITY_Y_TO_ACTIVATE = 100;\n\nexport const SPRING_CONFIG = {\n  damping: 500,\n  stiffness: 1000,\n  mass: 3,\n  overshootClamping: true,\n  restDisplacementThreshold: 10,\n  restSpeedThreshold: 10,\n};\n\nexport const SNAP_POINTS_HORIZONTAL = {\n  LEFT_PAGE: SCREEN_WIDTH,\n  LEFT_PAGE_HALF: SCREEN_WIDTH / 2,\n  ORIGIN: 0,\n  FIRST_PAGE_HALF: SCREEN_WIDTH / -2,\n  SECOND_PAGE: SCREEN_WIDTH * -1,\n  SECOND_PAGE_HALF: (SCREEN_WIDTH * -3) / 2,\n  RIGHT_PAGE: SCREEN_WIDTH * -2, // -750\n};\n\nexport const SNAP_POINTS_HORIZONTAL_AS_ARRAY = [\n  SCREEN_WIDTH * 3,\n  (SCREEN_WIDTH * 3) / 2,\n  SCREEN_WIDTH,\n  SCREEN_WIDTH / 2, // 187.5\n  0,\n  SCREEN_WIDTH / -2, // -187\n  SCREEN_WIDTH * -1, // -375\n  (SCREEN_WIDTH * -3) / 2, // -375 + (-375/2) = -562\n  SCREEN_WIDTH * -2, // -375 * 2 == -750\n  (SCREEN_WIDTH * -5) / 2, // -375 * 2 + (-375/2) = -937\n  SCREEN_WIDTH * -3, // -375 * 3 = -1500\n  (SCREEN_WIDTH * -7) / 2, // -375 * 3 + (-375/2) = -1875\n];\n"
  },
  {
    "path": "src/constants/apps.ts",
    "content": "export default {\n  footer: {\n    settings: {\n      title: \"Settings\",\n      icon: require(\"../assets/img/app-icons/Settings.png\"),\n    },\n    safari: {\n      title: \"Safari\",\n      icon: require(\"../assets/img/app-icons/Safari.png\"),\n    },\n    photos: {\n      title: \"Photos\",\n      icon: require(\"../assets/img/app-icons/Photos.png\"),\n    },\n    messages: {\n      title: \"Messages\",\n      icon: require(\"../assets/img/app-icons/Messages.png\"),\n    },\n  },\n  home: {\n    row1: [\n      {\n        title: \"FaceTime\",\n        icon: require(\"../assets/img/app-icons/Facetime.png\"),\n      },\n      {\n        title: \"Messages\",\n        icon: require(\"../assets/img/app-icons/Messages.png\"),\n      },\n      {\n        title: \"Clock\",\n        icon: require(\"../assets/img/app-icons/Clock.png\"),\n      },\n      {\n        title: \"Weather\",\n        icon: require(\"../assets/img/app-icons/Weather.png\"),\n      },\n    ],\n    row2: [\n      {\n        title: \"Notes\",\n        icon: require(\"../assets/img/app-icons/Notes.png\"),\n      },\n      {\n        title: \"Calculator\",\n        icon: require(\"../assets/img/app-icons/Calculator.png\"),\n      },\n      {\n        title: \"Wallet\",\n        icon: require(\"../assets/img/app-icons/Wallet.png\"),\n      },\n      {\n        title: \"News\",\n        icon: require(\"../assets/img/app-icons/News.png\"),\n      },\n    ],\n    row3: [\n      {\n        title: \"Compass\",\n        icon: require(\"../assets/img/app-icons/Compass.png\"),\n      },\n      {\n        title: \"iTunes\",\n        icon: require(\"../assets/img/app-icons/iTunes.png\"),\n      },\n      {\n        title: \"AppStore\",\n        icon: require(\"../assets/img/app-icons/AppStore.png\"),\n      },\n      {\n        title: \"Home\",\n        icon: require(\"../assets/img/app-icons/Home.png\"),\n      },\n    ],\n  },\n  home2: {\n    row1: [],\n  },\n};\n"
  },
  {
    "path": "src/constants/theme.ts",
    "content": "export default {\n  colors: {\n    white: {\n      white100: \"rgba(255, 255, 255, 1)\",\n      white75: \"rgba(255, 255, 255, 0.75)\",\n      white50: \"rgba(255, 255, 255, 0.5)\",\n      white25: \"rgba(255, 255, 255, 0.25)\",\n      white15: \"rgba(255, 255, 255, 0.15)\",\n    },\n    black: {\n      black100: \"rgba(0, 0, 0, 1)\",\n      black75: \"rgba(0, 0, 0, 0.75)\",\n      black50: \"rgba(0, 0, 0, 0.5)\",\n      black25: \"rgba(0, 0, 0, 0.25)\",\n    },\n  },\n  spacing: {\n    xs: 4,\n    sm: 7,\n    sm13: 13,\n    md: 16,\n    lg: 24,\n    xl: 32,\n  },\n  font: {\n    largeTitle: { fontSize: 34, lineHeight: 41 },\n    title1: { fontSize: 28, lineHeight: 34 },\n    title2: { fontSize: 22, lineHeight: 28 },\n    title3: { fontSize: 20, lineHeight: 25 },\n    headline: { fontSize: 17, lineHeight: 22 },\n    body: { fontSize: 17, lineHeight: 22 },\n    callout: { fontSize: 16, lineHeight: 21 },\n    subhead: { fontSize: 15, lineHeight: 20 },\n    footnote: { fontSize: 13, lineHeight: 18 },\n    caption1: { fontSize: 12, lineHeight: 16 },\n    caption2: { fontSize: 11, lineHeight: 13 },\n  },\n};\n"
  },
  {
    "path": "src/constants/ui.ts",
    "content": "import { Dimensions } from \"react-native\";\n\nconst { width, height } = Dimensions.get(\"screen\");\n\nexport const SCREEN_WIDTH = width;\nexport const SCREEN_HEIGHT = height;\n"
  },
  {
    "path": "src/hooks/useGestureHandler.ts",
    "content": "import {\n  handleGestureOnEnd,\n  handleGestureOnStart,\n  handleGestureOnUpdate,\n} from \"@react-native-ios/utils/swipeableGestureHandlers\";\nimport { Gesture } from \"react-native-gesture-handler\";\nimport { SharedValue, useSharedValue } from \"react-native-reanimated\";\n\ntype useGestureHandlerProps = {\n  startX: SharedValue<number>;\n  offsetX: SharedValue<number>;\n  offsetY?: SharedValue<number>;\n};\n\nconst useGestureHandler = ({\n  startX,\n  offsetX,\n  offsetY,\n}: useGestureHandlerProps) => {\n  const direction = useSharedValue(0);\n  const destination = useSharedValue(0);\n\n  const swipeableProviderGesture = Gesture.Pan()\n    .onStart((e) => {\n      handleGestureOnStart({\n        direction,\n        e,\n        startX,\n      });\n    })\n    .onUpdate((e) => {\n      handleGestureOnUpdate({\n        direction,\n        e,\n        startX,\n        offsetX,\n        offsetY,\n      });\n    })\n    .onEnd((e) => {\n      handleGestureOnEnd({\n        direction,\n        e,\n        startX,\n        offsetX,\n        offsetY,\n        destination,\n      });\n    });\n\n  return { swipeableProviderGesture };\n};\n\nexport default useGestureHandler;\n"
  },
  {
    "path": "src/hooks/useSwipeableProvider.ts",
    "content": "import {\n  Extrapolate,\n  interpolate,\n  useAnimatedStyle,\n  useSharedValue,\n} from \"react-native-reanimated\";\nimport { useSafeAreaInsets } from \"react-native-safe-area-context\";\n\nimport {\n  MAX_OFFSET_TO_ANIMATE,\n  SNAP_POINTS_HORIZONTAL,\n} from \"@react-native-ios/constants/animation\";\nimport theme from \"@react-native-ios/constants/theme\";\n\nimport useGestureHandler from \"./useGestureHandler\";\n\nconst useSwipeableProvider = () => {\n  const { top } = useSafeAreaInsets();\n  const offsetY = useSharedValue(0);\n  const offsetX = useSharedValue(0);\n  const startX = useSharedValue(0);\n  const { swipeableProviderGesture } = useGestureHandler({\n    offsetY,\n    offsetX,\n    startX,\n  });\n\n  const animatedStyles = useAnimatedStyle(() => {\n    return {\n      transform: [{ translateY: offsetY.value }],\n      opacity: interpolate(\n        offsetY.value,\n        [0, MAX_OFFSET_TO_ANIMATE],\n        [1, 0.6],\n        Extrapolate.CLAMP\n      ),\n      paddingTop: top + theme.spacing.md,\n    };\n  });\n\n  const animatedPageContainerStyles = useAnimatedStyle(() => {\n    return {\n      transform: [\n        {\n          scale:\n            offsetX.value > 0\n              ? interpolate(\n                  offsetX.value,\n                  [\n                    SNAP_POINTS_HORIZONTAL.LEFT_PAGE,\n                    SNAP_POINTS_HORIZONTAL.ORIGIN,\n                  ],\n                  [0.85, 1],\n                  Extrapolate.CLAMP\n                )\n              : interpolate(\n                  offsetX.value,\n                  [\n                    SNAP_POINTS_HORIZONTAL.SECOND_PAGE,\n                    SNAP_POINTS_HORIZONTAL.RIGHT_PAGE,\n                  ],\n                  [1, 0.85],\n                  Extrapolate.CLAMP\n                ),\n        },\n      ],\n    };\n  });\n\n  const animatedPagesContainerStyles = useAnimatedStyle(() => {\n    return {\n      transform: [\n        {\n          translateX: Math.min(\n            Math.max(offsetX.value, SNAP_POINTS_HORIZONTAL.SECOND_PAGE),\n            SNAP_POINTS_HORIZONTAL.ORIGIN\n          ),\n        },\n      ],\n    };\n  }, [offsetX]);\n\n  return {\n    offsetY,\n    offsetX,\n    startX,\n    animatedStyles,\n    animatedPageContainerStyles,\n    animatedPagesContainerStyles,\n    swipeableProviderGesture,\n  };\n};\n\nexport default useSwipeableProvider;\n"
  },
  {
    "path": "src/screens/Home/Home.tsx",
    "content": "import { StyleSheet, View } from \"react-native\";\n\nimport { SCREEN_HEIGHT, SCREEN_WIDTH } from \"@react-native-ios/constants/ui\";\n\nimport AppItem from \"@react-native-ios/components/AppItem\";\nimport apps from \"@react-native-ios/constants/apps\";\n\nexport function Page1() {\n  return (\n    <View style={styles.container}>\n      <View style={styles.row}>\n        {apps.home.row1.map((item) => {\n          return <AppItem key={`app-${item.title}`} {...item} />;\n        })}\n      </View>\n      <View style={styles.row}>\n        {apps.home.row2.map((item) => {\n          return <AppItem key={`app-${item.title}`} {...item} />;\n        })}\n      </View>\n      <View style={styles.row}>\n        {apps.home.row3.map((item) => {\n          return <AppItem key={`app-${item.title}`} {...item} />;\n        })}\n      </View>\n    </View>\n  );\n}\n\nexport function Page2() {\n  return (\n    <View style={styles.container}>\n      <View style={styles.row}>\n        {apps.home.row3.map((item) => {\n          return <AppItem key={`app-${item.title}`} {...item} />;\n        })}\n      </View>\n      <View style={styles.row}>\n        {apps.home.row2.map((item) => {\n          return <AppItem key={`app-${item.title}`} {...item} />;\n        })}\n      </View>\n      <View style={styles.row}>\n        {apps.home.row1.map((item) => {\n          return <AppItem key={`app-${item.title}`} {...item} />;\n        })}\n      </View>\n    </View>\n  );\n}\n\nconst styles = StyleSheet.create({\n  gestureContainer: {\n    width: SCREEN_WIDTH * 2,\n    height: SCREEN_HEIGHT,\n    display: \"flex\",\n    flexDirection: \"row\",\n  },\n  container: {\n    flex: 1,\n    width: SCREEN_WIDTH,\n    display: \"flex\",\n    flexDirection: \"column\",\n  },\n  row: {\n    display: \"flex\",\n    flexDirection: \"row\",\n    width: \"100%\",\n    justifyContent: \"space-between\",\n    paddingHorizontal: 24,\n    marginBottom: 32,\n  },\n});\n"
  },
  {
    "path": "src/screens/Home/index.ts",
    "content": "export { Page1, Page2 } from \"./Home\";\n"
  },
  {
    "path": "src/screens/Search/AnimatedProvider.tsx",
    "content": "import React from \"react\";\nimport { Keyboard, StyleSheet, View } from \"react-native\";\n\nimport { BlurView } from \"expo-blur\";\nimport { TapGestureHandler } from \"react-native-gesture-handler\";\nimport Animated, {\n  Extrapolate,\n  interpolate,\n  SharedValue,\n  useAnimatedProps,\n  useAnimatedStyle,\n  useSharedValue,\n  withSpring,\n} from \"react-native-reanimated\";\nimport { useSafeAreaInsets } from \"react-native-safe-area-context\";\n\nconst AnimatedBlurView = Animated.createAnimatedComponent(BlurView);\n\nimport {\n  BLUR_VIEW_MAX_INTENSITY,\n  MAX_OFFSET_TO_ANIMATE,\n  SNAP_POINTS_HORIZONTAL,\n  SPRING_CONFIG,\n} from \"@react-native-ios/constants/animation\";\nimport { SCREEN_HEIGHT, SCREEN_WIDTH } from \"@react-native-ios/constants/ui\";\nimport theme from \"@react-native-ios/constants/theme\";\n\ntype AnimatedProviderProps = {\n  startPoint: number;\n  snapPoint: number;\n  direction: \"left\" | \"right\" | \"vertical\";\n  offset: SharedValue<number>;\n  start?: SharedValue<number>;\n  children: React.ReactNode;\n};\n\nexport default function AnimatedProvider({\n  startPoint = 0,\n  snapPoint = 0,\n  direction,\n  offset,\n  start,\n  children,\n}: AnimatedProviderProps) {\n  const { top } = useSafeAreaInsets();\n  const direMultiply = useSharedValue(direction == \"right\" ? -1 : 1);\n\n  const animatedBlurBackdropStyles = useAnimatedStyle(() => {\n    return {\n      zIndex:\n        offset.value * direMultiply.value > startPoint * direMultiply.value\n          ? 10\n          : -1,\n    };\n  });\n\n  const animatedBlurBackdropProps = useAnimatedProps(() => {\n    return {\n      intensity: interpolate(\n        offset.value * direMultiply.value,\n        [startPoint * direMultiply.value, snapPoint * direMultiply.value],\n        [0, BLUR_VIEW_MAX_INTENSITY],\n        Extrapolate.CLAMP\n      ),\n    };\n  });\n\n  const animatedContentStyles = useAnimatedStyle(\n    () => ({\n      opacity:\n        direction == \"vertical\"\n          ? interpolate(\n              offset.value * direMultiply.value,\n              [startPoint * direMultiply.value, snapPoint * direMultiply.value],\n              [0, 1],\n              Extrapolate.CLAMP\n            )\n          : 1,\n      transform:\n        direction === \"vertical\"\n          ? [{ translateY: offset.value - MAX_OFFSET_TO_ANIMATE }]\n          : [\n              {\n                translateX: interpolate(\n                  offset.value * direMultiply.value,\n                  [\n                    startPoint * direMultiply.value,\n                    snapPoint * direMultiply.value,\n                  ],\n                  [SCREEN_WIDTH * (direction === \"right\" ? 1 : -1), 0],\n                  Extrapolate.CLAMP\n                ),\n              },\n            ],\n    }),\n    [offset]\n  );\n\n  const handleTapOutside = () => {\n    offset.value = withSpring(startPoint, SPRING_CONFIG);\n    if (start) start.value = withSpring(startPoint, SPRING_CONFIG);\n    Keyboard.dismiss();\n  };\n\n  return (\n    <AnimatedBlurView\n      tint=\"dark\"\n      animatedProps={animatedBlurBackdropProps}\n      style={[styles.blurBackdrop, animatedBlurBackdropStyles]}\n    >\n      <TapGestureHandler numberOfTaps={1} onEnded={handleTapOutside}>\n        <View style={styles.container}>\n          <Animated.View\n            style={[\n              styles.contentContainer,\n              { paddingTop: top + theme.spacing.sm },\n              animatedContentStyles,\n            ]}\n          >\n            {children}\n          </Animated.View>\n        </View>\n      </TapGestureHandler>\n    </AnimatedBlurView>\n  );\n}\n\nconst styles = StyleSheet.create({\n  container: {\n    width: SCREEN_WIDTH,\n    height: SCREEN_HEIGHT,\n  },\n  contentContainer: {\n    display: \"flex\",\n    flexDirection: \"column\",\n    paddingHorizontal: theme.spacing.sm,\n  },\n  blurBackdrop: {\n    width: SCREEN_WIDTH,\n    height: SCREEN_HEIGHT,\n    ...StyleSheet.absoluteFillObject,\n  },\n});\n"
  },
  {
    "path": "src/screens/Search/LeftSearch.tsx",
    "content": "import React from \"react\";\n\nimport { SharedValue } from \"react-native-reanimated\";\n\nimport { SNAP_POINTS_HORIZONTAL } from \"@react-native-ios/constants/animation\";\nimport LeftSearchContent from \"./components/LeftSearchContent\";\nimport AnimatedProvider from \"./AnimatedProvider\";\n\ntype AnimatedProviderProps = {\n  offsetX: SharedValue<number>;\n  startX: SharedValue<number>;\n};\n\nexport default function LeftSearch({ offsetX, startX }: AnimatedProviderProps) {\n  return (\n    <AnimatedProvider\n      direction=\"left\"\n      startPoint={SNAP_POINTS_HORIZONTAL.ORIGIN}\n      snapPoint={SNAP_POINTS_HORIZONTAL.LEFT_PAGE}\n      offset={offsetX}\n      start={startX}\n    >\n      <LeftSearchContent />\n    </AnimatedProvider>\n  );\n}\n"
  },
  {
    "path": "src/screens/Search/RightSearch.tsx",
    "content": "import React from \"react\";\n\nimport { SharedValue } from \"react-native-reanimated\";\n\nimport { SNAP_POINTS_HORIZONTAL } from \"@react-native-ios/constants/animation\";\nimport LeftSearchContent from \"./components/LeftSearchContent\";\nimport AnimatedProvider from \"./AnimatedProvider\";\n\ntype AnimatedProviderProps = {\n  offsetX: SharedValue<number>;\n  startX: SharedValue<number>;\n};\n\nexport default function LeftSearch({ offsetX, startX }: AnimatedProviderProps) {\n  return (\n    <AnimatedProvider\n      direction=\"right\"\n      startPoint={SNAP_POINTS_HORIZONTAL.SECOND_PAGE}\n      snapPoint={SNAP_POINTS_HORIZONTAL.RIGHT_PAGE}\n      offset={offsetX}\n      start={startX}\n    >\n      <LeftSearchContent />\n    </AnimatedProvider>\n  );\n}\n"
  },
  {
    "path": "src/screens/Search/Search.styles.ts",
    "content": "import { StyleSheet } from \"react-native\";\n\nimport theme from \"@react-native-ios/constants/theme\";\n\nexport default StyleSheet.create({\n  searchContainer: {\n    display: \"flex\",\n    flexDirection: \"row\",\n    alignItems: \"center\",\n    marginBottom: theme.spacing.md,\n  },\n  searchInputContainer: {\n    flex: 1,\n  },\n  searchIconContainer: {\n    position: \"absolute\",\n    top: theme.spacing.sm + theme.spacing.xs - 1,\n    left: theme.spacing.sm + theme.spacing.xs,\n  },\n  michrophoneIconContainer: {\n    position: \"absolute\",\n    top: 8,\n    right: 12 + theme.spacing.sm,\n  },\n  searchInput: {\n    paddingVertical: theme.spacing.sm,\n    paddingLeft: (theme.spacing.sm + theme.spacing.xs) * 2 + 14,\n    paddingRight: theme.spacing.sm * 2 + theme.spacing.xs + 12,\n    backgroundColor: theme.colors.white.white15,\n    color: theme.colors.white.white75,\n    borderRadius: 12,\n    marginRight: 8,\n    ...theme.font.body,\n  },\n  cancelText: {\n    color: theme.colors.white.white50,\n    ...theme.font.body,\n  },\n  titleSectionContainer: {\n    display: \"flex\",\n    flexDirection: \"row\",\n    justifyContent: \"space-between\",\n    alignItems: \"center\",\n    paddingHorizontal: theme.spacing.xs,\n    marginBottom: theme.spacing.xs,\n  },\n  titleText: {\n    ...theme.font.title2,\n    fontWeight: \"600\",\n    color: \"white\",\n  },\n  showMoreText: {\n    ...theme.font.footnote,\n    color: theme.colors.white.white50,\n  },\n  appsContainer: {\n    display: \"flex\",\n    flexDirection: \"column\",\n    padding: theme.spacing.md,\n    borderRadius: theme.spacing.md,\n    width: \"100%\",\n    backgroundColor: theme.colors.white.white15,\n  },\n  row: {\n    display: \"flex\",\n    flexDirection: \"row\",\n    justifyContent: \"space-between\",\n    marginBottom: theme.spacing.xl,\n  },\n});\n"
  },
  {
    "path": "src/screens/Search/Search.tsx",
    "content": "import { MAX_OFFSET_TO_ANIMATE } from \"@react-native-ios/constants/animation\";\nimport React from \"react\";\n\nimport { SharedValue } from \"react-native-reanimated\";\n\nimport AnimatedProvider from \"./AnimatedProvider\";\nimport SearchContent from \"./SearchContent\";\n\ntype SearchProps = {\n  offsetY: SharedValue<number>;\n};\n\nexport default function Search({ offsetY }: SearchProps) {\n  return (\n    <AnimatedProvider\n      direction=\"vertical\"\n      snapPoint={MAX_OFFSET_TO_ANIMATE}\n      startPoint={0}\n      offset={offsetY}\n    >\n      <SearchContent />\n    </AnimatedProvider>\n  );\n}\n"
  },
  {
    "path": "src/screens/Search/SearchContent.tsx",
    "content": "import React from \"react\";\nimport { Pressable, Text, View } from \"react-native\";\n\nimport { TextInput } from \"react-native-gesture-handler\";\n\nimport SearchSVG from \"@react-native-ios/assets/svg/search.svg\";\nimport MichrophoneSVG from \"@react-native-ios/assets/svg/michrophone.svg\";\nimport ChevronRightSVG from \"@react-native-ios/assets/svg/chevron-right.svg\";\nimport AppItem from \"@react-native-ios/components/AppItem\";\nimport apps from \"@react-native-ios/constants/apps\";\n\nimport styles from \"./Search.styles\";\nimport theme from \"@react-native-ios/constants/theme\";\n\nexport default function SearchContent() {\n  return (\n    <>\n      <View style={styles.searchContainer}>\n        <View style={styles.searchInputContainer}>\n          <View style={styles.searchIconContainer}>\n            <SearchSVG\n              height={16}\n              width={16}\n              fill={theme.colors.white.white50}\n            />\n          </View>\n          <View style={styles.michrophoneIconContainer}>\n            <MichrophoneSVG\n              width={18}\n              height={18}\n              fill={theme.colors.white.white50}\n            />\n          </View>\n          <TextInput\n            placeholder=\"Search\"\n            placeholderTextColor={theme.colors.white.white75}\n            autoCorrect={false}\n            autoCompleteType=\"off\"\n            keyboardAppearance=\"dark\"\n            style={styles.searchInput}\n          />\n        </View>\n        <Pressable>\n          <Text style={styles.cancelText}>Cancel</Text>\n        </Pressable>\n      </View>\n      <View style={styles.titleSectionContainer}>\n        <Text style={styles.titleText}>Siri Suggestions</Text>\n        <ChevronRightSVG width={6} stroke={theme.colors.white.white50} />\n      </View>\n      <View style={styles.appsContainer}>\n        <View style={styles.row}>\n          {apps.home.row3.map((item) => {\n            return <AppItem key={`app-${item.title}`} {...item} />;\n          })}\n        </View>\n        <View style={[styles.row, { marginBottom: 24 }]}>\n          {apps.home.row1.map((item) => {\n            return <AppItem key={`app-${item.title}`} {...item} />;\n          })}\n        </View>\n      </View>\n    </>\n  );\n}\n"
  },
  {
    "path": "src/screens/Search/components/LeftSearchContent.styles.tsx",
    "content": "import { StyleSheet } from \"react-native\";\n\nimport theme from \"@react-native-ios/constants/theme\";\n\nexport default StyleSheet.create({\n  searchContainer: {\n    display: \"flex\",\n    flexDirection: \"row\",\n    alignItems: \"center\",\n    justifyContent: \"center\",\n    paddingHorizontal: theme.spacing.lg - theme.spacing.sm,\n  },\n  searchInputContainer: {\n    flex: 1,\n    paddingVertical: theme.spacing.md,\n    borderRadius: theme.spacing.md,\n    backgroundColor: theme.colors.white.white15,\n    display: \"flex\",\n    flexDirection: \"row\",\n    justifyContent: \"center\",\n  },\n  searchIconContainer: {\n    position: \"absolute\",\n    top: theme.spacing.sm + theme.spacing.xs - 1,\n    left: theme.spacing.sm + theme.spacing.xs,\n  },\n  michrophoneIconContainer: {\n    position: \"absolute\",\n    top: theme.spacing.sm,\n    right: 12 + theme.spacing.sm,\n  },\n  searchInput: {\n    paddingHorizontal: theme.spacing.sm,\n    color: theme.colors.white.white75,\n    ...theme.font.body,\n  },\n  cancelButton: {\n    position: \"absolute\",\n    right: 0,\n    opacity: 0,\n  },\n  cancelText: {\n    color: theme.colors.white.white50,\n    ...theme.font.body,\n  },\n  appsContainer: {\n    display: \"flex\",\n    flexDirection: \"column\",\n    padding: theme.spacing.lg + 4,\n    paddingHorizontal: 56 / 2 - 8,\n    paddingTop: 0,\n    width: \"100%\",\n    backgroundColor: \"transparent\",\n    overflow: \"hidden\",\n  },\n  row: {\n    display: \"flex\",\n    flexDirection: \"row\",\n    justifyContent: \"space-between\",\n  },\n  center: {\n    display: \"flex\",\n    flexDirection: \"column\",\n    alignItems: \"center\",\n    justifyContent: \"center\",\n    width: \"100%\",\n    height: \"100%\",\n  },\n  column: {\n    display: \"flex\",\n    flexDirection: \"column\",\n    alignItems: \"flex-start\",\n    justifyContent: \"flex-start\",\n    height: \"100%\",\n    width: \"100%\",\n    position: \"relative\",\n    padding: 24,\n  },\n  linkIconContainer: {\n    position: \"absolute\",\n    right: 16,\n    bottom: 16,\n  },\n  textBody: {\n    ...theme.font.body,\n    color: theme.colors.white.white50,\n  },\n  textBodySecondary: {\n    marginLeft: 8,\n    ...theme.font.body,\n    color: theme.colors.white.white75,\n  },\n});\n"
  },
  {
    "path": "src/screens/Search/components/LeftSearchContent.tsx",
    "content": "import React from \"react\";\nimport { Text, View } from \"react-native\";\n\nimport * as WebBrowser from \"expo-web-browser\";\n\nimport ELetterSVG from \"@react-native-ios/assets/svg/e-letter.svg\";\nimport GithubSVG from \"@react-native-ios/assets/svg/github.svg\";\nimport LinkSVG from \"@react-native-ios/assets/svg/link.svg\";\nimport MessageSVG from \"@react-native-ios/assets/svg/message.svg\";\nimport WidgetItem from \"@react-native-ios/components/WidgetItem\";\nimport AnimatedInput from \"@react-native-ios/components/AnimatedInput\";\n\nimport styles from \"./LeftSearchContent.styles\";\nimport theme from \"@react-native-ios/constants/theme\";\n\nexport default function LeftSearchContent() {\n  const handleNavigateWebsite = async () => {\n    await WebBrowser.openBrowserAsync(\"https://ozturkenes.com\");\n  };\n\n  const handleNavigateToGithub = async () => {\n    await WebBrowser.openBrowserAsync(\"https://github.com/enesozturk\");\n  };\n\n  return (\n    <>\n      <AnimatedInput />\n      <View style={styles.appsContainer}>\n        <View style={styles.row}>\n          <WidgetItem onPress={handleNavigateWebsite}>\n            <View style={styles.center}>\n              <View style={styles.linkIconContainer}>\n                <LinkSVG\n                  width={24}\n                  height={24}\n                  color={theme.colors.white.white50}\n                />\n              </View>\n              <ELetterSVG\n                color={theme.colors.white.white75}\n                width={48}\n                height={48}\n              />\n            </View>\n          </WidgetItem>\n          <WidgetItem />\n        </View>\n        <WidgetItem wide onPress={handleNavigateToGithub}>\n          <View\n            style={{\n              display: \"flex\",\n              flexDirection: \"column\",\n              alignItems: \"flex-start\",\n              justifyContent: \"flex-start\",\n              padding: 24,\n              height: \"100%\",\n              width: \"100%\",\n              position: \"relative\",\n            }}\n          >\n            <View style={styles.linkIconContainer}>\n              <LinkSVG\n                width={24}\n                height={24}\n                color={theme.colors.white.white50}\n              />\n            </View>\n            <Text style={styles.textBody}>\n              Did you like this app? Give me a star and follow me on Github for\n              more ⭐️\n            </Text>\n            <View\n              style={[\n                styles.row,\n                {\n                  alignItems: \"center\",\n                  justifyContent: \"flex-start\",\n                  marginTop: 24,\n                },\n              ]}\n            >\n              <GithubSVG\n                color={theme.colors.white.white75}\n                width={32}\n                height={32}\n              />\n              <Text style={styles.textBodySecondary}>\n                enesozturk/react-native-ios\n              </Text>\n            </View>\n          </View>\n        </WidgetItem>\n        <WidgetItem wide>\n          <View style={styles.column}>\n            <View style={styles.linkIconContainer}>\n              <LinkSVG\n                width={24}\n                height={24}\n                color={theme.colors.white.white50}\n              />\n            </View>\n            <Text style={styles.textBody}>\n              You need a React & React Native developer? Contact me!\n            </Text>\n            <View\n              style={[\n                styles.row,\n                {\n                  alignItems: \"center\",\n                  justifyContent: \"flex-start\",\n                  marginTop: 24,\n                },\n              ]}\n            >\n              <MessageSVG\n                color={theme.colors.white.white75}\n                width={32}\n                height={32}\n              />\n              <Text style={styles.textBodySecondary}>\n                enesozturk.d@gmail.com\n              </Text>\n            </View>\n          </View>\n        </WidgetItem>\n      </View>\n    </>\n  );\n}\n"
  },
  {
    "path": "src/screens/Search/index.ts",
    "content": "export { default } from \"./Search\";\n"
  },
  {
    "path": "src/utils/swipeableGestureHandlers.ts",
    "content": "import { PanGestureHandlerEventPayload } from \"react-native-gesture-handler\";\nimport { cancelAnimation, SharedValue } from \"react-native-reanimated\";\n\nimport { handleOnEndHorizontal } from \"@react-native-ios/configs/horizontalGestureCalculations\";\nimport { handleOnEndVertical } from \"@react-native-ios/configs/verticalGestureCalculations\";\n\ntype GestureHandlerStartProps = {\n  direction: SharedValue<number>;\n  e: PanGestureHandlerEventPayload;\n  startX: SharedValue<number>;\n};\n\ntype GestureHandlerProps = {\n  direction: SharedValue<number>;\n  e: PanGestureHandlerEventPayload;\n  offsetY?: SharedValue<number>;\n  startX: SharedValue<number>;\n  offsetX: SharedValue<number>;\n};\n\nexport const handleGestureOnStart = ({\n  e,\n  direction,\n  startX,\n}: GestureHandlerStartProps) => {\n  \"worklet\";\n\n  cancelAnimation(startX);\n  direction.value = Math.abs(e.translationX) > Math.abs(e.translationY) ? 1 : 0;\n};\n\nexport const handleGestureOnUpdate = ({\n  direction,\n  offsetY,\n  offsetX,\n  startX,\n  e,\n}: GestureHandlerProps) => {\n  \"worklet\";\n\n  if (direction.value) {\n    cancelAnimation(startX);\n    offsetX.value = e.translationX + startX.value;\n  } else {\n    if (offsetY) offsetY.value = Math.max(0, e.translationY);\n  }\n};\n\nexport const handleGestureOnEnd = ({\n  direction,\n  offsetY,\n  offsetX,\n  startX,\n  e,\n  destination,\n}: GestureHandlerProps & { destination: SharedValue<number> }) => {\n  \"worklet\";\n  if (direction.value) {\n    handleOnEndHorizontal({\n      e,\n      startX,\n      offsetX,\n      destination,\n    });\n  } else {\n    if (offsetY)\n      handleOnEndVertical({\n        e,\n        offsetY,\n      });\n  }\n};\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"extends\": \"expo/tsconfig.base\",\n  \"compilerOptions\": {\n    \"strict\": true,\n    \"baseUrl\": \"./src\",\n    \"paths\": {\n      \"@react-native-ios/api/*\": [\"./api/*\"],\n      \"@react-native-ios/assets/*\": [\"./assets/*\"],\n      \"@react-native-ios/configs/*\": [\"./configs/*\"],\n      \"@react-native-ios/constants/*\": [\"./constants/*\"],\n      \"@react-native-ios/components/*\": [\"./components/*\"],\n      \"@react-native-ios/helpers/*\": [\"./helpers/*\"],\n      \"@react-native-ios/hooks/*\": [\"./hooks/*\"],\n      \"@react-native-ios/jest/*\": [\"./jest/*\"],\n      \"@react-native-ios/navigators/*\": [\"./navigators/*\"],\n      \"@react-native-ios/providers/*\": [\"./providers/*\"],\n      \"@react-native-ios/screens/*\": [\"./screens/*\"],\n      \"@react-native-ios/store/*\": [\"./store/*\"],\n      \"@react-native-ios/storybook/*\": [\"./storybook/*\"],\n      \"@react-native-ios/translations/*\": [\"./translations/*\"],\n      \"@react-native-ios/utils/*\": [\"./utils/*\"]\n    }\n  }\n}\n"
  }
]