@rpcbase/client 0.175.0 → 0.176.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,6 +2,7 @@
2
2
  import assert from "assert"
3
3
  import debug from "debug"
4
4
  import {useState, useEffect} from "react"
5
+ import {useTranslation} from "react-i18next"
5
6
  import useSearchParam from "react-use/lib/useSearchParam"
6
7
  import isEmail from "validator/lib/isEmail"
7
8
 
@@ -14,6 +15,8 @@ import {setIsSignedIn, setUid, setTenantId} from "../../index"
14
15
  const log = debug("rb:auth:signin")
15
16
 
16
17
  export const SignInEmailForm = ({onSuccess, onSuccessRedirect}) => {
18
+ const {t} = useTranslation("rb.sign_in", {useSuspense: false})
19
+
17
20
  const redirect = useSearchParam("redirect")
18
21
 
19
22
  const [isLoading, setIsLoading] = useState(false)
@@ -86,27 +89,27 @@ export const SignInEmailForm = ({onSuccess, onSuccessRedirect}) => {
86
89
  <input type="email"
87
90
  className="form-control"
88
91
  id="input-email"
89
- placeholder="name@example.com"
92
+ placeholder={t("email_placeholder")}
90
93
  value={email}
91
94
  onChange={onChangeEmail} />
92
- <label htmlFor="input-email">Email Address</label>
95
+ <label htmlFor="input-email">{t("email_address")}</label>
93
96
  </div>
94
97
 
95
98
  <div className="form-floating text-start">
96
99
  <input type="password"
97
100
  className="form-control"
98
101
  id="input-password"
99
- placeholder="Password"
102
+ placeholder={t("password")}
100
103
  value={password}
101
104
  onChange={onChangePassword} />
102
- <label htmlFor="input-password">Password</label>
105
+ <label htmlFor="input-password">{t("password")}</label>
103
106
  </div>
104
107
 
105
108
  <div className="mt-2 text-start w-100">
106
- <a href={`/forgot-password${forgotUrlParam && "#" + forgotUrlParam}`}>Forgot your password?</a>
109
+ <a href={`/forgot-password${forgotUrlParam && "#" + forgotUrlParam}`}>{t("forgot_password")}</a>
107
110
  </div>
108
111
 
109
- <button className="mt-4 mb-3 w-100 btn btn-lg btn-primary" type="submit" onClick={onSubmit}>Sign In</button>
112
+ <button className="mt-4 mb-3 w-100 btn btn-lg btn-primary" type="submit" onClick={onSubmit}>{t("submit_btn")}</button>
110
113
 
111
114
  </form>
112
115
  )
@@ -1,6 +1,6 @@
1
1
  /* @flow */
2
2
  import assert from "assert"
3
-
3
+ import {useTranslation} from "react-i18next"
4
4
  import authConfig from "@rpcbase/dot-rb/auth"
5
5
 
6
6
  import {AUTH_BUTTONS} from "../../../src/ui/oauth"
@@ -20,6 +20,7 @@ export const SignIn = ({
20
20
  onSuccessRedirect = "/",
21
21
  onSuccess = () => null
22
22
  }) => {
23
+ const {t} = useTranslation("rb.sign_in", {useSuspense: false})
23
24
 
24
25
  return (
25
26
  <div id="sign-in-wrapper">
@@ -31,10 +32,10 @@ export const SignIn = ({
31
32
 
32
33
  <hr />
33
34
 
34
- <h1 className="h4 mt-3 mb-3 fw-normal">Sign In</h1>
35
+ <h1 className="h4 mt-3 mb-3 fw-normal">{t("title")}</h1>
35
36
  <p className="text-start text-muted">
36
- Don't have an account?
37
- <a href="/signup" className="ms-1">Sign up here</a>
37
+ {t("dont_have_account")}
38
+ <a href="/signup" className="ms-1">{t("sign_up_here")}</a>
38
39
  </p>
39
40
 
40
41
  {hasEmail && (
@@ -47,7 +48,7 @@ export const SignIn = ({
47
48
  {hasEmail && hasOAuth && (
48
49
  <>
49
50
  <hr />
50
- <p className="text-muted">OR</p>
51
+ <p className="text-muted">{t("or")}</p>
51
52
  </>
52
53
  )}
53
54
 
@@ -55,7 +56,7 @@ export const SignIn = ({
55
56
  const Comp = AUTH_BUTTONS[provider.id]
56
57
  assert(Comp, `unable to find oauth button for provider: ${JSON.stringify(provider)}`)
57
58
 
58
- return <Comp key={`${provider.id}-${index}`} text="Sign in" className="w-100 justify-content-center" />
59
+ return <Comp key={`${provider.id}-${index}`} text={t("submit_btn")} className="w-100 justify-content-center" />
59
60
  })}
60
61
 
61
62
 
@@ -2,6 +2,7 @@
2
2
  import assert from "assert"
3
3
  import {useState, useEffect} from "react"
4
4
  import Alert from "react-bootstrap/Alert"
5
+ import {useTranslation} from "react-i18next"
5
6
 
6
7
  import authConfig from "@rpcbase/dot-rb/auth"
7
8
 
@@ -23,6 +24,7 @@ export const SignOut = ({
23
24
  name,
24
25
  onSignOutSuccess = () => null
25
26
  }) => {
27
+ const {t} = useTranslation("rb.sign_out", {useSuspense: false})
26
28
 
27
29
  const [isSignedOut, setIsSignedOut] = useState(false)
28
30
 
@@ -55,19 +57,17 @@ export const SignOut = ({
55
57
  }
56
58
  }
57
59
 
58
-
59
60
  const onSubmit = () => {
60
- console.log("sign out from selected accounts", selectedAccounts)
61
61
  onSignOut()
62
62
  }
63
63
 
64
- const message = hasMultiAccounts ? "Select accounts to sign out of:" : "Click here to sign out of your account:"
64
+ const message = hasMultiAccounts ? "Select accounts to sign out of:" : t("click_here_signout")
65
65
 
66
66
  return (
67
67
  <div id="sign-out-wrapper">
68
68
 
69
69
  {isSignedOut && (
70
- <Alert variant="light" data-bs-theme="dark" style={{maxWidth: 260, position: "absolute"}} onClose={() => setIsSignedOut(false)} dismissible>
70
+ <Alert variant="light" transition data-bs-theme="dark" style={{maxWidth: 260, position: "absolute"}} onClose={() => setIsSignedOut(false)} dismissible>
71
71
  Signed out successfully
72
72
  </Alert>
73
73
  )}
@@ -80,7 +80,7 @@ export const SignOut = ({
80
80
 
81
81
  <hr />
82
82
 
83
- <h1 className="h4 mt-3 mb-3 fw-normal">Sign Out</h1>
83
+ <h1 className="h4 mt-3 mb-3 fw-normal">{t("title")}</h1>
84
84
 
85
85
  <p className="text-start text-muted">
86
86
  {message}
package/auth/index.js CHANGED
@@ -17,11 +17,17 @@ const log = debug("rb:auth")
17
17
 
18
18
  // TODO: this should be refactored to AuthContext + Provider
19
19
 
20
- let __isAuthenticated = null
20
+ let __isSignedIn = null
21
21
 
22
22
  let __tenantId = typeof storage !== "undefined" && storage.getItem(LAST_TENANT_KEY)
23
23
  let __userId = typeof storage !== "undefined" && __tenantId && storage.getItem(uidStorageKey(__tenantId))
24
24
 
25
+ let __pendingSignedInStatusCallbacks = []
26
+
27
+ const purgePendingSignedInStatusCallbacks = () => {
28
+ __pendingSignedInStatusCallbacks.forEach((fn) => fn(__isSignedIn))
29
+ __pendingSignedInStatusCallbacks = []
30
+ }
25
31
 
26
32
  const getQueryStringAuthId = () => {
27
33
  const url = new URL(window.location.href)
@@ -51,11 +57,11 @@ const runSessionCheck = async() => {
51
57
 
52
58
  log("check_session response", res)
53
59
 
54
- __isAuthenticated = res.is_signed_in
55
-
60
+ __isSignedIn = res.is_signed_in
61
+ purgePendingSignedInStatusCallbacks() // IMPORTANT: call and purge all pending callbacks
56
62
 
57
63
  // if not authenticated, clear __userId + __tenantId and disconnect socket
58
- if (!__isAuthenticated) {
64
+ if (!__isSignedIn) {
59
65
  __userId = null
60
66
  __tenantId = null
61
67
  rtsDisconnect()
@@ -83,14 +89,14 @@ if (typeof window !== "undefined" && !isJest) runSessionCheck()
83
89
 
84
90
 
85
91
  export const getSessionRestrictMiddleware = () => (ctx, next) => {
86
- log("session_restrict:__isAuthenticated", __isAuthenticated)
92
+ log("session_restrict:__isSignedIn", __isSignedIn)
87
93
 
88
- if (typeof __isAuthenticated !== "boolean") {
94
+ if (typeof __isSignedIn !== "boolean") {
89
95
  log("session_restrict: will run session check")
90
96
 
91
97
  runSessionCheck()
92
98
  .then(() => {
93
- if (__isAuthenticated) {
99
+ if (__isSignedIn) {
94
100
  next()
95
101
  } else {
96
102
  redirectSignIn(ctx)
@@ -100,13 +106,12 @@ export const getSessionRestrictMiddleware = () => (ctx, next) => {
100
106
  console.log("warning error in check session request", err)
101
107
  throw err
102
108
  })
103
- } else if (__isAuthenticated) {
109
+ } else if (__isSignedIn) {
104
110
  next()
105
111
  } else {
106
- log("AUTH", JSON.stringify(__isAuthenticated))
107
- log("NOT AUTHENTICATED, REDIRECT HERE")
112
+ log("AUTH", JSON.stringify(__isSignedIn))
113
+ log("NOT SIGNED IN, REDIRECT HERE")
108
114
  redirectSignIn(ctx)
109
- // next()
110
115
  }
111
116
  }
112
117
 
@@ -127,6 +132,18 @@ export const setTenantId = (val) => {
127
132
  storage.setItem(LAST_TENANT_KEY, val)
128
133
  }
129
134
 
130
- export const setIsSignedIn = (val) => __isAuthenticated = val
135
+ export const setIsSignedIn = (val) => {
136
+ __isSignedIn = val
137
+ purgePendingSignedInStatusCallbacks() // IMPORTANT: call and purge all pending callbacks
138
+ }
139
+
140
+ export const getIsSignedIn = () => __isSignedIn
141
+
131
142
 
132
- export const getIsSignedIn = () => __isAuthenticated
143
+ export const waitForSignedInStatus = () => new Promise((resolve) => {
144
+ if (typeof __isSignedIn === "boolean") {
145
+ resolve(__isSignedIn)
146
+ } else {
147
+ __pendingSignedInStatusCallbacks.push(resolve)
148
+ }
149
+ })
@@ -0,0 +1,11 @@
1
+ {
2
+ "title": "Sign In",
3
+ "dont_have_account": "Don't have an account?",
4
+ "sign_up_here": "Sign up here",
5
+ "or": "OR",
6
+ "email_address": "Email Address",
7
+ "email_placeholder": "name@example.com",
8
+ "password": "Password",
9
+ "forgot_password": "Forgot your password?",
10
+ "submit_btn": "Sign in"
11
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "title": "Sign Out",
3
+ "click_here_signout": "Click here to sign out of your account:",
4
+ "success_text": "Signed out succesfully"
5
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "title": "Connexion",
3
+ "dont_have_account": "Vous n'avez pas de compte?",
4
+ "sign_up_here": "Créez un compte ici",
5
+ "or": "OU",
6
+ "email_address": "Addresse Email",
7
+ "email_placeholder": "nom@example.com",
8
+ "password": "Mot de passe",
9
+ "forgot_password": "Mot de passe oublié?",
10
+ "submit_btn": "Connexion"
11
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "title": "Déconnexion",
3
+ "click_here_signout": "Cliquez ici pour vous déconnecter:",
4
+ "success_text": "Opération complétée avec succès"
5
+ }
@@ -1,5 +1,5 @@
1
1
  /* @flow */
2
- import i18n from "i18next"
2
+ import i18next from "i18next"
3
3
  import {initReactI18next} from "react-i18next"
4
4
  import ChainedBackend from "i18next-chained-backend"
5
5
  import resourcesToBackend from "i18next-resources-to-backend"
@@ -22,20 +22,33 @@ if (!locale) {
22
22
  }
23
23
 
24
24
  // Do not call this code directly, it is injected from babel-plugin-i18next-client by the bundler
25
- export default (backendFn) => {
26
-
27
- i18n
25
+ export const registerI18N = async (backendFn) => {
26
+ i18next
28
27
  .use(ChainedBackend)
29
28
  .use(initReactI18next)
30
29
  .init({
31
30
  lng: locale,
32
- fallbackLng: (code) => getLocaleFromCode(code, DEFAULT_LOCALE, SUPPORTED_LOCALES),
31
+ fallbackLng: (code) => {
32
+ const val = getLocaleFromCode({ code, supported_locales: SUPPORTED_LOCALES, default_locale: DEFAULT_LOCALE })
33
+ // console.log("GOT VAL", {code, val})
34
+ return val
35
+ },
33
36
  defaultNS: "common",
34
37
  backend: {
35
- backends: [resourcesToBackend(backendFn)],
38
+ backends: [
39
+ // rb internal i18n
40
+ resourcesToBackend((lng, ns) => import(/* webpackChunkName: "rb-i18n" */ `./${lng}/${ns}.json`)),
41
+ // users's i18n
42
+ resourcesToBackend(backendFn)
43
+ ],
36
44
  },
37
45
  interpolation: {
38
46
  escapeValue: false,
39
47
  },
40
48
  })
49
+
50
+ // save preference to local storage so it works on reload
51
+ i18next.on('languageChanged', (lng) => {
52
+ localStorage.setItem(LOCALE_KEY, lng)
53
+ })
41
54
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rpcbase/client",
3
- "version": "0.175.0",
3
+ "version": "0.176.0",
4
4
  "scripts": {
5
5
  "test": "../../node_modules/.bin/wireit"
6
6
  },
@@ -84,7 +84,7 @@
84
84
  "@rpcbase/redbox-react": "0.3.0",
85
85
  "figma-squircle": "0.3.1",
86
86
  "firebase": "10.12.3",
87
- "i18next": "23.11.5",
87
+ "i18next": "23.12.2",
88
88
  "i18next-chained-backend": "4.6.2",
89
89
  "i18next-resources-to-backend": "1.2.1",
90
90
  "lz-string": "1.5.0",
@@ -92,7 +92,7 @@
92
92
  "pouchdb-adapter-indexeddb": "8.0.1",
93
93
  "pouchdb-core": "8.0.1",
94
94
  "pouchdb-find": "8.0.1",
95
- "react-i18next": "14.1.2",
95
+ "react-i18next": "15.0.0",
96
96
  "socket.io-client": "4.7.5"
97
97
  },
98
98
  "devDependencies": {