mirror of
https://github.com/movie-web/native-app.git
synced 2025-09-13 08:03:26 +00:00
add header and background design
This commit is contained in:
@@ -28,11 +28,11 @@
|
||||
"@react-native-anywhere/polyfill-base64": "0.0.1-alpha.0",
|
||||
"@react-navigation/native": "^6.1.9",
|
||||
"@salihgun/react-native-video-processor": "^0.3.1",
|
||||
"@tamagui/animations-moti": "^1.91.4",
|
||||
"@tamagui/babel-plugin": "^1.91.4",
|
||||
"@tamagui/config": "^1.91.4",
|
||||
"@tamagui/metro-plugin": "^1.91.4",
|
||||
"@tamagui/toast": "1.91.4",
|
||||
"@tamagui/animations-moti": "^1.94.0",
|
||||
"@tamagui/babel-plugin": "^1.94.0",
|
||||
"@tamagui/config": "^1.94.0",
|
||||
"@tamagui/metro-plugin": "^1.94.0",
|
||||
"@tamagui/toast": "1.94.0",
|
||||
"@tanstack/react-query": "^5.22.2",
|
||||
"burnt": "^0.12.2",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
@@ -75,7 +75,7 @@
|
||||
"react-native-svg": "14.1.0",
|
||||
"react-native-web": "^0.19.10",
|
||||
"subsrt-ts": "^2.1.2",
|
||||
"tamagui": "^1.91.4",
|
||||
"tamagui": "^1.94.0",
|
||||
"zustand": "^4.4.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@@ -16,7 +16,7 @@ import Item from "~/components/item/item";
|
||||
import ScreenLayout from "~/components/layout/ScreenLayout";
|
||||
import { SearchBar } from "~/components/ui/Searchbar";
|
||||
|
||||
export default function HomeScreen() {
|
||||
export default function SearchScreen() {
|
||||
const [query, setQuery] = useState("");
|
||||
const translateY = useSharedValue(0);
|
||||
const fadeAnim = useSharedValue(1);
|
||||
@@ -98,42 +98,46 @@ export default function HomeScreen() {
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1 }}>
|
||||
<ScreenLayout>
|
||||
<ScrollView
|
||||
onScrollBeginDrag={handleScrollBegin}
|
||||
onMomentumScrollEnd={handleScrollEnd}
|
||||
scrollEnabled={searchResultsLoaded ? true : false}
|
||||
keyboardDismissMode="on-drag"
|
||||
keyboardShouldPersistTaps="handled"
|
||||
contentContainerStyle={{ flexGrow: 1 }}
|
||||
>
|
||||
<ScreenLayout>
|
||||
{searchResultsLoaded && (
|
||||
<Animated.View style={[searchResultsStyle, { flex: 1 }]}>
|
||||
<View flexDirection="row" flexWrap="wrap">
|
||||
{data?.map((item, index) => (
|
||||
<View
|
||||
key={index}
|
||||
paddingHorizontal={12}
|
||||
paddingBottom={12}
|
||||
width="50%"
|
||||
>
|
||||
<Item data={item} />
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
</Animated.View>
|
||||
)}
|
||||
</ScreenLayout>
|
||||
<View>
|
||||
<Animated.View style={[searchResultsStyle, { flex: 1 }]}>
|
||||
<View flexDirection="row" flexWrap="wrap">
|
||||
{data?.map((item, index) => (
|
||||
<View
|
||||
key={index}
|
||||
paddingHorizontal={12}
|
||||
paddingBottom={12}
|
||||
width="50%"
|
||||
>
|
||||
<Item data={item} />
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
</Animated.View>
|
||||
</View>
|
||||
</ScrollView>
|
||||
<Animated.View
|
||||
style={[
|
||||
{ position: "absolute", left: 0, right: 0, bottom: 0 },
|
||||
{
|
||||
position: "absolute",
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
},
|
||||
animatedStyle,
|
||||
]}
|
||||
>
|
||||
<SearchBar onSearchChange={setQuery} />
|
||||
</Animated.View>
|
||||
</View>
|
||||
</ScreenLayout>
|
||||
);
|
||||
}
|
||||
|
||||
|
28
apps/expo/src/components/BrandPill.tsx
Normal file
28
apps/expo/src/components/BrandPill.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import { Image, Text, View } from "tamagui";
|
||||
|
||||
import Icon from "../../assets/images/icon-transparent.png";
|
||||
|
||||
export function BrandPill() {
|
||||
return (
|
||||
<View
|
||||
flexDirection="row"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
paddingHorizontal="$2.5"
|
||||
paddingVertical="$2"
|
||||
gap={2}
|
||||
opacity={0.8}
|
||||
backgroundColor="$pillBackground"
|
||||
borderRadius={24}
|
||||
pressStyle={{
|
||||
opacity: 1,
|
||||
scale: 1.05,
|
||||
}}
|
||||
>
|
||||
<Image source={Icon} height={20} width={20} />
|
||||
<Text fontSize="$4" fontWeight="$bold" paddingRight={5}>
|
||||
movie-web
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
41
apps/expo/src/components/layout/Header.tsx
Normal file
41
apps/expo/src/components/layout/Header.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import { Linking } from "react-native";
|
||||
import { FontAwesome6, MaterialIcons } from "@expo/vector-icons";
|
||||
import { Circle, View } from "tamagui";
|
||||
|
||||
import { DISCORD_LINK, GITHUB_LINK } from "~/constants/core";
|
||||
import { BrandPill } from "../BrandPill";
|
||||
|
||||
export function Header() {
|
||||
return (
|
||||
<View alignItems="center" gap="$3" flexDirection="row">
|
||||
<BrandPill />
|
||||
|
||||
<Circle
|
||||
backgroundColor="$pillBackground"
|
||||
size="$2.5"
|
||||
pressStyle={{
|
||||
opacity: 1,
|
||||
scale: 1.05,
|
||||
}}
|
||||
onPress={async () => {
|
||||
await Linking.openURL(DISCORD_LINK);
|
||||
}}
|
||||
>
|
||||
<MaterialIcons name="discord" size={20} color="white" />
|
||||
</Circle>
|
||||
<Circle
|
||||
backgroundColor="$pillBackground"
|
||||
size="$2.5"
|
||||
pressStyle={{
|
||||
opacity: 1,
|
||||
scale: 1.05,
|
||||
}}
|
||||
onPress={async () => {
|
||||
await Linking.openURL(GITHUB_LINK);
|
||||
}}
|
||||
>
|
||||
<FontAwesome6 name="github" size={20} color="white" />
|
||||
</Circle>
|
||||
</View>
|
||||
);
|
||||
}
|
@@ -1,24 +1,34 @@
|
||||
import { Text, View } from "tamagui";
|
||||
import { View } from "tamagui";
|
||||
import { LinearGradient } from "tamagui/linear-gradient";
|
||||
|
||||
import { Header } from "./Header";
|
||||
|
||||
interface Props {
|
||||
title?: React.ReactNode | string;
|
||||
subtitle?: string;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
export default function ScreenLayout({ title, subtitle, children }: Props) {
|
||||
export default function ScreenLayout({ children }: Props) {
|
||||
return (
|
||||
<View flex={1} padding={44} backgroundColor="$screenBackground">
|
||||
{typeof title === "string" && (
|
||||
<Text fontWeight="bold" fontSize={24}>
|
||||
{title}
|
||||
</Text>
|
||||
)}
|
||||
{typeof title !== "string" && title}
|
||||
<Text fontSize={16} fontWeight="bold" marginTop={1}>
|
||||
{subtitle}
|
||||
</Text>
|
||||
<View paddingVertical={12}>{children}</View>
|
||||
</View>
|
||||
<LinearGradient
|
||||
flex={1}
|
||||
paddingVertical="$4"
|
||||
paddingHorizontal="$7"
|
||||
colors={[
|
||||
"$shade900",
|
||||
"$purple900",
|
||||
"$purple800",
|
||||
"$shade700",
|
||||
"$shade900",
|
||||
]}
|
||||
locations={[0.02, 0.15, 0.2, 0.4, 0.8]}
|
||||
start={[0, 0]}
|
||||
end={[1, 1]}
|
||||
flexGrow={1}
|
||||
>
|
||||
<Header />
|
||||
<View paddingVertical="$4" flexGrow={1}>
|
||||
{children}
|
||||
</View>
|
||||
</LinearGradient>
|
||||
);
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { Image, Text, View } from "tamagui";
|
||||
import { Text, View } from "tamagui";
|
||||
|
||||
import { usePlayerStore } from "~/stores/player/store";
|
||||
import Icon from "../../../assets/images/icon-transparent.png";
|
||||
import { BrandPill } from "../BrandPill";
|
||||
import { BackButton } from "./BackButton";
|
||||
import { Controls } from "./Controls";
|
||||
|
||||
@@ -39,21 +39,8 @@ export const Header = () => {
|
||||
: ""}
|
||||
</Text>
|
||||
)}
|
||||
<View
|
||||
height="$3.5"
|
||||
width="$11"
|
||||
flexDirection="row"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
gap={2}
|
||||
paddingHorizontal="$4"
|
||||
paddingVertical="$1"
|
||||
opacity={0.8}
|
||||
backgroundColor="$pillBackground"
|
||||
borderRadius={24}
|
||||
>
|
||||
<Image source={Icon} height={24} width={24} />
|
||||
<Text fontWeight="bold">movie-web</Text>
|
||||
<View alignItems="center" justifyContent="center" width={130}>
|
||||
<BrandPill />
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
|
@@ -28,7 +28,7 @@ export const QualitySelector = () => {
|
||||
(key) => qualities[key as keyof typeof qualities]!.url === videoSrc.uri,
|
||||
);
|
||||
|
||||
qualityMap = Object.keys(qualities).map((key: string) => ({
|
||||
qualityMap = Object.keys(qualities).map((key) => ({
|
||||
quality: key,
|
||||
url: qualities[key as keyof typeof qualities]!.url,
|
||||
}));
|
||||
|
@@ -24,6 +24,7 @@ import { useScrape } from "~/hooks/player/useSourceScrape";
|
||||
import { convertMetaToScrapeMedia } from "~/lib/meta";
|
||||
import { PlayerStatus } from "~/stores/player/slices/interface";
|
||||
import { usePlayerStore } from "~/stores/player/store";
|
||||
import { BackButton } from "./BackButton";
|
||||
import { ScrapeCard, ScrapeItem } from "./ScrapeCard";
|
||||
|
||||
interface ScraperProcessProps {
|
||||
@@ -169,6 +170,9 @@ export const ScraperProcess = ({
|
||||
justifyContent="center"
|
||||
backgroundColor="$screenBackground"
|
||||
>
|
||||
<View position="absolute" top={40} left={40}>
|
||||
<BackButton />
|
||||
</View>
|
||||
<ScrollView
|
||||
ref={scrollViewRef}
|
||||
contentContainerStyle={{
|
||||
|
@@ -17,7 +17,6 @@ export const MWInput = styled(Input, {
|
||||
outlineStyle: "none",
|
||||
focusStyle: {
|
||||
borderColor: "$colorTransparent",
|
||||
backgroundColor: "$searchFocused",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@@ -51,7 +51,6 @@ export function SearchBar({
|
||||
|
||||
return (
|
||||
<View
|
||||
marginBottom={12}
|
||||
flexDirection="row"
|
||||
alignItems="center"
|
||||
borderRadius={999}
|
||||
@@ -67,8 +66,7 @@ export function SearchBar({
|
||||
onChangeText={handleChange}
|
||||
ref={inputRef}
|
||||
placeholder="What are you looking for?"
|
||||
width="80%"
|
||||
borderColor={isFocused ? theme.colorTransparent : theme.inputBorder}
|
||||
width="75%"
|
||||
backgroundColor={
|
||||
isFocused ? theme.searchFocused : theme.searchBackground
|
||||
}
|
||||
|
2
apps/expo/src/constants/core.ts
Normal file
2
apps/expo/src/constants/core.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export const DISCORD_LINK = "https://movie-web.github.io/links/discord";
|
||||
export const GITHUB_LINK = "https://github.com/movie-web";
|
@@ -21,7 +21,9 @@ export const useThemeStore = create(
|
||||
updateTheme(newTheme);
|
||||
set((state) => {
|
||||
state.theme = newTheme;
|
||||
void setAlternateAppIcon(newTheme);
|
||||
setAlternateAppIcon(newTheme).catch(() => {
|
||||
console.log("Failed to set alternate app icon");
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
@@ -83,22 +83,97 @@ const createThemeConfig = (tokens: Tokens) => ({
|
||||
inputPlaceholderText: tokens.shade.c300,
|
||||
inputText: tokens.white,
|
||||
inputIconColor: tokens.shade.c50,
|
||||
|
||||
red100: tokens.semantic.red.c100,
|
||||
red200: tokens.semantic.red.c200,
|
||||
red300: tokens.semantic.red.c300,
|
||||
red400: tokens.semantic.red.c400,
|
||||
|
||||
green100: tokens.semantic.green.c100,
|
||||
green200: tokens.semantic.green.c200,
|
||||
green300: tokens.semantic.green.c300,
|
||||
green400: tokens.semantic.green.c400,
|
||||
|
||||
silver100: tokens.semantic.silver.c100,
|
||||
silver200: tokens.semantic.silver.c200,
|
||||
silver300: tokens.semantic.silver.c300,
|
||||
silver400: tokens.semantic.silver.c400,
|
||||
|
||||
yellow100: tokens.semantic.yellow.c100,
|
||||
yellow200: tokens.semantic.yellow.c200,
|
||||
yellow300: tokens.semantic.yellow.c300,
|
||||
yellow400: tokens.semantic.yellow.c400,
|
||||
|
||||
rose100: tokens.semantic.rose.c100,
|
||||
rose200: tokens.semantic.rose.c200,
|
||||
rose300: tokens.semantic.rose.c300,
|
||||
rose400: tokens.semantic.rose.c400,
|
||||
|
||||
blue50: tokens.blue.c50,
|
||||
blue100: tokens.blue.c100,
|
||||
blue200: tokens.blue.c200,
|
||||
blue300: tokens.blue.c300,
|
||||
blue400: tokens.blue.c400,
|
||||
blue500: tokens.blue.c500,
|
||||
blue600: tokens.blue.c600,
|
||||
blue700: tokens.blue.c700,
|
||||
blue800: tokens.blue.c800,
|
||||
blue900: tokens.blue.c900,
|
||||
|
||||
purple50: tokens.purple.c50,
|
||||
purple100: tokens.purple.c100,
|
||||
purple200: tokens.purple.c200,
|
||||
purple300: tokens.purple.c300,
|
||||
purple400: tokens.purple.c400,
|
||||
purple500: tokens.purple.c500,
|
||||
purple600: tokens.purple.c600,
|
||||
purple700: tokens.purple.c700,
|
||||
purple800: tokens.purple.c800,
|
||||
purple900: tokens.purple.c900,
|
||||
|
||||
ash50: tokens.ash.c50,
|
||||
ash100: tokens.ash.c100,
|
||||
ash200: tokens.ash.c200,
|
||||
ash300: tokens.ash.c300,
|
||||
ash400: tokens.ash.c400,
|
||||
ash500: tokens.ash.c500,
|
||||
ash600: tokens.ash.c600,
|
||||
ash700: tokens.ash.c700,
|
||||
ash800: tokens.ash.c800,
|
||||
ash900: tokens.ash.c900,
|
||||
|
||||
shade50: tokens.shade.c50,
|
||||
shade100: tokens.shade.c100,
|
||||
shade200: tokens.shade.c200,
|
||||
shade300: tokens.shade.c300,
|
||||
shade400: tokens.shade.c400,
|
||||
shade500: tokens.shade.c500,
|
||||
shade600: tokens.shade.c600,
|
||||
shade700: tokens.shade.c700,
|
||||
shade800: tokens.shade.c800,
|
||||
shade900: tokens.shade.c900,
|
||||
});
|
||||
|
||||
const openSansFace = {
|
||||
normal: { normal: "OpenSans-Regular", italic: "OpenSans-Italic" },
|
||||
bold: { normal: "OpenSans-Bold", italic: "OpenSans-BoldItalic" },
|
||||
300: { normal: "OpenSans-Light", italic: "OpenSans-LightItalic" },
|
||||
500: { normal: "OpenSans-Regular", italic: "OpenSans-Italic" },
|
||||
600: { normal: "OpenSans-SemiBold", italic: "OpenSans-SemiBoldItalic" },
|
||||
700: { normal: "OpenSans-Bold", italic: "OpenSans-BoldItalic" },
|
||||
800: { normal: "OpenSans-ExtraBold", italic: "OpenSans-ExtraBoldItalic" },
|
||||
normal: { normal: "OpenSansRegular", italic: "OpenSansItalic" },
|
||||
bold: { normal: "OpenSansBold", italic: "OpenSansBoldItalic" },
|
||||
300: { normal: "OpenSansLight", italic: "OpenSansLightItalic" },
|
||||
500: { normal: "OpenSansRegular", italic: "OpenSansItalic" },
|
||||
600: { normal: "OpenSansSemiBold", italic: "OpenSansSemiBoldItalic" },
|
||||
700: { normal: "OpenSansBold", italic: "OpenSansBoldItalic" },
|
||||
800: { normal: "OpenSansExtraBold", italic: "OpenSansExtraBoldItalic" },
|
||||
};
|
||||
|
||||
const headingFont = createFont({
|
||||
size: config.fonts.heading.size,
|
||||
lineHeight: config.fonts.heading.lineHeight,
|
||||
weight: config.fonts.heading.weight,
|
||||
weight: {
|
||||
light: 300,
|
||||
normal: 500,
|
||||
semibold: 600,
|
||||
bold: 700,
|
||||
extrabold: 800,
|
||||
},
|
||||
letterSpacing: config.fonts.heading.letterSpacing,
|
||||
face: openSansFace,
|
||||
});
|
||||
@@ -106,7 +181,13 @@ const headingFont = createFont({
|
||||
const bodyFont = createFont({
|
||||
size: config.fonts.body.size,
|
||||
lineHeight: config.fonts.body.lineHeight,
|
||||
weight: config.fonts.body.weight,
|
||||
weight: {
|
||||
light: 300,
|
||||
normal: 500,
|
||||
semibold: 600,
|
||||
bold: 700,
|
||||
extrabold: 800,
|
||||
},
|
||||
letterSpacing: config.fonts.body.letterSpacing,
|
||||
face: openSansFace,
|
||||
});
|
||||
|
1658
pnpm-lock.yaml
generated
1658
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user