added device actions
This commit is contained in:
parent
cd986c4f56
commit
aa3301ac35
10 changed files with 557 additions and 15 deletions
34
src/models/Tag.ts
Normal file
34
src/models/Tag.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import { pond } from "protobuf-ts/pond";
|
||||
import { or } from "utils/types";
|
||||
import { cloneDeep } from "lodash";
|
||||
|
||||
export class Tag {
|
||||
public settings: pond.TagSettings = pond.TagSettings.create();
|
||||
|
||||
public static create(pb?: pond.Tag): Tag {
|
||||
let my = new Tag();
|
||||
if (pb) {
|
||||
my.settings = pond.TagSettings.fromObject(cloneDeep(or(pb.settings, {})));
|
||||
}
|
||||
return my;
|
||||
}
|
||||
|
||||
public static clone(other?: Tag): Tag {
|
||||
if (other) {
|
||||
return Tag.create(pond.Tag.fromObject({ settings: cloneDeep(other.settings) }));
|
||||
}
|
||||
return Tag.create();
|
||||
}
|
||||
|
||||
public static any(data: any): Tag {
|
||||
return Tag.create(pond.Tag.fromObject(cloneDeep(data)));
|
||||
}
|
||||
|
||||
public key(): string {
|
||||
return this.settings.key;
|
||||
}
|
||||
|
||||
public name(): string {
|
||||
return this.settings.name !== "" ? this.settings.name : "Tag " + this.settings.key;
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ export * from "./Device";
|
|||
export * from "./Scope";
|
||||
export * from "./ShareableLink";
|
||||
// export * from "./Site";
|
||||
// export * from "./Tag";
|
||||
export * from "./Tag";
|
||||
// export * from "./Task";
|
||||
// export * from "./Upgrade";
|
||||
// export * from "./User";
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
import { Theme, Typography } from "@mui/material";
|
||||
import { Grid2, Theme, Typography } from "@mui/material";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import { Device } from "models";
|
||||
import { Device, User } from "models";
|
||||
import { getContextKeys, getContextTypes } from "pbHelpers/Context";
|
||||
import { useDeviceAPI, useSnackbar } from "providers";
|
||||
import { useDeviceAPI, useGlobalState, useSnackbar } from "providers";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useLocation, useParams } from "react-router-dom";
|
||||
import PageContainer from "./PageContainer";
|
||||
import { useMobile } from "hooks";
|
||||
import LoadingScreen from "app/LoadingScreen";
|
||||
import SmartBreadcrumb from "common/SmartBreadcrumb";
|
||||
import DeviceActions from "common/DeviceActions";
|
||||
import { pond } from "protobuf-ts/pond";
|
||||
import { cloneDeep } from "lodash";
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) => {
|
||||
// const isMobile = useMobile()
|
||||
|
|
@ -24,19 +27,53 @@ export default function DevicePage() {
|
|||
const isMobile = useMobile()
|
||||
const deviceID = useParams<{ deviceID: string }>()?.deviceID ?? "";
|
||||
const { state } = useLocation();
|
||||
const [{ as, team, user }] = useGlobalState()
|
||||
const [device, setDevice] = useState<Device>(state?.device ? Device.create(state.device) : Device.create())
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [permissions, setPermissions] = useState<pond.Permission[]>([])
|
||||
const [preferences, setPreferences] = useState<pond.DevicePreferences>(pond.DevicePreferences.create())
|
||||
|
||||
const loadDevice = () => {
|
||||
if (loading) return
|
||||
if (state?.device && parseInt(state.device.settings.deviceId) === device.id()) return
|
||||
deviceAPI.get(deviceID, false, getContextKeys(), getContextTypes()).then(resp => {
|
||||
setDevice(Device.create(resp.data))
|
||||
}).catch(() => {
|
||||
snackbar.error("Error loading device.")
|
||||
}).finally(() => {
|
||||
setLoading(false)
|
||||
// if (state?.device && parseInt(state.device.settings.deviceId) === device.id()) return
|
||||
// deviceAPI.get(deviceID, false, getContextKeys(), getContextTypes()).then(resp => {
|
||||
// setDevice(Device.create(resp.data))
|
||||
// console.log(resp.data)
|
||||
// }).catch(() => {
|
||||
// snackbar.error("Error loading device.")
|
||||
// }).finally(() => {
|
||||
// setLoading(false)
|
||||
// })
|
||||
|
||||
setLoading(true)
|
||||
deviceAPI.getPageData(deviceID, getContextKeys(), getContextTypes()).then(resp => {
|
||||
setDevice(Device.any(resp.data.device))
|
||||
setPermissions(resp.data.permissions)
|
||||
let u = User.any(resp.data.user);
|
||||
setPreferences(u.preferences)
|
||||
console.log(u.preferences)
|
||||
}).catch(err => {
|
||||
setDevice(Device.create());
|
||||
// setComponents(new Map());
|
||||
// setInteractions([]);
|
||||
// setAvailablePositions(new Map());
|
||||
setPermissions([]);
|
||||
setPreferences(pond.UserPreferences.create());
|
||||
// setInvalidDevice(true);
|
||||
// error(err);
|
||||
if (err?.response?.data?.error.includes("not found")) {
|
||||
let name = as === team.key() ? team.name() : user.name();
|
||||
let warningString = name + " not permitted to view Device " + deviceID;
|
||||
let length = getContextTypes().length;
|
||||
if (length > 0) {
|
||||
warningString = warningString + " through " + getContextTypes()[length - 1];
|
||||
}
|
||||
snackbar.warning(warningString);
|
||||
} else {
|
||||
snackbar.error(err);
|
||||
}
|
||||
})
|
||||
.finally(() => setLoading(false));
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -47,6 +84,21 @@ export default function DevicePage() {
|
|||
loadDevice()
|
||||
}, [])
|
||||
|
||||
const toggleNotificationPreference = () => {
|
||||
let updatedPreferences = cloneDeep(preferences);
|
||||
updatedPreferences.notify = !preferences.notify;
|
||||
deviceAPI
|
||||
.updatePreferences(deviceID, updatedPreferences, getContextKeys(), getContextTypes())
|
||||
.then(() => setPreferences(updatedPreferences))
|
||||
.catch(() => {
|
||||
snackbar.error(
|
||||
"Error occured while " +
|
||||
(preferences.notify ? "enabling" : "disabling") +
|
||||
" notifications"
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
if (loading) return (
|
||||
<LoadingScreen message="Loading device"/>
|
||||
)
|
||||
|
|
@ -56,7 +108,22 @@ export default function DevicePage() {
|
|||
{/* <Typography>
|
||||
{device.name()}
|
||||
</Typography> */}
|
||||
<SmartBreadcrumb />
|
||||
<Grid2 container justifyContent={"space-between"}>
|
||||
<Grid2>
|
||||
<SmartBreadcrumb />
|
||||
</Grid2>
|
||||
<Grid2>
|
||||
<DeviceActions
|
||||
device={device}
|
||||
isPaused={false}
|
||||
isLoading={loading}
|
||||
permissions={permissions}
|
||||
preferences={preferences}
|
||||
toggleNotificationPreference={toggleNotificationPreference}
|
||||
refreshCallback={loadDevice}
|
||||
/>
|
||||
</Grid2>
|
||||
</Grid2>
|
||||
</PageContainer>
|
||||
);
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ import { Avatar, AvatarGroup, Box, Button, Card, Grid2, Theme, Typography } from
|
|||
import { makeStyles } from "@mui/styles";
|
||||
import LoadingScreen from "app/LoadingScreen";
|
||||
import Chat from "chat/Chat";
|
||||
// import SmartBreadcrumb from "common/SmartBreadcrumb";
|
||||
import { useMobile } from "hooks";
|
||||
import { cloneDeep } from "lodash";
|
||||
import { Team, teamScope, User } from "models";
|
||||
|
|
@ -159,6 +160,7 @@ export default function TeamPage() {
|
|||
<Grid2 >
|
||||
<Typography variant={isMobile ? "h5" : "h4"} className={classes.ellipsis} >
|
||||
{team.name()}
|
||||
{/* <SmartBreadcrumb /> */}
|
||||
</Typography>
|
||||
</Grid2>
|
||||
<Grid2>
|
||||
|
|
|
|||
149
src/pbHelpers/Device.ts
Normal file
149
src/pbHelpers/Device.ts
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
import { Device, Tag } from "models";
|
||||
import moment from "moment";
|
||||
import { filterByTags } from "pbHelpers/Tag";
|
||||
import { GetDeviceProductLabel } from "products/DeviceProduct";
|
||||
import { pond } from "protobuf-ts/pond";
|
||||
import { quack } from "protobuf-ts/quack";
|
||||
import { capitalize } from "utils/strings";
|
||||
import { getDevicePlatformLabel } from "./DevicePlatform";
|
||||
import { filterByDeviceState } from "./DeviceState";
|
||||
import { filterByPower } from "./Power";
|
||||
import { getUpgradeChannelLabel } from "./UpgradeChannel";
|
||||
|
||||
//complex filter algorithm for searching a list of devices
|
||||
export function filterDevices(searchValue: string, devices: Device[], tags: Tag[]): Device[] {
|
||||
return devices.filter(device =>
|
||||
filterDevice(searchValue.toLowerCase(), device, deviceTags(device, tags))
|
||||
);
|
||||
}
|
||||
|
||||
//helper function for filterDevices
|
||||
export function filterDevice(searchValue: string, device: Device, tags: Tag[]): boolean {
|
||||
const idMatch: boolean = filterByDeviceID(searchValue, device.settings.deviceId.toString());
|
||||
const nameMatch: boolean = filterByDeviceName(searchValue, device.name());
|
||||
const tagsMatch: boolean = filterByTags(searchValue, tags);
|
||||
const deviceStateMatch: boolean = filterByDeviceState(searchValue, device.status.state);
|
||||
const powerMatch: boolean = filterByPower(
|
||||
searchValue,
|
||||
device.status.power ? pond.DevicePower.create(device.status.power) : undefined
|
||||
);
|
||||
const lastActiveMatch: boolean = filterByLastActive(searchValue, device.status.lastActive);
|
||||
return idMatch || nameMatch || tagsMatch || powerMatch || deviceStateMatch || lastActiveMatch;
|
||||
}
|
||||
|
||||
export function filterByDeviceID(searchValue: string, deviceIDString: string): boolean {
|
||||
return searchValue.replace("id:", "").trim() === deviceIDString;
|
||||
}
|
||||
|
||||
export function filterByDeviceName(searchValue: string, deviceName: string): boolean {
|
||||
return (
|
||||
deviceName.toLowerCase().indexOf(
|
||||
searchValue
|
||||
.replace("name:", "")
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
) > -1
|
||||
);
|
||||
}
|
||||
|
||||
export function filterByDeviceDescription(searchValue: string, deviceDescription: string): boolean {
|
||||
return (
|
||||
deviceDescription.toLowerCase().indexOf(
|
||||
searchValue
|
||||
.replace("description:", "")
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
) > -1
|
||||
);
|
||||
}
|
||||
|
||||
export function filterByLastActive(searchValue: string, lastActive: string): boolean {
|
||||
return (
|
||||
moment(lastActive)
|
||||
.fromNow()
|
||||
.indexOf(searchValue.replace("last active:", "")) > -1
|
||||
);
|
||||
}
|
||||
|
||||
export function deviceTags(device: Device, tags: Tag[]): Tag[] {
|
||||
return tags.filter(tag => device.status.tagKeys.some(key => key === tag.settings.key));
|
||||
}
|
||||
|
||||
const keyTranslator = new Map<keyof pond.DeviceSettings, string>([
|
||||
["deviceId", "Device ID"],
|
||||
["name", "Name"],
|
||||
["description", "Description"],
|
||||
["sleepType", "Sleep Type"],
|
||||
["sleepDurationS", "Sleep Duration"],
|
||||
["sleepDelayMs", "Sleep Delay"],
|
||||
["pondCheckPeriodS", "Check Period"],
|
||||
["removed", "Removed"],
|
||||
["platform", "Platform"],
|
||||
["wakePin", "Wake Pin"],
|
||||
["platform", "Platform"],
|
||||
["automaticallyUpgrade", "Upgrades Automatically"],
|
||||
["product", "Product"],
|
||||
["upgradeChannel", "Upgrade Channel"],
|
||||
["latitude", "Latitude"],
|
||||
["longitude", "Longitude"],
|
||||
["theme", "Marker Theme"],
|
||||
["extensionComponents", "Extension Components"],
|
||||
["mutations", "Device Mutations"]
|
||||
]);
|
||||
|
||||
// Keys will be stringified by default if not found in the keyTranslator
|
||||
export function TranslateKey(key: keyof pond.DeviceSettings): string {
|
||||
let translatedKey = keyTranslator.get(key);
|
||||
return translatedKey ? translatedKey : capitalize(key.toString());
|
||||
}
|
||||
|
||||
const valueTranslator = new Map<keyof pond.DeviceSettings, (device: pond.DeviceSettings) => string>(
|
||||
[
|
||||
["name", device => device.name],
|
||||
["description", device => device.description],
|
||||
[
|
||||
"sleepType",
|
||||
device => {
|
||||
switch (device.sleepType) {
|
||||
case quack.SleepType.SLEEP_TYPE_DEEP:
|
||||
return "Deep Sleep";
|
||||
case quack.SleepType.SLEEP_TYPE_HIBERNATE:
|
||||
return "Hibernate";
|
||||
case quack.SleepType.SLEEP_TYPE_NAP:
|
||||
return "Nap";
|
||||
default:
|
||||
return "None";
|
||||
}
|
||||
}
|
||||
],
|
||||
["sleepDurationS", device => device.sleepDurationS.toString() + "s"],
|
||||
["sleepDelayMs", device => (device.sleepDelayMs / 1000).toString() + "s"],
|
||||
["pondCheckPeriodS", device => device.pondCheckPeriodS.toString() + "s"],
|
||||
["platform", device => getDevicePlatformLabel(device.platform)],
|
||||
["product", device => GetDeviceProductLabel(device.product)],
|
||||
["upgradeChannel", device => getUpgradeChannelLabel(device.upgradeChannel)],
|
||||
["automaticallyUpgrade", device => device.automaticallyUpgrade.toString()],
|
||||
["latitude", device => device.latitude.toString()],
|
||||
["longitude", device => device.longitude.toString()],
|
||||
["theme", device => "Colour: " + device.theme?.color + "Size: " + device.theme?.height],
|
||||
["extensionComponents", device => device.extensionComponents.toString()],
|
||||
["mutations", device => device.mutations.toString()]
|
||||
]
|
||||
);
|
||||
|
||||
// Values will be stringified by default if its key is not found in the valueTranslator
|
||||
export function TranslateValue(key: keyof pond.DeviceSettings, device: pond.DeviceSettings) {
|
||||
//console.log(device)
|
||||
//console.log(key)
|
||||
let translatorFunc = valueTranslator.get(key);
|
||||
let dKey = device[key];
|
||||
return translatorFunc ? translatorFunc(device) : capitalize(dKey ? dKey.toString() : "");
|
||||
}
|
||||
|
||||
//checks the user's path to determine if they are on a shared url
|
||||
export function isShareableLink(rawDeviceID: string): boolean {
|
||||
if (!rawDeviceID) {
|
||||
return false;
|
||||
}
|
||||
return isNaN(Number(rawDeviceID));
|
||||
}
|
||||
128
src/pbHelpers/Power.tsx
Normal file
128
src/pbHelpers/Power.tsx
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
import {
|
||||
Battery20,
|
||||
Battery30,
|
||||
Battery50,
|
||||
Battery60,
|
||||
Battery80,
|
||||
Battery90,
|
||||
BatteryAlert,
|
||||
BatteryCharging20,
|
||||
BatteryCharging30,
|
||||
BatteryCharging50,
|
||||
BatteryCharging60,
|
||||
BatteryCharging80,
|
||||
BatteryCharging90,
|
||||
BatteryChargingFull,
|
||||
BatteryFull,
|
||||
BatteryUnknown,
|
||||
PowerOff,
|
||||
Power
|
||||
} from "@mui/icons-material";
|
||||
import { pond } from "protobuf-ts/pond";
|
||||
import { quack } from "protobuf-ts/quack";
|
||||
|
||||
export interface PowerDescriber {
|
||||
description: string;
|
||||
icon: any;
|
||||
}
|
||||
|
||||
interface batteryThreshold {
|
||||
threshold: number;
|
||||
icon: any;
|
||||
}
|
||||
|
||||
let chargingThresholds: batteryThreshold[] = [
|
||||
{ threshold: 100, icon: <BatteryChargingFull style={{ color: "var(--status-ok)" }} /> },
|
||||
{ threshold: 90, icon: <BatteryCharging90 style={{ color: "var(--status-ok)" }} /> },
|
||||
{ threshold: 80, icon: <BatteryCharging80 style={{ color: "var(--status-ok)" }} /> },
|
||||
{ threshold: 60, icon: <BatteryCharging60 style={{ color: "var(--status-unstable)" }} /> },
|
||||
{ threshold: 40, icon: <BatteryCharging50 style={{ color: "var(--status-unstable)" }} /> },
|
||||
{ threshold: 30, icon: <BatteryCharging30 style={{ color: "var(--status-warning)" }} /> },
|
||||
{ threshold: 20, icon: <BatteryCharging20 style={{ color: "var(--status-warning)" }} /> },
|
||||
{ threshold: 1, icon: <BatteryCharging20 style={{ color: "var(--status-alert)" }} /> },
|
||||
{ threshold: 0, icon: <BatteryAlert style={{ color: "var(--status-alert)" }} /> }
|
||||
];
|
||||
|
||||
let notChargingThresholds: batteryThreshold[] = [
|
||||
{ threshold: 100, icon: <BatteryFull style={{ color: "var(--status-ok)" }} /> },
|
||||
{ threshold: 90, icon: <Battery90 style={{ color: "var(--status-ok)" }} /> },
|
||||
{ threshold: 80, icon: <Battery80 style={{ color: "var(--status-ok)" }} /> },
|
||||
{ threshold: 60, icon: <Battery60 style={{ color: "var(--status-unstable)" }} /> },
|
||||
{ threshold: 40, icon: <Battery50 style={{ color: "var(--status-unstable)" }} /> },
|
||||
{ threshold: 30, icon: <Battery30 style={{ color: "var(--status-warning)" }} /> },
|
||||
{ threshold: 20, icon: <Battery20 style={{ color: "var(--status-warning)" }} /> },
|
||||
{ threshold: 1, icon: <Battery20 style={{ color: "var(--status-alert)" }} /> },
|
||||
{ threshold: 0, icon: <BatteryAlert style={{ color: "var(--status-alert)" }} /> }
|
||||
];
|
||||
|
||||
export function describePower(power?: pond.DevicePower | null): PowerDescriber {
|
||||
let result = {
|
||||
description: "",
|
||||
icon: <BatteryUnknown style={{ color: "var(--status-unknown)" }} />
|
||||
};
|
||||
if (!power) return result;
|
||||
if (power.type === quack.PowerSubtype.POWER_SUBTYPE_NO_BATTERY) {
|
||||
if (power.inputVoltage > 0) {
|
||||
result.icon = <Power style={{ color: "var(--status-ok)" }} />;
|
||||
result.description = "Plugged in";
|
||||
} else {
|
||||
result.icon = <PowerOff style={{ color: "var(--status-alert)" }} />;
|
||||
result.description = "Unplugged";
|
||||
}
|
||||
} else {
|
||||
let thresholds = notChargingThresholds;
|
||||
let suffix = "not charging";
|
||||
if (power.inputVoltage >= 4) {
|
||||
thresholds = chargingThresholds;
|
||||
if (power.chargePercent === 100) {
|
||||
suffix = "charged";
|
||||
} else {
|
||||
suffix = "charging";
|
||||
}
|
||||
}
|
||||
result.description = power.chargePercent.toString() + "%, " + suffix;
|
||||
for (let i = 0; i < thresholds.length; i++) {
|
||||
if (power.chargePercent >= thresholds[i].threshold) {
|
||||
result.icon = thresholds[i].icon;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//returns a boolean indicating whether the search term matches a power query
|
||||
export function filterByPower(searchValue: string, power: pond.DevicePower | undefined): boolean {
|
||||
let matches: string[] = [];
|
||||
|
||||
if (power) {
|
||||
switch (power.type) {
|
||||
case quack.PowerSubtype.POWER_SUBTYPE_NO_BATTERY:
|
||||
if (power.inputVoltage > 0) {
|
||||
matches.push("on");
|
||||
} else {
|
||||
matches.push("off");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
const isCharging = power.inputVoltage >= 4.0;
|
||||
if (isCharging) {
|
||||
matches.push("charging");
|
||||
}
|
||||
|
||||
if (power.chargePercent >= 0 && power.chargePercent <= 33) {
|
||||
matches.push("low");
|
||||
} else if (power.chargePercent <= 66) {
|
||||
matches.push("medium");
|
||||
} else {
|
||||
matches.push("high");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
matches = ["unknown", "?"];
|
||||
}
|
||||
|
||||
return matches.some(match => match.indexOf(searchValue.replace("power:", "").trim()) > -1);
|
||||
}
|
||||
19
src/pbHelpers/Tag.tsx
Normal file
19
src/pbHelpers/Tag.tsx
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import { Tag } from "models";
|
||||
|
||||
export function filterByTags(searchValue: string, tags: Tag[]): boolean {
|
||||
return tags.some(tag => filterByTag(searchValue, tag));
|
||||
}
|
||||
|
||||
export function filterByTag(searchValue: string, tag: Tag): boolean {
|
||||
return (
|
||||
tag
|
||||
.name()
|
||||
.toLowerCase()
|
||||
.indexOf(
|
||||
searchValue
|
||||
.replace("tag:", "")
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
) > -1
|
||||
);
|
||||
}
|
||||
54
src/pbHelpers/UpgradeChannel.ts
Normal file
54
src/pbHelpers/UpgradeChannel.ts
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
import { pond } from "protobuf-ts/pond";
|
||||
|
||||
export interface UpgradeChannelHelper {
|
||||
label: string;
|
||||
colour: string;
|
||||
}
|
||||
|
||||
const Unknown: UpgradeChannelHelper = {
|
||||
label: "Unknown Channel",
|
||||
colour: "var(--status-ok)"
|
||||
};
|
||||
|
||||
const Alpha: UpgradeChannelHelper = {
|
||||
label: "Alpha",
|
||||
colour: "var(--status-risk)"
|
||||
};
|
||||
|
||||
const Beta: UpgradeChannelHelper = {
|
||||
label: "Beta",
|
||||
colour: "var(--status-unstable)"
|
||||
};
|
||||
|
||||
const Stable: UpgradeChannelHelper = {
|
||||
label: "Stable",
|
||||
colour: "var(--status-ok)"
|
||||
};
|
||||
|
||||
const Development: UpgradeChannelHelper = {
|
||||
label: "Development",
|
||||
colour: "var(--status-warning)"
|
||||
};
|
||||
|
||||
export function getUpgradeChannelHelper(channel: pond.UpgradeChannel): UpgradeChannelHelper {
|
||||
switch (channel) {
|
||||
case pond.UpgradeChannel.UPGRADE_CHANNEL_ALPHA:
|
||||
return Alpha;
|
||||
case pond.UpgradeChannel.UPGRADE_CHANNEL_BETA:
|
||||
return Beta;
|
||||
case pond.UpgradeChannel.UPGRADE_CHANNEL_STABLE:
|
||||
return Stable;
|
||||
case pond.UpgradeChannel.UPGRADE_CHANNEL_DEVELOPMENT:
|
||||
return Development;
|
||||
default:
|
||||
return Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
export function getUpgradeChannelLabel(channel: pond.UpgradeChannel): string {
|
||||
return getUpgradeChannelHelper(channel).label;
|
||||
}
|
||||
|
||||
export function getUpgradeChannelColour(channel: pond.UpgradeChannel): string {
|
||||
return getUpgradeChannelHelper(channel).colour;
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import { pondURL } from "./pond";
|
|||
import { AxiosResponse } from "axios";
|
||||
import { useGlobalState } from "providers";
|
||||
import moment from "moment";
|
||||
import { or } from "utils/types";
|
||||
|
||||
export interface IDeviceAPIContext {
|
||||
add: (name: string, description: string, backpack: pond.BackpackSettings) => Promise<any>;
|
||||
|
|
@ -55,7 +56,7 @@ export interface IDeviceAPIContext {
|
|||
updatePermissions: (id: number | string, users: User[]) => Promise<any>;
|
||||
updatePreferences: (
|
||||
id: number | string,
|
||||
preferences: pond.UserPreferences,
|
||||
preferences: pond.DevicePreferences,
|
||||
keys?: string[],
|
||||
types?: string[]
|
||||
) => Promise<any>;
|
||||
|
|
@ -333,7 +334,7 @@ export default function DeviceProvider(props: PropsWithChildren<Props>) {
|
|||
|
||||
const updatePreferences = (
|
||||
id: number | string,
|
||||
preferences: pond.UserPreferences,
|
||||
preferences: pond.DevicePreferences,
|
||||
keys?: string[],
|
||||
types?: string[]
|
||||
) => {
|
||||
|
|
@ -343,7 +344,7 @@ export default function DeviceProvider(props: PropsWithChildren<Props>) {
|
|||
id +
|
||||
"/preferences" +
|
||||
"?as=" +
|
||||
as +
|
||||
or(as, "") +
|
||||
(keys ? "&keys=" + keys.toString() : "") +
|
||||
(types ? "&types=" + types.toString() : "")
|
||||
),
|
||||
|
|
|
|||
88
src/utils/strings.ts
Normal file
88
src/utils/strings.ts
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
import {
|
||||
red,
|
||||
purple,
|
||||
deepPurple,
|
||||
indigo,
|
||||
blue,
|
||||
lightBlue,
|
||||
cyan,
|
||||
teal,
|
||||
green,
|
||||
lightGreen,
|
||||
yellow,
|
||||
amber,
|
||||
orange,
|
||||
deepOrange
|
||||
} from "@mui/material/colors";
|
||||
|
||||
export function lowercaseStart(s: string): string {
|
||||
if (s === "" || s.length <= 0) return "";
|
||||
return s.charAt(0).toLowerCase() + s.substring(1);
|
||||
}
|
||||
|
||||
export function capitalize(s: string): string {
|
||||
return s.replace(/\b\w/g, c => c.toUpperCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes in a string and uses the first character of the frist word to determin colour and the first character of the second to determin shade
|
||||
* if a string with only one word is passed will use default shade, words past 2 are not used
|
||||
* @param s any string
|
||||
* @returns hex colour string
|
||||
*/
|
||||
export function stringToMaterialColour(s: string): string {
|
||||
const colours = [
|
||||
red,
|
||||
purple,
|
||||
deepPurple,
|
||||
indigo,
|
||||
blue,
|
||||
lightBlue,
|
||||
cyan,
|
||||
teal,
|
||||
green,
|
||||
lightGreen,
|
||||
yellow,
|
||||
amber,
|
||||
orange,
|
||||
deepOrange
|
||||
];
|
||||
const shades = [
|
||||
"50",
|
||||
"100",
|
||||
"200",
|
||||
"300",
|
||||
"400",
|
||||
"500",
|
||||
"600",
|
||||
"700",
|
||||
"800",
|
||||
"900",
|
||||
"A100",
|
||||
"A200",
|
||||
"A400",
|
||||
"A700"
|
||||
];
|
||||
let strings = s.split(" ");
|
||||
let colourVal = 0;
|
||||
let shadeVal = 5;
|
||||
if (strings[0]) {
|
||||
let val = Number(strings[0].charCodeAt(0));
|
||||
while (val > 13) {
|
||||
val = val - 13;
|
||||
}
|
||||
colourVal = val;
|
||||
}
|
||||
|
||||
if (strings[1]) {
|
||||
let val = Number(strings[1].charCodeAt(0));
|
||||
while (val > 13) {
|
||||
val = val - 13;
|
||||
}
|
||||
shadeVal = val;
|
||||
}
|
||||
|
||||
let colour: any = colours[colourVal];
|
||||
let shade: any = shades[shadeVal];
|
||||
return colour[shade];
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue