casdoor-app/Header.js

202 lines
5.7 KiB
JavaScript

// Copyright 2023 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import * as React from "react";
import {Dimensions, StyleSheet, View} from "react-native";
import {Appbar, Avatar, Menu, Text, TouchableRipple} from "react-native-paper";
import {useNotifications} from "react-native-notificated";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import CasdoorLoginPage, {CasdoorLogout} from "./CasdoorLoginPage";
import useStore from "./useStorage";
import {useAccountSync} from "./useAccountStore";
import LoginMethodSelector from "./LoginMethodSelector";
const {width} = Dimensions.get("window");
const Header = () => {
const {userInfo, clearAll} = useStore();
const {isSyncing, syncError, clearSyncError} = useAccountSync();
const [showLoginPage, setShowLoginPage] = React.useState(false);
const [menuVisible, setMenuVisible] = React.useState(false);
const [loginMethod, setLoginMethod] = React.useState(null);
const {notify} = useNotifications();
const openMenu = () => setMenuVisible(true);
const closeMenu = () => setMenuVisible(false);
const handleMenuLogoutClicked = () => {
handleCasdoorLogout();
closeMenu();
};
const {openActionSheet} = LoginMethodSelector({
onSelectMethod: (method) => {
setLoginMethod(method);
setShowLoginPage(true);
},
});
const handleCasdoorLogin = () => {
openActionSheet();
};
const handleHideLoginPage = () => {
setShowLoginPage(false);
};
const handleCasdoorLogout = () => {
CasdoorLogout();
clearAll();
clearSyncError();
};
const handleSyncErrorPress = () => {
notify("error", {
params: {
title: "Error",
description: syncError || "An unknown error occurred during synchronization.",
},
});
};
return (
<Appbar.Header mode="small" style={styles.header}>
<Appbar.Content
title={
<View style={styles.titleContainer}>
<Text style={styles.titleTextCasdoor}>Casdoor</Text>
</View>
}
style={styles.titleWrapper}
/>
<View style={styles.rightContainer}>
{userInfo !== null && (
<Icon
name={
isSyncing
? "cloud-sync-outline"
: syncError
? "cloud-off-outline"
: "cloud-check-outline"
}
size={22}
color={isSyncing ? "#FFC107" : syncError ? "#E53935" : "#4CAF50"}
style={styles.syncIcon}
onPress={(isSyncing || syncError === null) ? null : handleSyncErrorPress}
/>
)}
<Menu
visible={menuVisible}
onDismiss={closeMenu}
contentStyle={styles.menuContent}
anchorPosition="bottom"
mode="elevated"
anchor={
<TouchableRipple
onPress={userInfo === null ? handleCasdoorLogin : openMenu}
style={styles.buttonContainer}
>
<View style={styles.buttonContent}>
{userInfo !== null && (
<Avatar.Image
size={28}
source={{uri: userInfo.avatar}}
style={styles.avatar}
/>
)}
<Text style={[
styles.buttonText,
userInfo === null && {marginLeft: 0},
]}>
{userInfo === null ? "Login" : userInfo.name}
</Text>
</View>
</TouchableRipple>
}
>
<Menu.Item onPress={handleMenuLogoutClicked} title="Logout" />
</Menu>
</View>
{showLoginPage && (
<CasdoorLoginPage
onWebviewClose={handleHideLoginPage}
initialMethod={loginMethod}
/>
)}
</Appbar.Header>
);
};
const styles = StyleSheet.create({
header: {
backgroundColor: "#F2F2F2",
height: 56,
},
rightContainer: {
flexDirection: "row",
alignItems: "center",
paddingRight: width * 0.04,
},
titleWrapper: {
alignItems: "flex-start",
},
titleContainer: {
flexDirection: "row",
alignItems: "baseline",
},
titleTextCasdoor: {
fontSize: Math.max(24, width * 0.05),
fontWeight: "bold",
color: "#212121",
fontFamily: "Lato_700Bold",
},
buttonContainer: {
borderRadius: 24,
overflow: "hidden",
borderWidth: 0.5,
borderColor: "#DDDDDD",
},
buttonContent: {
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
paddingVertical: 6,
paddingHorizontal: 14,
},
buttonText: {
fontSize: Math.max(14, width * 0.042),
fontWeight: "600",
marginLeft: 8,
color: "#424242",
fontFamily: "Roboto_500Medium",
},
menuContent: {
backgroundColor: "#FAFAFA",
borderRadius: 8,
elevation: 2,
shadowColor: "#000000",
shadowOffset: {width: 0, height: 1},
shadowOpacity: 0.1,
shadowRadius: 2,
},
avatar: {
backgroundColor: "transparent",
},
syncIcon: {
marginRight: 12,
},
});
export default Header;