Skip to content

Commit

Permalink
Merge pull request #789 from remap-keys/operation-logs
Browse files Browse the repository at this point in the history
Send operation logs and display them for keyboard designers
  • Loading branch information
yoichiro authored Dec 19, 2023
2 parents 418a2b3 + 804cef2 commit 3ea3084
Show file tree
Hide file tree
Showing 23 changed files with 319 additions and 26 deletions.
9 changes: 8 additions & 1 deletion firestore.rules
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,13 @@ service cloud.firestore {
&& (request.auth.uid == resource.data.uid);
}

match /logs/v1/operations/{operationLogID} {
allow create;
allow update: if false;
allow delete: if false;
allow read: if false;
}

function isAuthenticated() {
return request.auth.uid != null;
}
Expand All @@ -149,4 +156,4 @@ service cloud.firestore {
return get(/databases/$(database)/documents/keyboards/v2/definitions/$(definitionID));
}
}
}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-draggable": "^4.4.3",
"react-google-charts": "^4.0.1",
"react-helmet-async": "^1.0.9",
"react-image-gallery": "^1.2.7",
"react-redux": "^7.2.2",
Expand Down
20 changes: 17 additions & 3 deletions src/actions/hid.action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { sendEventToGoogleAnalytics } from '../utils/GoogleAnalytics';
import { LayoutOption } from '../components/configure/keymap/Keymap';
import { maxValueByBitLength } from '../utils/NumberUtils';
import { KeyOp } from '../gen/types/KeyboardDefinition';
import { getEncoderIdList } from './utils';
import { getEncoderIdList, sendOperationLog } from './utils';
import { bmpKeyInfoList } from '../services/hid/KeycodeInfoListBmp';

