Skip to content

Commit

Permalink
Merge pull request #423 from gertrude-app/support-app-blocking-poc
Browse files Browse the repository at this point in the history
dash: support app blocking canary testing
  • Loading branch information
jaredh159 authored Dec 18, 2024
2 parents 4d786f9 + 2947a43 commit c3786ec
Show file tree
Hide file tree
Showing 18 changed files with 155 additions and 48 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ jobs:
start: pnpm --filter @dash/app preview
project: dash/app
- name: upload-videos
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
if: failure()
with:
name: cypress-videos
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
- name: run smoke tests
run: just smoke-run
- name: upload-videos
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
if: failure()
with:
name: smoke-videos
Expand Down
2 changes: 1 addition & 1 deletion dash/app/cypress/e2e/create-key.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ describe(`create key flow`, () => {
id: `app-123`,
name: `Brave`,
slug: `brave`,
selectable: true,
launchable: true,
bundleIds: [],
},
]);
Expand Down
8 changes: 8 additions & 0 deletions dash/app/src/components/routes/User.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const UserRoute: React.FC = () => {
showSuspensionActivity: user.draft.showSuspensionActivity,
downtime: user.draft.downtime,
keychains: user.draft.keychains.map(({ id, schedule }) => ({ id, schedule })),
blockedApps: user.draft.blockedApps,
}),
{
onSuccess: () => dispatch({ type: `userSaved` }),
Expand Down Expand Up @@ -147,6 +148,13 @@ const UserRoute: React.FC = () => {
setAssignedKeychainSchedule={(id, schedule) =>
dispatch({ type: `setKeychainSchedule`, id, schedule })
}
blockedApps={draft.blockedApps}
newBlockedAppIdentifier={state.newBlockedAppIdentifier ?? ``}
updateNewBlockedAppIdentifier={(identifier) =>
dispatch({ type: `updateNewBlockedAppIdentifier`, identifier })
}
addNewBlockedApp={() => dispatch({ type: `addNewBlockedApp` })}
removeBlockedApp={(id) => dispatch({ type: `removeBlockedApp`, id })}
/>
);
};
Expand Down
2 changes: 1 addition & 1 deletion dash/app/src/reducers/__tests__/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export function identifiedApp(
id: uuid(),
name: `Brave Browser`,
slug: `brave`,
selectable: true,
launchable: true,
bundleIds: [{ id: uuid(), bundleId: `com.brave.Browser` }],
...override,
};
Expand Down
28 changes: 25 additions & 3 deletions dash/app/src/reducers/user-reducer.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import { v4 as uuid } from 'uuid';
import { produce } from 'immer';
import { defaults } from '@dash/types';
import type {
UserKeychainSummary,
PlainTimeWindow,
User,
KeychainSchedule,
RuleSchedule,
} from '@dash/types';
import { commit, editable } from '../lib/helpers';

type State = {
user?: Editable<User>;
addingKeychain?: UserKeychainSummary | null;
newBlockedAppIdentifier?: string;
};

export type Action =
Expand All @@ -25,9 +27,12 @@ export type Action =
| { type: 'setDowntime'; downtime: PlainTimeWindow }
| { type: 'setShowSuspensionActivity'; show: boolean }
| { type: 'removeKeychain'; id: UUID }
| { type: 'setKeychainSchedule'; id: UUID; schedule?: KeychainSchedule }
| { type: 'updateNewBlockedAppIdentifier'; identifier: string }
| { type: 'removeBlockedApp'; id: UUID }
| { type: 'addNewBlockedApp' }
| { type: 'setKeychainSchedule'; id: UUID; schedule?: RuleSchedule }
| { type: 'addKeychain'; keychain: UserKeychainSummary }
| { type: 'setAddingKeychainSchedule'; schedule?: KeychainSchedule }
| { type: 'setAddingKeychainSchedule'; schedule?: RuleSchedule }
| { type: 'setAddingKeychain'; keychain?: UserKeychainSummary | null };

