Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
node-version: '20.x'

- name: Cache node modules
uses: actions/cache@v2
uses: actions/cache@v4
with:
path: ~/.npm
key: cache-node-modules-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
Expand Down
1 change: 1 addition & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export const API_KEY_LOCAL_STORAGE = '__tolgee_apiKey';
export const API_URL_LOCAL_STORAGE = '__tolgee_apiUrl';
export const BRANCH_LOCAL_STORAGE = '__tolgee_branch';
12 changes: 11 additions & 1 deletion src/content/contentScript.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { API_KEY_LOCAL_STORAGE, API_URL_LOCAL_STORAGE } from '../constants';
import {
API_KEY_LOCAL_STORAGE,
API_URL_LOCAL_STORAGE,
BRANCH_LOCAL_STORAGE,
} from '../constants';
import { LibConfig } from '../types';
import { injectUiLib } from './injectUiLib';
import { Messages } from './Messages';
Expand All @@ -13,6 +17,7 @@ const getAppliedCredenials = () => {
return {
apiKey: sessionStorage.getItem(API_KEY_LOCAL_STORAGE),
apiUrl: sessionStorage.getItem(API_URL_LOCAL_STORAGE),
branch: sessionStorage.getItem(BRANCH_LOCAL_STORAGE),
};
};

