## Prerequisites
- node >= 16.10
- yarn package manager, no need to install, just it through Corepack.
Corepack is included by default with all Node.js installs, but is currently opt-in. To enable it, run the following command:
```shell
corepack enable
```

## Init
```shell
yarn install
```

## Start

### Environmental variables
Create packages/client/.env for override keys default values from packages/client/env.ts
ex:
```dotenv
API_BACKEND=https://dev1.videonext.net/
BUILD_TARGET=browser
```

For start in electron:
```dotenv
BUILD_TARGET=electron
```

You can specify username and password for auto login in electron:
```dotenv
API_USER=user
API_PASS=pass
```

NOTE: If you need to run on some old Mac and/or old MacOS version - you need to roll back to the electron v17.4.11
      in `packages/client/package.json` and you MAY need to add 'FORCE_EGL', 'FORCE_WEBGL' environmental variables
      equal "yes".

### Start local dev server
yarn start

## Structure of project

#### entry point, routes
packages/client/src/core/app/index.tsx

### list of views
packages/client/src/core/store/actions/config.ts

#### global store
packages/client/src/core/store/index.tsx

#### actions
packages/client/src/core/store/actions

#### simple example
packages/client/src/components/examples

#### graphql schemes
packages/client/src/graphql

##### local
packages/client/src/graphql/localQueries.graphql
packages/client/src/graphql/localSchema.graphql

# 3rdParty

## Lumino
https://phosphorjs.github.io/docs.html
https://github.com/jupyterlab/lumino

## GraphQL
/api/graphql

## Theming

#### Theme
Theme for necessary element should be specified in packages/client/src/semantic-ui/theme.config

packages/client/src/semantic-ui/themes/${theme}

#### Styles for all themes

packages/client/src/semantic-ui/site

#### Some docs
http://learnsemantic.com/themes/overview.html

https://semantic-ui.com/usage/theming.html

https://react.semantic-ui.com/theming

## Add new widget / Configure widget
Widget definitions are in packages/client/src/components/Widgets/index.ts.

To add new widget:
- put widget SVG icon to packages/client/src/components/Widgets/images. Usually 48x48 px.
- do the following changes in packages/client/src/components/Widgets/index.ts:
- import widget icon. Example: import CameraLiveIcon from "./images/panorama_48px.svg";
- add unique widget internal ID to WidgetId enumeration.
- register widget module (component) in packages/client/src/core/utils.ts, getModule function.
- widget is added and configured by creating an instance of Widget class (or ancestor) in
  the Widgets array.
- Widget class constructor has the following parameters:
  1) id: unique widget ID from WidgetId enumeration
  2) name: short name (typically the same as title)
  3) title: widget display name
  4) module: widget module (component) name from packages/client/src/core/utils.ts, getModule function
  5) icon: imported widget icon
  6) options (see below)

Widget options:
- props: array of WidgetPropDesc, list of widget properties available to configure by user in view edit mode.
  see description of WidgetPropDesc below.
- acceptsConstructorProps: if true, two additional properties are passed to the widget stub in view edit mode:
  - inConstructor: boolean - indicates that widget stub is in view edit mode.
  - selectedInConstructor: boolean - indicates that widget is selected by user.
- countLimit: number | ((widgets: WidgetInfo[]) => number) - limits number of widget instances in a view.
  can be a constant or a function dynamically returning limit depending on the information about other widgets
  currently present in the view.
- disableCondition: (widgets: WidgetInfo[]) => boolean - function to disable/enable widget in the list of widgets
  in view edit mode depending on the information about other widgets added to the view.
- hideCondition: (widgets: WidgetInfo[]) => boolean - function to hide/show widget in the list of widgets
  in view edit mode depending on the information about other widgets added to the view.
- compatibleWidgets: WidgetId[] - shortcut to disable/enable widget in the list of widgets depending on presence
  of other, compatible or incompatible widgets.
- getViewValidationError: (widgets: WidgetInfo[]) => string | undefined - function returning view validation error
  message to prohibit creation of a view which does not satisfy certain conditions.
- headerDragObjectType: defines type of drag object for widgets supporting drag-n-drop of a widget header.
  The actual drag object must be provided by widget component code using setCellProps function.
- minWidth, minHeight: custom widget size constraints overriding default min size specified with CSS.
- getWidgetWarning: (widget: Widget, props: object, size: WidgetSize) => WidgetWarning | undefined - function which
  can return warning code and text message depending on certain conditions. Currently is used to implement Events
  widget size constraints. Warning message is displayed on a widget stub in view edit mode and in a popup window at
  the top-right corner of the screen in view mode. Widget component code can subscribe to widget warning event to
  implement some actions depending on the presence of the warning. "props" argument of the function is a collection
  of currently assigned values of widget properties.

Widget properties definition:
Widget properties available to configure in view edit mode are defined by "props" array of WidgetPropDesc in widget options.
WidgetPropDesc structure:
- name: name of a property passed to the widget component. Widget component usually defines a property with the corresponding
  name in the definition of component prop type and can access the property along with other component properties.
- label: display name of the property visible in edit mode on the left panel.
- controlType: one of the following: Checkbox, Dropdown, Input, Button. Currently only Checkbox, Dropdown and Button are
  supported. Checkbox corresponds to a property of boolean type, Dropdown property can have number or string type, Button
  property does not pass any value to a widget component, it is intended to show a modal dialog by pressing a button on
  widget properties panel in edit mode.
