add header and background design

This commit is contained in:
Jorrin
2024-04-01 21:59:03 +02:00
parent 9ace6afc9e
commit 908da0bd24
14 changed files with 1003 additions and 963 deletions

View File

@@ -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": {

View File

@@ -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>
);
}

View 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>
);
}

View 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>
);
}

View File

@@ -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>
);
}

View File

@@ -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>
);

View File

@@ -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,
}));

View File

@@ -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={{

View File

@@ -17,7 +17,6 @@ export const MWInput = styled(Input, {
outlineStyle: "none",
focusStyle: {
borderColor: "$colorTransparent",
backgroundColor: "$searchFocused",
},
},
},

View File

@@ -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
}

View 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";

View File

@@ -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");
});
});
},
};

View File

@@ -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

File diff suppressed because it is too large Load Diff