Expand Down Expand Up @@ -72,6 +77,11 @@ messages.listenRuntime('SET_CREDENTIALS', async (data) => {
} else {
sessionStorage.removeItem(API_URL_LOCAL_STORAGE);
}
if (data.branch) {
sessionStorage.setItem(BRANCH_LOCAL_STORAGE, data.branch);
} else {
sessionStorage.removeItem(BRANCH_LOCAL_STORAGE);
}
location.reload();
updateState(configuration, messages);
});
75 changes: 73 additions & 2 deletions src/popup/TolgeeDetector.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import React, { useState } from 'react';
import {
Autocomplete,
Box,
Button,
CircularProgress,
Expand All @@ -26,7 +27,9 @@ export const TolgeeDetector = () => {
libConfig,
tolgeePresent,
credentialsCheck,
branches,
} = state;
const [branchOpen, setBranchOpen] = useState(false);

const handleApplyChange = async () => {
if (appliedValues) {
Expand Down Expand Up @@ -66,7 +69,8 @@ export const TolgeeDetector = () => {
const valuesNotChanged =
isInDevelopmentMode &&
libConfig?.config.apiKey === values?.apiKey &&
libConfig?.config.apiUrl === values?.apiUrl;
libConfig?.config.apiUrl === values?.apiUrl &&
(libConfig?.config.branch || '') === (values?.branch || '');

return (
<Box
Expand Down Expand Up @@ -126,6 +130,73 @@ export const TolgeeDetector = () => {
)}
</FormHelperText>
</FormControl>
{typeof credentialsCheck === 'object' &&
credentialsCheck?.branchingEnabled && (
<Autocomplete
style={{ marginBottom: branchOpen ? 150 : 0 }}
open={branchOpen}
onOpen={() => setBranchOpen(true)}
onClose={() => setBranchOpen(false)}
freeSolo
size="small"
disablePortal
slotProps={{
popper: {
placement: 'bottom',
modifiers: [{ name: 'flip', enabled: false }],
},
}}
ListboxProps={{ style: { maxHeight: 150 } }}
options={branches ?? []}
getOptionLabel={(option) =>
typeof option === 'string' ? option : option.name
}
value={
branches?.find((b) => b.name === values?.branch) ??
values?.branch ??
null
}
onChange={(_e: any, newValue: any) => {
dispatch({
type: 'CHANGE_VALUES',
payload: {
branch:
typeof newValue === 'string'
? newValue
: newValue?.name ?? '',
},
});
}}
onInputChange={(_e: any, newInput: string, reason: string) => {
if (reason === 'input') {
dispatch({
type: 'CHANGE_VALUES',
payload: { branch: newInput },
});
}
}}
renderOption={(props, option) => (
<li {...props}>
{option.name}
{option.isDefault && (
<span style={{ color: '#999', marginLeft: 6 }}>
default
</span>
)}
</li>
)}
renderInput={(params) => (
<TextField
{...params}
label="Branch"
variant="outlined"
placeholder={libConfig?.config?.branch || 'Default branch'}
helperText="Leave empty to use the branch from SDK config"
onKeyDown={handleKeyDown}
/>
)}
/>
)}
<Box
display="flex"
justifyContent="space-between"
Expand Down
3 changes: 3 additions & 0 deletions src/popup/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import browser from 'webextension-polyfill';
type Values = {
apiUrl?: string;
apiKey?: string;
branch?: string;
};

const getCurrentTab = async () => {
Expand All @@ -25,6 +26,7 @@ export const storeValues = async (values: Values | null) => {
[origin]: {
apiUrl: values.apiUrl,
apiKey: values.apiKey,
branch: values.branch,
},
});
} else {
Expand All @@ -45,6 +47,7 @@ export const loadValues = async () => {
return {
apiKey: data?.apiKey,
apiUrl: data?.apiUrl,
branch: data?.branch,
};
} catch (e) {
console.error(e);
Expand Down
5 changes: 4 additions & 1 deletion src/popup/tools.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export type Values = {
apiUrl?: string;
apiKey?: string;
branch?: string;
};

export const validateValues = (values?: Values | null) => {
Expand All @@ -15,7 +16,9 @@ export const compareValues = (
values2?: Values | null
) => {
return (
values1?.apiKey === values2?.apiKey && values2?.apiUrl === values2?.apiUrl
values1?.apiKey === values2?.apiKey &&
values1?.apiUrl === values2?.apiUrl &&
(values1?.branch || '') === (values2?.branch || '')
);
};

Expand Down
76 changes: 74 additions & 2 deletions src/popup/useDetectorForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,20 @@ import { RuntimeMessage } from '../content/Messages';

type ProjectInfo = {
projectName: string;
projectId: number;
scopes: string[];
userFullName: string;
branchingEnabled: boolean;
};

type CredentialsCheck = null | 'loading' | 'invalid' | ProjectInfo;
type TolgeePresent = 'loading' | 'present' | 'not_present' | 'legacy';

type BranchOption = {
name: string;
isDefault: boolean;
};

const initialState = {
values: null as Values | null,
storedValues: null as Values | null,
Expand All @@ -27,6 +34,7 @@ const initialState = {
libConfig: null as LibConfig | null,
error: null as string | null,
frameId: null as number | null,
branches: null as BranchOption[] | null,
};

type State = typeof initialState;
Expand All @@ -43,7 +51,8 @@ type Action =
| { type: 'APPLY_VALUES' }
| { type: 'CLEAR_ALL' }
| { type: 'STORE_VALUES' }
| { type: 'LOAD_VALUES' };
| { type: 'LOAD_VALUES' }
| { type: 'SET_BRANCHES'; payload: BranchOption[] | null };

export const useDetectorForm = () => {
const { applyRequired, apply } = useApplier();
Expand All @@ -57,6 +66,7 @@ export const useDetectorForm = () => {
const newValues = {
apiKey: libData?.config?.apiKey,
apiUrl: libData?.config?.apiUrl,
branch: libData?.config?.branch,
};
if (state.libConfig !== null && state.frameId !== frameId) {
return {
Expand Down Expand Up @@ -98,20 +108,29 @@ export const useDetectorForm = () => {
storedValues: action.payload,
values: action.payload,
};
case 'APPLY_VALUES':
case 'APPLY_VALUES': {
// sync values with storage/localStorage
apply();
const branchEnabled =
typeof state.credentialsCheck === 'object' &&
state.credentialsCheck?.branchingEnabled;
const effectiveBranch = branchEnabled
? state.values?.branch
: undefined;
return {
...state,
appliedValues: {
apiKey: state.values?.apiKey,
apiUrl: state.values?.apiUrl,
branch: effectiveBranch,
},
storedValues: {
apiKey: state.values?.apiKey,
apiUrl: state.values?.apiUrl,
branch: effectiveBranch,
},
};
}
case 'CLEAR_ALL': {
apply();
return {
Expand All @@ -137,6 +156,11 @@ export const useDetectorForm = () => {
appliedValues: state.storedValues,
values: state.storedValues,
};
case 'SET_BRANCHES':
return {
...state,
branches: action.payload,
};
default:
// @ts-expect-error action type is type uknown
throw new Error(`Unknown action ${action.type}`);
Expand Down Expand Up @@ -260,8 +284,10 @@ export const useDetectorForm = () => {
data &&
setCredentialsCheck({
projectName: data.projectName,
projectId: data.projectId,
scopes: data.scopes,
userFullName: data.userFullName,
branchingEnabled: data.branchingEnabled ?? false,
});
});
} else {
Expand All @@ -272,5 +298,51 @@ export const useDetectorForm = () => {
};
}, [checkableValues?.apiUrl, checkableValues?.apiKey]);

// fetch branches when credentials are valid and branching is enabled
useEffect(() => {
let cancelled = false;
const check = state.credentialsCheck;
if (
typeof check === 'object' &&
check?.branchingEnabled &&
validateValues(checkableValues)
) {
const url = normalizeUrl(checkableValues!.apiUrl);
fetch(
`${url}/v2/projects/${check.projectId}/branches?ak=${
checkableValues!.apiKey
}&size=100`
)
.then((r) => {
if (!r.ok) {
throw new Error('Failed to load branches');
}
return r.json();
})
.then((data) => {
if (!cancelled) {
dispatch({
type: 'SET_BRANCHES',
payload:
data?._embedded?.branches?.map((b: any) => ({
name: b.name,
isDefault: b.isDefault,
})) ?? null,
});
}
})
.catch(() => {
if (!cancelled) {
dispatch({ type: 'SET_BRANCHES', payload: null });
}
});
} else {
dispatch({ type: 'SET_BRANCHES', payload: null });
}
return () => {
cancelled = true;
};
}, [state.credentialsCheck]);

return [state, dispatch] as const;
};
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export type LibConfig = {
config: {
apiUrl: '';
apiKey: '';
branch?: string;
// @deprecated older versions
mode?: 'production' | 'development';
};
Expand Down
Loading