const PRODUCT_PREFIX_FOR_BLE_MICRO_PRO = '(BMP)';
Expand Down Expand Up @@ -278,7 +278,7 @@ export const hidActionsThunk = {
dispatch: ThunkDispatch<RootState, undefined, ActionTypes>,
getState: () => RootState
) => {
const { app, entities } = getState();
const { app, entities, storage, auth } = getState();
const keyboard = entities.keyboard!;
const result = await keyboard.open();
if (!result.success) {
Expand All @@ -291,6 +291,13 @@ export const hidActionsThunk = {
product_id: keyboard.getInformation().productId,
product_name: keyboard.getInformation().productName,
});
await sendOperationLog(
auth.instance!,
storage.instance!,
app.localAuthenticationInfo.uid,
entities.keyboardDefinitionDocument!.id,
'configure/open'
);

const isBleMicroPro = keyboard
.getInformation()
Expand Down Expand Up @@ -487,13 +494,20 @@ export const hidActionsThunk = {
dispatch: ThunkDispatch<RootState, undefined, ActionTypes>,
getState: () => RootState
) => {
const { app, entities } = getState();
const { app, entities, storage, auth } = getState();
const keyboard: IKeyboard = entities.keyboard!;
sendEventToGoogleAnalytics('configure/flash', {
vendor_id: keyboard.getInformation().vendorId,
product_id: keyboard.getInformation().productId,
product_name: keyboard.getInformation().productName,
});
await sendOperationLog(
auth.instance!,
storage.instance!,
app.localAuthenticationInfo.uid,
entities.keyboardDefinitionDocument!.id,
'configure/flash'
);
const remaps = app.remaps;
for (let layer = 0; layer < remaps.length; layer++) {
const remap = remaps[layer];
Expand Down
8 changes: 8 additions & 0 deletions src/actions/keyboards.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
IBuildableFirmwareFileType,
IKeyboardDefinitionAuthorType,
IKeyboardDefinitionDocument,
IKeyboardStatistics,
IStore,
} from '../services/storage/Storage';
import { ThunkAction, ThunkDispatch } from 'redux-thunk';
Expand Down Expand Up @@ -196,6 +197,7 @@ export const KEYBOARDS_EDIT_DEFINITION_UPDATE_ORGANIZATION_ID = `${KEYBOARDS_EDI
export const KEYBOARDS_EDIT_DEFINITION_UPDATE_AUTHOR_TYPE = `${KEYBOARDS_EDIT_DEFINITION_ACTIONS}/UpdateAuthorType`;
export const KEYBOARDS_EDIT_DEFINITION_UPDATE_BUILDABLE_FIRMWARE_FILE = `${KEYBOARDS_EDIT_DEFINITION_ACTIONS}/UpdateBuildableFirmwareFile`;
export const KEYBOARDS_EDIT_DEFINITION_UPDATE_BUILDABLE_FIRMWARE_CODE_PARAMETERS = `${KEYBOARDS_EDIT_DEFINITION_ACTIONS}/UpdateBuildableFirmwareCodeParameters`;
export const KEYBOARDS_EDIT_DEFINITION_UPDATE_KEYBOARD_STATISTICS = `${KEYBOARDS_EDIT_DEFINITION_ACTIONS}/UpdateKeyboardStatistics`;

export const KeyboardsEditDefinitionActions = {
clear: () => {
Expand Down Expand Up @@ -447,6 +449,12 @@ export const KeyboardsEditDefinitionActions = {
value: parameters,
};
},
updateKeyboardStatistics: (statistics: IKeyboardStatistics | undefined) => {
return {
type: KEYBOARDS_EDIT_DEFINITION_UPDATE_KEYBOARD_STATISTICS,
value: statistics,
};
},
};

type ActionTypes = ReturnType<
Expand Down
21 changes: 19 additions & 2 deletions src/actions/storage.action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,23 @@ export const storageActionsThunk = {
);
return;
}
const fetchKeyboardStatisticsResult =
await storage.instance!.fetchKeyboardStatistics(definitionId);
if (isError(fetchKeyboardStatisticsResult)) {
console.error(fetchKeyboardStatisticsResult.cause);
dispatch(
NotificationActions.addError(
fetchKeyboardStatisticsResult.error,
fetchKeyboardStatisticsResult.cause
)
);
return;
}
dispatch(
KeyboardsEditDefinitionActions.updateKeyboardStatistics(
fetchKeyboardStatisticsResult.value
)
);
dispatch(
StorageActions.updateBuildableFirmware(
fetchBuildableFirmwareResult.value
Expand Down Expand Up @@ -601,7 +618,7 @@ export const storageActionsThunk = {
) => {
const { storage, auth, keyboards, github } = getState();
const keyboardDefinition = keyboards.createdefinition.keyboardDefinition!;
const user = auth.instance!.getCurrentAuthenticatedUser();
const user = auth.instance!.getCurrentAuthenticatedUserIgnoreNull();
const githubProviderDataResult = getGitHubProviderData(user);
if (!githubProviderDataResult.exists) {
console.error('The user does not have a GitHub Provider data.');
Expand Down Expand Up @@ -673,7 +690,7 @@ export const storageActionsThunk = {
const { storage, auth, keyboards, github } = getState();
const keyboardDefinition = keyboards.createdefinition.keyboardDefinition!;

const user = auth.instance!.getCurrentAuthenticatedUser();
const user = auth.instance!.getCurrentAuthenticatedUserIgnoreNull();
const githubProviderDataResutl = getGitHubProviderData(user);
if (!githubProviderDataResutl.exists) {
console.error('The user does not have a GitHub Provider data.');
Expand Down
19 changes: 19 additions & 0 deletions src/actions/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { KeyOp } from '../gen/types/KeyboardDefinition';
import KeyboardModel from '../models/KeyboardModel';
import { IAuth } from '../services/auth/Auth';
import { IOperationLogType, IStorage } from '../services/storage/Storage';

export const getEncoderIdList = (
keymapDefinition: ((string | KeyOp)[] | { name: string })[]
Expand All @@ -13,3 +15,20 @@ export const getEncoderIdList = (
return result;
}, []);
};

export const sendOperationLog = async (
auth: IAuth,
storage: IStorage,
localAuthenticationUid: string,
keyboardDefinitionId: string,
operation: IOperationLogType
): Promise<void> => {
let uid: string;
const user = auth.getCurrentAuthenticatedUserOrNull();
if (user === null) {
uid = `local:${localAuthenticationUid}`;
} else {
uid = `firebase:${user.uid}`;
}
await storage.sendOperationLog(uid, keyboardDefinitionId, operation);
};
6 changes: 3 additions & 3 deletions src/components/common/auth/ProfileIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export default class ProfileIcon extends React.Component<
}

renderLinkGoogleAccountMenu() {
const user = this.props.auth!.getCurrentAuthenticatedUser();
const user = this.props.auth!.getCurrentAuthenticatedUserIgnoreNull();
if (user && !getGoogleProviderData(user).exists) {
return (
<MenuItem
Expand All @@ -89,7 +89,7 @@ export default class ProfileIcon extends React.Component<
}

renderLinkGitHubAccountMenu() {
const user = this.props.auth!.getCurrentAuthenticatedUser();
const user = this.props.auth!.getCurrentAuthenticatedUserIgnoreNull();
if (user && !getGitHubProviderData(user).exists) {
return (
<MenuItem
Expand All @@ -107,7 +107,7 @@ export default class ProfileIcon extends React.Component<
render() {
const { authMenuAnchorEl } = this.state;
if (this.props.signedIn) {
const user = this.props.auth!.getCurrentAuthenticatedUser();
const user = this.props.auth!.getCurrentAuthenticatedUserIgnoreNull();
const profileImageUrl = user.photoURL || '';
const profileDisplayName = user.displayName || '';
let avatar: React.ReactNode;
Expand Down
2 changes: 1 addition & 1 deletion src/components/configure/info/InfoDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ export default class InfoDialog extends React.Component<
googleFormUrl={this.googleFormUrl}
authenticatedUser={
this.props.auth
? this.props.auth.getCurrentAuthenticatedUser()
? this.props.auth.getCurrentAuthenticatedUserIgnoreNull()
: undefined
}
organization={this.props.organization}
Expand Down
6 changes: 4 additions & 2 deletions src/components/configure/keymaplist/KeymapListPopover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ export default class KeymapListPopover extends React.Component<
labelLang
);

const uid = this.props.auth!.getCurrentAuthenticatedUser().uid;
const uid = this.props.auth!.getCurrentAuthenticatedUserIgnoreNull().uid;
if (uid !== savedKeymapData.author_uid) {
this.props.createOrUpdateAppliedKeymap!(savedKeymapData);
}
Expand Down Expand Up @@ -284,7 +284,9 @@ export default class KeymapListPopover extends React.Component<
<KeymapSaveDialog
open={this.state.openKeymapSaveDialog}
savedKeymapData={this.state.savedKeymapData}
authorUid={this.props.auth!.getCurrentAuthenticatedUser().uid}
authorUid={
this.props.auth!.getCurrentAuthenticatedUserIgnoreNull().uid
}
authorDisplayName={this.props.auth!.getCurrentAuthenticatedUserDisplayName()}
onClose={() => {
this.onCloseKeymapSaveDialog();
Expand Down
1 change: 1 addition & 0 deletions src/components/keyboards/content/Content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ function Contents(props: ContentsProps) {
case KeyboardsPhase.catalog:
case KeyboardsPhase.firmware:
case KeyboardsPhase.build:
case KeyboardsPhase.statistics:
return <EditKeyboard />;
default:
throw new Error(
Expand Down
9 changes: 8 additions & 1 deletion src/components/keyboards/editdefinition/EditDefinition.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import DefinitionForm from './definitionform/DefinitionForm';
import CatalogForm from './catalogform/CatalogForm.container';
import FirmwareForm from './firmwareform/FirmwareForm.container';
import BuildForm from './buildform/BuildForm.container';
import Statistics from './statistics/Statistics.container';

type ConfirmDialogMode =
| 'save_as_draft'
Expand Down Expand Up @@ -207,6 +208,8 @@ export default function EditDefinition(props: EditKeyboardProps) {
props.updatePhase!('firmware');
} else if (tabIndex === 3) {
props.updatePhase!('build');
} else if (tabIndex === 4) {
props.updatePhase!('statistics');
} else {
throw new Error(`Invalid tabIndex: ${tabIndex}`);
}
Expand Down Expand Up @@ -280,7 +283,9 @@ export default function EditDefinition(props: EditKeyboardProps) {
? 1
: props.phase === 'firmware'
? 2
: 3
: props.phase === 'build'
? 3
: 4
}
indicatorColor="primary"
textColor="primary"
Expand All @@ -292,6 +297,7 @@ export default function EditDefinition(props: EditKeyboardProps) {
<Tab label="Catalog" />
<Tab label="Firmware" />
<Tab label="Build" />
<Tab label="Statistics" />
</Tabs>
</div>
) : null}
Expand Down Expand Up @@ -361,6 +367,7 @@ export default function EditDefinition(props: EditKeyboardProps) {
{props.phase === 'catalog' ? <CatalogForm /> : null}
{props.phase === 'firmware' ? <FirmwareForm /> : null}
{props.phase === 'build' ? <BuildForm /> : null}
{props.phase === 'statistics' ? <Statistics /> : null}
</CardContent>
</Card>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { RootState } from '../../../../store/state';
import { connect } from 'react-redux';
import Statistics from './Statistics';

const mapStateToProps = (state: RootState) => {
return {
statistics: state.keyboards.editdefinition.keyboardStatistics,
};
};
export type StatisticsStateType = ReturnType<typeof mapStateToProps>;

// eslint-disable-next-line no-unused-vars
const mapDispatchToProps = (_dispatch: any) => {
return {};
};
export type StatisticsActionsType = ReturnType<typeof mapDispatchToProps>;

export default connect(mapStateToProps, mapDispatchToProps)(Statistics);
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@import 'src/variables';

.edit-definition-statistics {
&-container {
display: flex;
flex-direction: column;
width: 100%;
}
}
Loading

0 comments on commit 3ea3084

Please sign in to comment.