lfabl 1 settimana fa
parent
commit
4951c06f1b

+ 144 - 28
example/src/pages/components/userShortcut/index.tsx

@@ -7,6 +7,7 @@ import {
     View
 } from "react-native";
 import {
+    NCoreUIKitCoreComplexProvider,
     type ActivityStatusType,
     type IUserShortcutProps,
     STATUS_CHEAT_SHEET,
@@ -97,6 +98,36 @@ const UserShortcutPage = () => {
         setTitle
     ] = useState("Furkan Atakan BOZKURT");
 
+    const [
+        isShowLoginButtonWhenNoSession,
+        setIsShowLoginButtonWhenNoSession
+    ] = useState(true);
+
+    const [
+        isWorkWithCoreComplex,
+        setIsWorkWithCoreComplex
+    ] = useState(false);
+
+    const [
+        isShowRegisterButton,
+        setIsShowRegisterButton
+    ] = useState(false);
+
+    const [
+        isShowLogoutButton,
+        setIsShowLogoutButton
+    ] = useState(true);
+
+    const [
+        isShowArrowButton,
+        setIsShowArrowButton
+    ] = useState(true);
+
+    const [
+        isSessionActive,
+        setIsSessionActive
+    ] = useState(true);
+
     useLayoutEffect(() => {
         navigation.setOptions({
             headerTitle: "UserShortcut",
@@ -141,34 +172,56 @@ const UserShortcutPage = () => {
                     alignItems: "center"
                 }}
             >
-                <UserShortcut
-                    actionButtons={[
-                        {
-                            onPress: () => console.log("Profile pressed"),
-                            title: "Profile"
-                        },
-                        {
-                            onPress: () => console.log("Settings pressed"),
-                            title: "Settings"
-                        }
-                    ]}
-                    avatarProps={{
-                        statusIndicatorType: statusIndicatorType,
-                        title
-                    }}
-                    onLogoutPress={() => {
-                        console.log("Logout pressed");
-                    }}
-                    spreadBehaviour={spreadBehaviour}
-                    backgroundColor={backgroundColor}
-                    onPress={() => {
-                        console.log("Card pressed");
-                    }}
-                    isDisabled={isDisabled}
-                    statusText={statusText}
-                    subTitle={subTitle}
-                    title={title}
-                />
+                <NCoreUIKitCoreComplexProvider
+                    privateKeyID="test"
+                    rsaPublicKey="test"
+                    privateKey="test"
+                    appID="test"
+                >
+                    <UserShortcut
+                        isShowLoginButtonWhenNoSession={isShowLoginButtonWhenNoSession}
+                        actionButtons={[
+                            {
+                                onPress: () => console.log("Profile pressed"),
+                                title: "Profile"
+                            },
+                            {
+                                onPress: () => console.log("Settings pressed"),
+                                title: "Settings"
+                            }
+                        ]}
+                        avatarProps={{
+                            statusIndicatorType: statusIndicatorType,
+                            title
+                        }}
+                        isWorkWithCoreComplex={isWorkWithCoreComplex}
+                        isShowRegisterButton={isShowRegisterButton}
+                        isShowLogoutButton={isShowLogoutButton}
+                        onLogoutPress={(response, error) => {
+                            console.log("Logout pressed", {
+                                response,
+                                error
+                            });
+                        }}
+                        isShowArrowButton={isShowArrowButton}
+                        onRegisterPress={() => {
+                            console.log("Register pressed");
+                        }}
+                        onLoginPress={() => {
+                            console.log("Login pressed");
+                        }}
+                        spreadBehaviour={spreadBehaviour}
+                        backgroundColor={backgroundColor}
+                        isSessionActive={isSessionActive}
+                        onPress={() => {
+                            console.log("Card pressed");
+                        }}
+                        isDisabled={isDisabled}
+                        statusText={statusText}
+                        subTitle={subTitle}
+                        title={title}
+                    />
+                </NCoreUIKitCoreComplexProvider>
             </View>
             <View
                 style={{
@@ -303,6 +356,69 @@ const UserShortcutPage = () => {
                             isActive={isDisabled}
                             title="Disabled"
                         />
+                        <Switch
+                            onPress={() => setIsWorkWithCoreComplex(!isWorkWithCoreComplex)}
+                            isActive={isWorkWithCoreComplex}
+                            title="isWorkWithCoreComplex"
+                            spreadBehaviour="baseline"
+                        />
+                    </View>
+                    <View
+                        style={[
+                            styles.rowContainer,
+                            {
+                                gap: spaces.spacingMd
+                            }
+                        ]}
+                    >
+                        <Switch
+                            onPress={() => setIsShowLogoutButton(!isShowLogoutButton)}
+                            isActive={isShowLogoutButton}
+                            spreadBehaviour="baseline"
+                            title="isShowLogoutButton"
+                        />
+                        <Switch
+                            onPress={() => setIsShowArrowButton(!isShowArrowButton)}
+                            isActive={isShowArrowButton}
+                            spreadBehaviour="baseline"
+                            title="isShowArrowButton"
+                        />
+                    </View>
+                    <View
+                        style={[
+                            styles.rowContainer,
+                            {
+                                gap: spaces.spacingMd
+                            }
+                        ]}
+                    >
+                        <Switch
+                            onPress={() => setIsShowLoginButtonWhenNoSession(!isShowLoginButtonWhenNoSession)}
+                            isActive={isShowLoginButtonWhenNoSession}
+                            title="isShowLoginButtonWhenNoSession"
+                            spreadBehaviour="baseline"
+                        />
+                        <Switch
+                            onPress={() => setIsSessionActive(!isSessionActive)}
+                            spreadBehaviour="baseline"
+                            isActive={isSessionActive}
+                            title="isSessionActive"
+                        />
+                    </View>
+                    <View
+                        style={[
+                            styles.rowContainer,
+                            {
+                                gap: spaces.spacingMd
+                            }
+                        ]}
+                    >
+                        <Switch
+                            onPress={() => setIsShowRegisterButton(!isShowRegisterButton)}
+                            isActive={isShowRegisterButton}
+                            title="isShowRegisterButton"
+                            spreadBehaviour="baseline"
+                        />
                     </View>
                 </View>
             </View>

+ 12 - 0
example/web/webpack.config.js

@@ -119,6 +119,10 @@ module.exports = {
         ]
     },
     plugins: [
+        new webpack.ProvidePlugin({
+            process: "process/browser",
+            Buffer: ["buffer", "Buffer"],
+        }),
         new HtmlWebpackPlugin({
             template: path.resolve(appDirectory, "./public/index.html"),
             filename: "index.html"
@@ -164,6 +168,14 @@ module.exports = {
         })
     ],
     resolve: {
+        fallback: {
+            "crypto": require.resolve("crypto-browserify"),
+            "stream": require.resolve("stream-browserify"),
+            "process": require.resolve("process/browser"),
+            "vm": require.resolve("vm-browserify"),
+            "buffer": require.resolve("buffer/"),
+            "util": require.resolve("util/")
+        },
         symlinks: false,
         alias: {
             "react-native$": "react-native-web",

+ 8 - 0
package.json

@@ -106,9 +106,11 @@
         "babel-plugin-module-resolver": "5.0.3",
         "babel-plugin-react-native-web": "0.21.2",
         "babel-preset-expo": "56.0.15",
+        "buffer": "^6.0.3",
         "copy-webpack-plugin": "11.0.0",
         "copyfiles": "2.4.1",
         "cross-env": "10.1.0",
+        "crypto-browserify": "^3.12.1",
         "del-cli": "6.0.0",
         "eslint": "9.37.0",
         "eslint-import-resolver-typescript": "4.4.4",
@@ -127,6 +129,7 @@
         "html-webpack-plugin": "5.6.7",
         "jest": "29.6.3",
         "jsonc-eslint-parser": "2.4.1",
+        "process": "^0.11.10",
         "react": "19.2.3",
         "react-dom": "19.2.3",
         "react-native": "0.85.3",
@@ -136,16 +139,20 @@
         "react-native-web": "0.21.2",
         "react-test-renderer": "19.2.3",
         "release-it": "19.0.4",
+        "stream-browserify": "^3.0.0",
         "turbo": "2.5.6",
         "typescript": "5.9.3",
         "typescript-eslint": "8.46.1",
         "url-loader": "4.1.1",
+        "util": "^0.12.5",
+        "vm-browserify": "^1.1.2",
         "webpack": "5.107.1",
         "webpack-cli": "7.0.2",
         "webpack-dev-server": "5.2.4"
     },
     "peerDependencies": {
         "@react-native-clipboard/clipboard": ">= 1.16.3",
+        "core-complex-client": ">= 1.0.19",
         "lucide-react-native": ">= 1.16.0",
         "moment": "*",
         "ncore-context": ">= 1.0.7",
@@ -208,6 +215,7 @@
     },
     "dependencies": {
         "@react-native-clipboard/clipboard": "1.16.3",
+        "core-complex-client": "1.0.19",
         "lucide-react-native": "1.16.0",
         "moment": "https://git.nibgat.space/nibgat-community/moment.git",
         "ncore-context": "1.0.8",

+ 145 - 15
src/components/userShortcut/index.tsx

@@ -14,9 +14,14 @@ import {
     NCoreUIKitLocalize,
     NCoreUIKitTheme
 } from "../../core/hooks";
+import {
+    useContext as useCoreComplexContext
+} from "../../core/contexts/coreComplex";
 import {
     ChevronDown as ChevronDownIcon,
-    ChevronLeft as ChevronLeftIcon
+    ChevronLeft as ChevronLeftIcon,
+    UserPlus as UserPlusIcon,
+    LogIn as LogInIcon
 } from "lucide-react-native";
 import {
     STATUS_CHEAT_SHEET
@@ -27,10 +32,18 @@ import Button from "../button";
 import Text from "../text";
 
 const UserShortcut = ({
+    isShowLoginButtonWhenNoSession = true,
+    isShowRegisterButton = false,
+    isWorkWithCoreComplex = true,
     spreadBehaviour = "baseline",
+    isShowLogoutButton = true,
+    isShowArrowButton = true,
     backgroundColor = "mid",
+    isSessionActive = true,
+    onRegisterPress,
     actionButtons,
     onLogoutPress,
+    onLoginPress,
     avatarProps,
     customTheme,
     statusColor,
@@ -48,6 +61,10 @@ const UserShortcut = ({
         setIsBottomSheetOpen
     ] = useState(false);
 
+    const {
+        client: coreComplexClient
+    } = useCoreComplexContext();
+
     const {
         radiuses,
         spaces,
@@ -72,6 +89,13 @@ const UserShortcut = ({
             "success"
     );
 
+    const resolvedIsSessionActive = isWorkWithCoreComplex ?
+        !!coreComplexClient?.variables?.accessToken
+    :
+        isSessionActive;
+
+    const isNoSession = !resolvedIsSessionActive;
+
     const {
         subTitleContainer: subTitleContainerDynamicStyle,
         actionContainer: actionContainerDynamicStyle,
@@ -88,7 +112,7 @@ const UserShortcut = ({
     });
 
     const renderSubTitle = () => {
-        if(!subTitle && !resolvedStatusText) {
+        if(isNoSession || (!subTitle && !resolvedStatusText)) {
             return null;
         }
 
@@ -143,6 +167,10 @@ const UserShortcut = ({
     };
 
     const renderContent = () => {
+        if(isNoSession) {
+            return null;
+        }
+
         return <View
             style={stylesheet.contentContainer}
         >
@@ -159,6 +187,71 @@ const UserShortcut = ({
     };
 
     const renderAction = () => {
+        if(isNoSession) {
+            if(!isShowLoginButtonWhenNoSession && !isShowRegisterButton) {
+                return null;
+            }
+
+            return <View
+                style={[
+                    stylesheet.actionContainer,
+                    actionContainerDynamicStyle,
+                    {
+                        marginLeft: spaces.spacingXs,
+                        gap: spaces.spacingMd,
+                        flexDirection: "row"
+                    }
+                ]}
+            >
+                {
+                    isShowLoginButtonWhenNoSession ?
+                        <Button
+                            icon={({
+                                color,
+                                size
+                            }) => {
+                                return <LogInIcon
+                                    color={colors.content.icon[color]}
+                                    size={size + 2}
+                                />;
+                            }}
+                            onPress={() => onLoginPress?.()}
+                            title={localize("login")}
+                            spreadBehaviour="free"
+                            type="neutral"
+                            size="small"
+                        />
+                    :
+                        null
+                }
+                {
+                    isShowRegisterButton ?
+                        <Button
+                            icon={({
+                                color,
+                                size
+                            }) => {
+                                return <UserPlusIcon
+                                    color={colors.content.icon[color]}
+                                    size={size + 2}
+                                />;
+                            }}
+                            onPress={() => onRegisterPress?.()}
+                            title={localize("register")}
+                            spreadBehaviour="free"
+                            type="neutral"
+                            size="small"
+                        />
+                    :
+                        null
+                }
+            </View>;
+        }
+
+        if(!isShowArrowButton) {
+            return null;
+        }
+
         return <View
             style={[
                 stylesheet.actionContainer,
@@ -173,13 +266,13 @@ const UserShortcut = ({
                     if(isBottomSheetOpen) {
                         return <ChevronDownIcon
                             color={colors.content.icon[color]}
-                            size={size}
+                            size={size + 5}
                         />;
                     }
 
                     return <ChevronLeftIcon
                         color={colors.content.icon[color]}
-                        size={size}
+                        size={size + 5}
                     />;
                 }}
                 onPress={() => {
@@ -194,7 +287,7 @@ const UserShortcut = ({
     };
 
     const renderBadges = () => {
-        if(!badges || badges.length === 0) {
+        if(isNoSession || !badges || badges.length === 0) {
             return null;
         }
 
@@ -215,6 +308,10 @@ const UserShortcut = ({
     };
 
     const renderBottomSheet = () => {
+        if(isNoSession || !isShowArrowButton) {
+            return null;
+        }
+
         return <BottomSheet
             onClose={() => setIsBottomSheetOpen(false)}
             isActive={isBottomSheetOpen}
@@ -236,22 +333,50 @@ const UserShortcut = ({
                         />;
                     })
                 }
-                <Button
-                    title={localize("logout" as keyof NCoreUIKit.Translation)}
-                    onPress={() => {
-                        setIsBottomSheetOpen(false);
-                        if(onLogoutPress) onLogoutPress();
-                    }}
-                    spreadBehaviour="stretch"
-                    type="danger"
-                />
+                {
+                    isShowLogoutButton ?
+                        <Button
+                            title={localize("logout" as keyof NCoreUIKit.Translation)}
+                            onPress={async () => {
+                                setIsBottomSheetOpen(false);
+
+                                let response;
+                                let error;
+
+                                if(isWorkWithCoreComplex && coreComplexClient) {
+                                    try {
+                                        response = await (
+                                            coreComplexClient.NAuth as unknown as {
+                                                logout: () => Promise<unknown>;
+                                            }
+                                        ).logout();
+                                    } catch(e) {
+                                        error = e;
+                                    }
+                                }
+
+                                if(onLogoutPress) {
+                                    onLogoutPress(response, error);
+                                }
+                            }}
+                            spreadBehaviour="stretch"
+                            type="danger"
+                        />
+                    :
+                        null
+                }
             </View>
         </BottomSheet>;
     };
 
     return <Fragment>
         <TouchableOpacity
+            activeOpacity={isNoSession && (isShowLoginButtonWhenNoSession || isShowRegisterButton) ? 1 : 0.2}
             onPress={isDisabled ? undefined : () => {
+                if(isNoSession && (isShowLoginButtonWhenNoSession || isShowRegisterButton)) {
+                    return;
+                }
+
                 if(onPress) onPress();
             }}
             style={[
@@ -265,7 +390,12 @@ const UserShortcut = ({
                 style={stylesheet.avatarContainer}
             >
                 <Avatar
-                    {...avatarProps}
+                    {...(isNoSession ? {
+                        ...avatarProps,
+                        statusIndicatorType: undefined,
+                        title: undefined,
+                        url: undefined
+                    } : avatarProps)}
                     size="small"
                 />
                 {renderBadges()}

+ 9 - 1
src/components/userShortcut/type.ts

@@ -23,6 +23,7 @@ export type UserShortcutDynamicStyleType = {
 
 interface IUserShortcutProps {
     backgroundColor?: keyof NCoreUIKit.ContainerContentColors | "transparent";
+    onLogoutPress?: (response?: unknown, error?: unknown) => void;
     customTheme?: {
         gapPropagation?: keyof NCoreUIKit.GapPropagationKey;
         sharpness?: keyof NCoreUIKit.SharpnessKey;
@@ -32,10 +33,17 @@ interface IUserShortcutProps {
     style?: StyleProp<ViewStyle>[] | StyleProp<ViewStyle>;
     statusColor?: keyof NCoreUIKit.TextContentColors;
     spreadBehaviour?: UserShortcutSpreadBehaviour;
+    isShowLoginButtonWhenNoSession?: boolean;
     avatarProps?: Omit<IAvatarProps, "size">;
+    isWorkWithCoreComplex?: boolean;
+    isShowRegisterButton?: boolean;
     actionButtons?: IButtonProps[];
-    onLogoutPress?: () => void;
+    onRegisterPress?: () => void;
+    isShowLogoutButton?: boolean;
+    isShowArrowButton?: boolean;
+    onLoginPress?: () => void;
     maxWidth?: DimensionValue;
+    isSessionActive?: boolean;
     isDisabled?: boolean;
     onPress?: () => void;
     badges?: ReactNode[];

+ 56 - 0
src/core/contexts/coreComplex/cryptoPolyfill.ts

@@ -0,0 +1,56 @@
+import {
+    Buffer
+} from "buffer";
+import cryptoModule from "crypto";
+
+const _global = typeof window !== "undefined" ? window : (typeof global !== "undefined" ? global : null);
+if (_global) {
+    (_global as Record<string, unknown>).Buffer = (_global as Record<string, unknown>).Buffer || Buffer;
+    (_global as Record<string, unknown>).process = (_global as Record<string, unknown>).process || process;
+}
+
+if (typeof (cryptoModule as Record<string, unknown>).KeyObject === "undefined") {
+    class KeyObject {
+        type: string;
+        key: unknown;
+        constructor(type: string, key: unknown) {
+            this.type = type;
+            this.key = key;
+        }
+        export() {
+            return this.key;
+        }
+    }
+
+    Object.assign(cryptoModule, {
+        createSecretKey: function(key: unknown) {
+            const buf = (typeof key === "string" ? Buffer.from(key) : (Buffer.isBuffer(key) ? key : Buffer.from(String(key)))) as Buffer & { type: string, export: () => Buffer };
+            buf.type = "secret";
+            buf.export = () => buf;
+            return buf;
+        },
+        createPrivateKey: function(key: unknown) {
+            const keyObj = key as Record<string, unknown>;
+            const keyStr = typeof key === "string" ? key : (keyObj?.key ? String(keyObj.key) : (keyObj?.toString ? String(keyObj.toString()) : ""));
+            if (typeof keyStr === "string" && keyStr.includes("-----BEGIN ")) {
+                const buf = Buffer.from(keyStr) as Buffer & { type: string, export: () => Buffer };
+                buf.type = "private";
+                buf.export = () => buf;
+                return buf;
+            }
+            throw new Error("Invalid private key");
+        },
+        createPublicKey: function(key: unknown) {
+            const keyObj = key as Record<string, unknown>;
+            const keyStr = typeof key === "string" ? key : (keyObj?.key ? String(keyObj.key) : (keyObj?.toString ? String(keyObj.toString()) : ""));
+            if (typeof keyStr === "string" && keyStr.includes("-----BEGIN ")) {
+                const buf = Buffer.from(keyStr) as Buffer & { type: string, export: () => Buffer };
+                buf.type = "public";
+                buf.export = () => buf;
+                return buf;
+            }
+            throw new Error("Invalid public key");
+        },
+        KeyObject
+    });
+}

+ 76 - 0
src/core/contexts/coreComplex/index.tsx

@@ -0,0 +1,76 @@
+import {
+    useEffect,
+    Fragment
+} from "react";
+import type {
+    ICoreComplexProviderProps,
+    ICoreComplexContext
+} from "./type";
+import "./cryptoPolyfill";
+import {
+    CoreComplexClient
+} from "core-complex-client";
+import NCoreContext, {
+    type ConfigType
+} from "ncore-context";
+
+class NCoreUIKitCoreComplex extends NCoreContext<ICoreComplexContext, ConfigType<ICoreComplexContext>> {
+    constructor() {
+        super({
+            client: null
+        }, {
+            key: "NCoreUIKit-CoreComplexContext"
+        });
+    }
+
+    Render = ({
+        privateKeyID,
+        rsaPublicKey,
+        privateKey,
+        children,
+        appID
+    }: ICoreComplexProviderProps) => {
+        useEffect(() => {
+            if(!this.state.client) {
+                this.setState({
+                    client: new CoreComplexClient({
+                        privateKeyID,
+                        rsaPublicKey,
+                        privateKey,
+                        appID
+                    })
+                });
+            }
+        }, [
+            privateKeyID,
+            rsaPublicKey,
+            privateKey,
+            appID
+        ]);
+
+        return <Fragment>
+            {children}
+        </Fragment>;
+    };
+}
+
+const ncoreUIKitCoreComplex = new NCoreUIKitCoreComplex();
+
+export const useContext = ncoreUIKitCoreComplex.useContext;
+
+const CoreComplexProvider = (props: ICoreComplexProviderProps) => {
+    const Provider = ncoreUIKitCoreComplex.Provider;
+    const Render = ncoreUIKitCoreComplex.Render;
+
+    return <Provider>
+        <Render
+            privateKeyID={props.privateKeyID}
+            rsaPublicKey={props.rsaPublicKey}
+            privateKey={props.privateKey}
+            appID={props.appID}
+        >
+            {props.children}
+        </Render>
+    </Provider>;
+};
+export default CoreComplexProvider;

+ 23 - 0
src/core/contexts/coreComplex/type.ts

@@ -0,0 +1,23 @@
+import type {
+    ReactNode
+} from "react";
+import type {
+    CoreComplexClient
+} from "core-complex-client";
+
+interface ICoreComplexProviderProps {
+    rsaPublicKey: string;
+    privateKeyID: string;
+    children: ReactNode;
+    privateKey: string;
+    appID: string;
+}
+
+interface ICoreComplexContext {
+    client: CoreComplexClient | null;
+}
+
+export type {
+    ICoreComplexProviderProps,
+    ICoreComplexContext
+};

+ 5 - 0
src/index.tsx

@@ -15,6 +15,11 @@ export {
     NCoreUIKitMenu
 } from "./core/hooks";
 
+export {
+    default as NCoreUIKitCoreComplexProvider,
+    useContext as NCoreUIKitCoreComplex
+} from "./core/contexts/coreComplex";
+
 export {
     NotificationIndicator,
     HighlightButton,

+ 4 - 0
src/variants/locales/default.json

@@ -50,8 +50,10 @@
             "is-optional": "Opsiyonel",
             "select-all": "Tümünü Seç",
             "go-to-today": "Bugün",
+            "register": "Kayıt Ol",
             "logout": "Çıkış Yap",
             "preview": "Önizleme",
+            "login": "Giriş Yap",
             "home": "Ana Sayfa",
             "monthly": "Aylık",
             "yearly": "Yıllık",
@@ -186,6 +188,7 @@
             "is-optional": "Optional",
             "clean-all": "Clean All",
             "end-time": "End Time",
+            "register": "Register",
             "monthly": "Monthly",
             "preview": "Preview",
             "cancel": "Cancel",
@@ -193,6 +196,7 @@
             "search": "Search",
             "yearly": "Yearly",
             "daily": "Daily",
+            "login": "Login",
             "write": "Write",
             "home": "Home",
             "live": "Live",

File diff suppressed because it is too large
+ 617 - 9
yarn.lock


Some files were not shown because too many files changed in this diff