feat: improve login experience & add manual syncing

This commit is contained in:
thecodrr
2020-03-21 09:29:56 +05:00
parent 84cab85fb1
commit cde8dd1384
9 changed files with 83 additions and 17 deletions

View File

@@ -21,6 +21,14 @@ import { useStore as useUserStore } from "./stores/user-store";
import Animated from "./components/animated";
const NavMenuItem = props => {
const [isLoading, setIsLoading] = useState(false);
const isSyncing = useUserStore(store => store.isSyncing);
useEffect(() => {
if (props.item.animatable) {
if (props.item.key === "sync") setIsLoading(isSyncing);
}
}, [isSyncing, setIsLoading, props.item]);
return (
<Button
onClick={() => props.onSelected()}
@@ -48,6 +56,7 @@ const NavMenuItem = props => {
size={18}
sx={{ marginRight: "2px" }}
color={props.selected ? "primary" : props.item.color || "icon"}
rotate={isLoading}
/>
<Text
sx={{
@@ -189,7 +198,7 @@ function App() {
<NavMenuItem
onSelected={async () => {
if (item.onClick) {
item.onClick();
await item.onClick();
if (item.component) setSelectedKey(item.key);
return;
}

View File

@@ -2,6 +2,7 @@ import React from "react";
import ReactDOM from "react-dom";
import { Flex, Box, Text, Button as RebassButton } from "rebass";
import { ThemeProvider } from "../../utils/theme";
import * as Icon from "../icons";
import Modal from "react-modal";
export default class Dialog extends React.Component {
@@ -72,9 +73,16 @@ export default class Dialog extends React.Component {
mx={1}
width={"50%"}
disabled={props.positiveButton.disabled || false}
onClick={props.positiveButton.onClick}
onClick={
!props.positiveButton.disabled &&
props.positiveButton.onClick
}
>
{props.positiveButton.text || "OK"}
{props.positiveButton.loading ? (
<Icon.Loading rotate={true} color="static" />
) : (
props.positiveButton.text || "OK"
)}
</RebassButton>
)}

View File

@@ -4,13 +4,15 @@ import { Input } from "@rebass/forms";
import * as Icon from "../icons";
import Dialog, { showDialog } from "./dialog";
import { showSignUpDialog } from "./signupdialog";
import { useStore } from "../../stores/user-store";
import { db } from "../../common";
const LoginDialog = props => {
const [username, setUsername] = useState();
const [password, setPassword] = useState();
const [errorMessage, setErrorMessage] = useState();
const isLoggingIn = useStore(store => store.isLoggingIn);
const login = useStore(store => store.login);
return (
<Dialog
isOpen={true}
@@ -20,6 +22,8 @@ const LoginDialog = props => {
negativeButton={{ onClick: props.onClose }}
positiveButton={{
text: "Login",
loading: isLoggingIn,
disabled: isLoggingIn,
onClick: () => {
setErrorMessage();
if (username === "" || username === undefined) {
@@ -32,19 +36,19 @@ const LoginDialog = props => {
return;
}
db.user
.login(username, password)
login(username, password)
.then(() => {
props.onClose();
})
.catch(() => {
setErrorMessage("Failed to login. Please try again.");
.catch(e => {
setErrorMessage(e.message);
});
}
}}
content={
<Box my={1}>
<Input
autoFocus
variant="default"
placeholder="Username"
onChange={e => {

View File

@@ -5,13 +5,14 @@ import { motion } from "framer-motion";
import { useTheme } from "emotion-theming";
import Animated from "../animated";
const Icon = ({ name, size = 24, color = "text" }) => {
const Icon = ({ name, size = 24, color = "text", rotate }) => {
const theme = useTheme();
return (
<MDIIcon
path={name}
size={size + "px"}
color={theme.colors[color] || color}
spin={rotate}
/>
);
};
@@ -23,6 +24,7 @@ const createIcon = name => {
height={(props.size || 24) + "px"}
whileHover={{ scale: 1.1 }}
transition={{ duration: 0.3, ease: "easeOut" }}
animate={props.animation}
>
<Icon name={name} {...props} />
</Animated.Box>
@@ -54,6 +56,8 @@ export const NormalMode = createIcon(Icons.mdiFullscreenExit);
export const Settings = createIcon(Icons.mdiCogOutline);
export const Home = createIcon(Icons.mdiHomeOutline);
export const Restore = createIcon(Icons.mdiRecycle);
export const Sync = createIcon(Icons.mdiSync);
export const Loading = createIcon(Icons.mdiLoading);
/** Properties Icons */
export const ChevronLeft = createIcon(Icons.mdiChevronLeft);

View File

@@ -18,7 +18,7 @@ function read(key) {
}
function readMulti(keys) {
if (keys.length <= 0) return {};
if (keys.length <= 0) return [];
return localforage.getItems(sort(keys).asc());
}

View File

@@ -16,19 +16,22 @@ import Search from "../../views/Search";
import { store as noteStore } from "../../stores/note-store";
import { store as userStore } from "../../stores/user-store";
import { showLogInDialog } from "../../components/dialogs/logindialog";
import { db } from "../../common";
export const bottomRoutes = {
...createDeadRoute("nightmode", Icon.Theme, {
onClick: () => changeTheme(),
isToggled: () => isDarkTheme()
onClick: () => changeTheme()
}),
...createDeadRoute("sync", Icon.Sync, {
onClick: async () => userStore.getState().sync(),
animatable: true
}),
...createNormalRoute("account", Account, Icon.User, {
onClick: async () => {
if (userStore.getState().isLoggedIn) {
RootNavigator.navigate("account");
} else {
if (!userStore.getState().isLoggedIn) {
await showLogInDialog();
}
RootNavigator.navigate("account");
}
}),
...createRoute("settings", SettingsContainer, {

View File

@@ -1,6 +1,8 @@
import createStore from "../common/store";
import { db } from "../common";
import { showPasswordDialog } from "../components/dialogs/passworddialog";
import { store as noteStore } from "./note-store";
import { store as notebookStore } from "./notebook-store";
function appStore(set, get) {
return {
@@ -12,6 +14,11 @@ function appStore(set, get) {
selectedItems: [],
theme: {},
colors: [],
refreshApp: function() {
noteStore.getState().refresh();
notebookStore.getState().refresh();
noteStore.getState().refreshSelectedContext();
},
closeSideMenu: function() {
set(state => {
state.isSideMenuOpen = false;

View File

@@ -25,6 +25,11 @@ function noteStore(set, get) {
state.notes = db.notes.group(undefined, true);
});
},
refreshSelectedContext: function() {
const { setSelectedContext, selectedContext } = get();
if (!selectedContext.type) return;
setSelectedContext(selectedContext);
},
setSelectedContext: function(context) {
let notes = [];
switch (context.type) {

View File

@@ -1,9 +1,12 @@
import createStore from "../common/store";
import { db } from "../common";
import { store as appStore } from "./app-store";
function userStore(set, get) {
return {
isLoggedIn: false,
isLoggingIn: false,
isSyncing: false,
user: undefined,
init: () => {
return db.user.get().then(user => {
@@ -12,17 +15,40 @@ function userStore(set, get) {
state.user = user;
state.isLoggedIn = true;
});
get().sync();
return true;
});
},
login: (username, password) => {
set(state => {
state.isLoggingIn = true;
});
return db.user
.login(username, password)
.then(() => {
return get().init();
})
.catch(() => {
return false;
.finally(() => {
set(state => {
state.isLoggingIn = false;
});
});
},
sync: () => {
set(state => {
state.isSyncing = true;
});
db.sync()
.then(() => {
appStore.getState().refreshApp();
})
.catch(err => {
console.error(err);
})
.finally(() => {
set(state => {
state.isSyncing = false;
});
});
},
logout: () => {