function reducer(state: State, action: Action): State | undefined {
Expand Down Expand Up @@ -63,6 +68,23 @@ function reducer(state: State, action: Action): State | undefined {
case `setShowSuspensionActivity`:
state.user.draft.showSuspensionActivity = action.show;
return;
case `updateNewBlockedAppIdentifier`:
state.newBlockedAppIdentifier = action.identifier;
return;
case `addNewBlockedApp`:
if (state.newBlockedAppIdentifier) {
state.user.draft.blockedApps = [
...(state.user.draft.blockedApps ?? []),
{ id: uuid(), identifier: state.newBlockedAppIdentifier },
];
state.newBlockedAppIdentifier = ``;
}
return;
case `removeBlockedApp`:
state.user.draft.blockedApps = state.user.draft.blockedApps?.filter(
(app) => app.id !== action.id,
);
return;
case `removeKeychain`:
state.user.draft.keychains = state.user.draft.keychains.filter(
(keychain) => keychain.id !== action.id,
Expand Down
4 changes: 2 additions & 2 deletions dash/components/src/Keychains/KeychainCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { inflect } from '@shared/string';
import { Button, Badge } from '@shared/components';
import { ChevronDownIcon, ClockIcon, TrashIcon } from '@heroicons/react/24/outline';
import { UsersIcon } from '@heroicons/react/24/solid';
import { defaults, type KeychainSchedule as Schedule } from '@dash/types';
import { defaults, type RuleSchedule as Schedule } from '@dash/types';
import GradientIcon from '../GradientIcon';
import KeychainSchedule from './schedule/KeychainSchedule';

Expand Down Expand Up @@ -118,7 +118,7 @@ const KeychainCard: React.FC<Props> = ({
{props.mode === `assign_to_child` && !props.schedule && (
<button
onClick={() => {
props.setSchedule(defaults.keychainSchedule());
props.setSchedule(defaults.ruleSchedule());
setShowSchedule(true);
}}
className={cx(
Expand Down
6 changes: 3 additions & 3 deletions dash/components/src/Keychains/schedule/KeychainSchedule.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import React from 'react';
import cx from 'classnames';
import type { KeychainSchedule } from '@dash/types';
import type { RuleSchedule } from '@dash/types';
import SpecifyingActiveOrInactive from './SpecifyingActiveOrInactive';
import WhatDays from './WhatDays';
import WhatTime from './WhatTime';

interface Props {
schedule: KeychainSchedule;
setSchedule(schedule: KeychainSchedule): void;
schedule: RuleSchedule;
setSchedule(schedule: RuleSchedule): void;
}

const KeychainSchedule: React.FC<Props> = ({ schedule, setSchedule }) => (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React from 'react';
import cx from 'classnames';
import { ArrowsRightLeftIcon } from '@heroicons/react/24/outline';
import type { KeychainSchedule } from '@dash/types';
import type { RuleSchedule } from '@dash/types';

const SpecifyingActiveOrInactive: React.FC<{
schedule: KeychainSchedule;
setSchedule(schedule: KeychainSchedule): void;
schedule: RuleSchedule;
setSchedule(schedule: RuleSchedule): void;
isTouchDevice: boolean;
}> = ({ schedule, setSchedule, isTouchDevice }) => (
<button
Expand Down
6 changes: 3 additions & 3 deletions dash/components/src/Keychains/schedule/WhatDays.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import React from 'react';
import cx from 'classnames';
import { typesafe } from '@shared/ts-utils';
import { CheckIcon } from '@heroicons/react/24/outline';
import type { KeychainSchedule } from '@dash/types';
import type { RuleSchedule } from '@dash/types';
import DropdownCustomizationPoint from './DropdownCustomizationPoint';

const WhatDays: React.FC<{
schedule: KeychainSchedule;
setSchedule(schedule: KeychainSchedule): void;
schedule: RuleSchedule;
setSchedule(schedule: RuleSchedule): void;
isTouchDevice: boolean;
}> = ({ schedule, setSchedule, isTouchDevice }) => (
<DropdownCustomizationPoint
Expand Down
6 changes: 3 additions & 3 deletions dash/components/src/Keychains/schedule/WhatTime.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React from 'react';
import cx from 'classnames';
import type { KeychainSchedule } from '@dash/types';
import type { RuleSchedule } from '@dash/types';
import TimeInput from '../../Forms/TimeInput';
import DropdownCustomizationPoint from './DropdownCustomizationPoint';

const WhatTime: React.FC<{
schedule: KeychainSchedule;
setSchedule(schedule: KeychainSchedule): void;
schedule: RuleSchedule;
setSchedule(schedule: RuleSchedule): void;
isTouchDevice: boolean;
}> = ({ schedule, setSchedule, isTouchDevice }) => {
const isInvalid = (() => {
Expand Down
4 changes: 2 additions & 2 deletions dash/components/src/Users/AddKeychainDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
import { Badge, Button, Loading } from '@shared/components';
import { inflect } from '@shared/string';
import { defaults, type KeychainSummary as Keychain } from '@dash/types';
import type { KeychainSchedule as Schedule, RequestState } from '@dash/types';
import type { RuleSchedule as Schedule, RequestState } from '@dash/types';
import KeychainSchedule from '../Keychains/schedule/KeychainSchedule';

interface Props {
Expand Down Expand Up @@ -318,7 +318,7 @@ const AddKeychainDrawer: React.FC<Props> = ({
</>
) : (
<button
onClick={() => selected && setSchedule(defaults.keychainSchedule())}
onClick={() => selected && setSchedule(defaults.ruleSchedule())}
className={cx(
`flex items-center px-2 py-1 rounded-full transition-[background-color,transform] duration-200 active:scale-90 gap-1.5 bg-slate-200/50 hover:bg-slate-200 active:bg-slate-300`,
)}
Expand Down
75 changes: 70 additions & 5 deletions dash/components/src/Users/EditUser.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React from 'react';
import cx from 'classnames';
import { inflect } from '@shared/string';
import { TextInput, Button, Toggle, Label } from '@shared/components';
import type { KeychainSchedule, PlainTimeWindow } from '@dash/types';
import { TextInput, Button, Badge, Toggle, Label } from '@shared/components';
import type { RuleSchedule, PlainTimeWindow, BlockedApp } from '@dash/types';
import type { Subcomponents, ConfirmableEntityAction, RequestState } from '@dash/types';
import type { UserKeychainSummary as Keychain } from '@dash/types';
import KeychainCard from '../Keychains/KeychainCard';
Expand Down Expand Up @@ -49,9 +49,14 @@ interface Props {
onDismissAddKeychain(): unknown;
addingKeychain?: Keychain | null;
fetchSelectableKeychainsRequest?: RequestState<{ own: Keychain[]; public: Keychain[] }>;
keychainSchedule?: KeychainSchedule;
setAddingKeychainSchedule(schedule?: KeychainSchedule): unknown;
setAssignedKeychainSchedule(id: UUID, schedule?: KeychainSchedule): unknown;
keychainSchedule?: RuleSchedule;
setAddingKeychainSchedule(schedule?: RuleSchedule): unknown;
setAssignedKeychainSchedule(id: UUID, schedule?: RuleSchedule): unknown;
blockedApps?: BlockedApp[];
newBlockedAppIdentifier: string;
updateNewBlockedAppIdentifier(identifier: string): unknown;
addNewBlockedApp(): unknown;
removeBlockedApp(id: UUID): unknown;
}

const EditUser: React.FC<Props> = ({
Expand Down Expand Up @@ -92,6 +97,11 @@ const EditUser: React.FC<Props> = ({
keychainSchedule,
setAddingKeychainSchedule,
setAssignedKeychainSchedule,
blockedApps,
newBlockedAppIdentifier,
updateNewBlockedAppIdentifier,
addNewBlockedApp,
removeBlockedApp,
}) => {
if (isNew) {
return (
Expand Down Expand Up @@ -317,6 +327,61 @@ const EditUser: React.FC<Props> = ({
</div>
</div>
</div>
{/* /downtime */}

{/* blocked apps */}
{blockedApps && (
<div className="mt-12 max-w-3xl mb-12">
<h2 className="text-lg font-bold text-slate-700 flex">
Blocked Apps{` `}
<Badge className="ml-2" size="small" type="green">
Beta
</Badge>
</h2>
{blockedApps.length === 0 ? (
<p className="text-center italic hidden text-slate-500 text-sm antialiased mt-2 mb-4">
No apps are currently blocked
</p>
) : (
<div className="gap-1.5 my-2 flex flex-col">
{blockedApps.map((app) => (
<div
className="text-slate-600 flex items-center font-bold px-3 py-2.5 border border-slate-200 bg-white rounded-lg"
key={app.id}
>
<i className="fa text-red-500 fa-ban mr-2 p-1.5 bg-red-100/80 rounded-md" />
<span className="grow">{app.identifier}</span>
<i
className="fa fa-trash text-slate-500 ml-2 mr-1 cursor-pointer hover:text-red-700"
onClick={() => removeBlockedApp(app.id)}
/>
</div>
))}
</div>
)}
<div className="flex gap-2 mt-4">
<TextInput
key={`new-blocked-app-${blockedApps.length}`}
type="text"
value={newBlockedAppIdentifier}
setValue={updateNewBlockedAppIdentifier}
placeholder="App name or bundle id"
/>
<Button
size="small"
className="whitespace-nowrap"
color="secondary"
type="button"
disabled={!newBlockedAppIdentifier}
onClick={addNewBlockedApp}
>
<i className="fa fa-plus mr-2" />
Add new
</Button>
</div>
</div>
)}
{/* /blocked apps */}

{/* keychains */}
<div className="mt-12 max-w-3xl">
Expand Down
4 changes: 2 additions & 2 deletions dash/types/src/defaults.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { PlainTimeWindow, KeychainSchedule } from './pairql/shared';
import type { PlainTimeWindow, RuleSchedule } from './pairql/shared';

export function timeWindow(): PlainTimeWindow {
return {
Expand All @@ -7,7 +7,7 @@ export function timeWindow(): PlainTimeWindow {
};
}

export function keychainSchedule(): KeychainSchedule {
export function ruleSchedule(): RuleSchedule {
return {
mode: `active`,
days: {
Expand Down
2 changes: 1 addition & 1 deletion dash/types/src/pairql/pairs/GetIdentifiedApps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export namespace GetIdentifiedApps {
id: UUID;
name: string;
slug: string;
selectable: boolean;
launchable: boolean;
bundleIds: Array<{
id: UUID;
bundleId: string;
Expand Down
5 changes: 3 additions & 2 deletions dash/types/src/pairql/pairs/SaveUser.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// auto-generated, do not edit
import type { KeychainSchedule, SuccessOutput, PlainTimeWindow } from '../shared';
import type { PlainTimeWindow, BlockedApp, SuccessOutput, RuleSchedule } from '../shared';

export namespace SaveUser {
export interface Input {
Expand All @@ -14,8 +14,9 @@ export namespace SaveUser {
downtime?: PlainTimeWindow;
keychains: Array<{
id: UUID;
schedule?: KeychainSchedule;
schedule?: RuleSchedule;
}>;
blockedApps?: BlockedApp[];
}

export type Output = SuccessOutput;
Expand Down
Loading

0 comments on commit c3786ec

Please sign in to comment.