- defaultValue: a value initially assigned to a widget property editing control.
- getDefaultValue: (widgets: WidgetInfo[], props: object) => WidgetPropValue - a function which can return default editing
  control value depending on some conditions. Arguments: widgets - information about widgets currently present in the view,
  props - collection of currently assigned values of widget properties.
- dropdownItems: list of dropdown items for a widget property with Dropdown control. Each item has a value (string | number)
  which will be passed to a widget component and a display text which will be shown in a dropdown.
- getDropdownItems: (client: ApolloClient<any>, widgets: WidgetInfo[], props: object) => Promise<DropdownItem[]> - a function
  returning dynamic list of dropdown items obtained by a GraphQL query or filtered by some condition. The arguments are the
  following: client - Apollo client to run GraphQL query, widgets - information about widgets currently present in the view,
  props - collection of currently assigned values of widget properties.
- disableCondition: (widgets: WidgetInfo[], props: object) => boolean - a function which can return true to disable widget
  property control depending on certain conditions.
- hideCondition: (widgets: WidgetInfo[], props: object) => boolean - a function which can return true to hide widget
  property control depending on certain conditions.
- minWidth: minimum width in pixels for a dropdown control.
- buttonModal: React.ComponentType<ButtonModalProps> - React component implementing modal dialog to display on button click
  of Button property.
- buttonLabel: text to display on a button for Button property.
- multiple: true to allow multiple choices from a Dropdown control. In case of multiple dropdown value of the property has
  a string[] type.

Widget component: receiving events/data from other widgets in the same view.
Communication between different widgets in the same view can be implemented using widgetEvent passed to each widget component.
Example:

Widget A - event provider.
```typescript
import { WidgetProps } from "components/Widgets";

export enum WidgetAEvent {
  Event1 = "Event1",
  Event2 = "Event2"
};

export type WidgetAEventArgs = {
  ...some props
};

type WidgetAProps = WidgetProps & {
  ..some specific widget props
};

const WidgetA = ({ widgetEvent, ...props }: WidgetAProps) => {
  useEffect(() => {
    const args: WidgetAEventArgs = {...};
    widgetEvent?.publish({ event: WidgetAEvent.Event1, args });
  }, [someCondition]);
  ...
};
```

Widget B - event consumer.
```typescript
import { WidgetProps, WidgetEventArgs } from "components/Widgets";
import { WidgetAEvent, WidgetAEventArgs } from "components/WidgetA";

type WidgetBProps = WidgetProps & {
  ..some specific widget props
};

const WidgetB = ({ widgetEvent, ...props }: WidgetBProps) => {
  useEffect(() => {
    const id = `${Date.now()}.${Math.random()}`;
    widgetEvent?.subscribe(id, onWidgetEvent);

    return function cleanup() {
      widgetEvent?.unsubscribe(id);
    };
  });

  function onWidgetEvent(args: WidgetEventArgs): void {
    switch (args.event) {
      case WidgetAEvent.Event1:
        const evArgs = args.args as WidgetAEventArgs;
        ...do something
        break;
    }
  }
  ...
};
```

## Generate graphql typings

- add to package/client/.env address of graphql api in API_GRAPHQL_CODEGEN and API_BACKEND var, specify API_USER and API_PASS

  ex:
```dotenv
API_USER=user
API_PASS=pass
API_GRAPHQL_CODEGEN=https://dev1.videonext.net/api/graphql
API_BACKEND=https://dev1.videonext.net/
```

- run
```shell
yarn gen
```

- commit, if necessary, generator result: src/generated/graphql.tsx

## Add new field to local store
- packages/client/src/graphql/localSchema.graphql
  - add new field to type AppStore and AppStoreInput

- packages/client/src/graphql/localQueries.graphql
  - specify new field in query store

- run
  ```shell
  cd packages/client/
  yarn gen
  ```

  changes will be stored in packages/client/src/generated/graphql.tsx and should be commited
-

- packages/client/src/core/api/api.ts
  - specify default value for new field in defaultStore property of Apollo: ApolloType object


## Troubleshooting

- If you see error like this:
```
ptzOnPlayer.js:8 Uncaught TypeError: API is not a constructor
    at 879 (ptzOnPlayer.js:8)
    at f.r (app.js:24)
    at 578 (cell.js:11)
    at f.r (app.js:24)
    at 402 (cell.js:972)
    at f.r (app.js:24)
    at 152 (index.ts:2)
    at f.r (app.js:24)
    at 1137 (index.ts:6)
    at f.r (app.js:24)
```
make clean rebuild
```shell
yarn clean
yarn start
```
This error can be found after add\remove packages

## Install

At first run application is try to get new version from "https://updates-dev.cavu.me/windows/solid/hostname". The download of the new version takes place in the background. After downloading new version application return modal with notification and offers restart application now or later. At next run application would auto updated.

## Error reporting

For error reporting specify Sentry Minidump Endpoint in packages/client/.env
```dotenv
API_MINIDUMP=https://1111111.ingest.sentry.io/api/2222222/minidump/?sentry_key=3333333333333333
```
This URL you can get from Project Settings - Client Keys - Minidump Endpoint (https://sentry.io/settings/titan-systems/projects/solid/)

## Publish

Run publish in necessary package dir
```shell
yarn publish
```

## Sentry

- Sentry tracing should be turned on\off on cluster level with SENTRY_ENABLED=yes|no
- Sentry release name should be current Solid version (Solid app for electron or gui-solid for web). Set release name in file: packages/client/src/index.ts
```shell
RELEASE_NAME = "1.0.1"
```
- login to sentry
```shell 
yarn sentry-cli login
```
start build or package
```shell
yarn build
```
or for electron
```shell
yarn build:full:installer-win  
```
