mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
Merge branch 'master' into release
This commit is contained in:
@@ -16,6 +16,12 @@
|
||||
"ios": {
|
||||
"supportsTablet": true,
|
||||
"bundleIdentifier": "com.oneuptime.oncall",
|
||||
"icon": "./assets/icon.png",
|
||||
"splash": {
|
||||
"image": "./assets/splash-icon.png",
|
||||
"resizeMode": "contain",
|
||||
"backgroundColor": "#0D1117"
|
||||
},
|
||||
"infoPlist": {
|
||||
"UIBackgroundModes": [
|
||||
"remote-notification"
|
||||
@@ -43,7 +49,17 @@
|
||||
{
|
||||
"backgroundColor": "#0D1117",
|
||||
"image": "./assets/splash-icon.png",
|
||||
"imageWidth": 200
|
||||
"imageWidth": 200,
|
||||
"ios": {
|
||||
"backgroundColor": "#0D1117",
|
||||
"image": "./assets/splash-icon.png",
|
||||
"imageWidth": 200
|
||||
},
|
||||
"android": {
|
||||
"backgroundColor": "#0D1117",
|
||||
"image": "./assets/splash-icon.png",
|
||||
"imageWidth": 200
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
|
||||
@@ -91,7 +91,14 @@ export async function requestPermissionsAndGetToken(): Promise<string | null> {
|
||||
let finalStatus: PermissionStatus = existingStatus;
|
||||
|
||||
if (existingStatus !== "granted") {
|
||||
const { status } = await Notifications.requestPermissionsAsync();
|
||||
const { status } = await Notifications.requestPermissionsAsync({
|
||||
ios: {
|
||||
allowAlert: true,
|
||||
allowBadge: true,
|
||||
allowSound: true,
|
||||
allowCriticalAlerts: true,
|
||||
},
|
||||
});
|
||||
finalStatus = status;
|
||||
}
|
||||
|
||||
@@ -115,10 +122,18 @@ export async function requestPermissionsAndGetToken(): Promise<string | null> {
|
||||
}
|
||||
|
||||
try {
|
||||
logger.info(
|
||||
`[PushNotifications] Requesting Expo push token with projectId: ${projectId}`,
|
||||
);
|
||||
|
||||
const tokenData: ExpoPushToken = await Notifications.getExpoPushTokenAsync({
|
||||
projectId,
|
||||
});
|
||||
|
||||
logger.info(
|
||||
`[PushNotifications] Successfully obtained push token: ${tokenData.data}`,
|
||||
);
|
||||
|
||||
return tokenData.data;
|
||||
} catch (error: unknown) {
|
||||
logger.error("[PushNotifications] Failed to get push token:", error);
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { useState, useCallback, useMemo } from "react";
|
||||
import {
|
||||
View,
|
||||
SectionList,
|
||||
ScrollView,
|
||||
RefreshControl,
|
||||
Text,
|
||||
SectionListRenderItemInfo,
|
||||
@@ -307,19 +308,21 @@ export default function AlertsScreen(): React.JSX.Element {
|
||||
<View
|
||||
style={{ flex: 1, backgroundColor: theme.colors.backgroundPrimary }}
|
||||
>
|
||||
<SegmentedControl
|
||||
segments={[
|
||||
{ key: "alerts" as const, label: "Alerts" },
|
||||
{ key: "episodes" as const, label: "Episodes" },
|
||||
]}
|
||||
selected={segment}
|
||||
onSelect={setSegment}
|
||||
/>
|
||||
<View style={{ padding: 16 }}>
|
||||
<SkeletonCard />
|
||||
<SkeletonCard />
|
||||
<SkeletonCard />
|
||||
</View>
|
||||
<ScrollView contentInsetAdjustmentBehavior="automatic">
|
||||
<SegmentedControl
|
||||
segments={[
|
||||
{ key: "alerts" as const, label: "Alerts" },
|
||||
{ key: "episodes" as const, label: "Episodes" },
|
||||
]}
|
||||
selected={segment}
|
||||
onSelect={setSegment}
|
||||
/>
|
||||
<View style={{ padding: 16 }}>
|
||||
<SkeletonCard />
|
||||
<SkeletonCard />
|
||||
<SkeletonCard />
|
||||
</View>
|
||||
</ScrollView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -337,43 +340,48 @@ export default function AlertsScreen(): React.JSX.Element {
|
||||
<View
|
||||
style={{ flex: 1, backgroundColor: theme.colors.backgroundPrimary }}
|
||||
>
|
||||
<SegmentedControl
|
||||
segments={[
|
||||
{ key: "alerts" as const, label: "Alerts" },
|
||||
{ key: "episodes" as const, label: "Episodes" },
|
||||
]}
|
||||
selected={segment}
|
||||
onSelect={setSegment}
|
||||
/>
|
||||
<EmptyState
|
||||
title="Something went wrong"
|
||||
subtitle={
|
||||
segment === "alerts"
|
||||
? "Failed to load alerts. Pull to refresh or try again."
|
||||
: "Failed to load alert episodes. Pull to refresh or try again."
|
||||
}
|
||||
icon="alerts"
|
||||
actionLabel="Retry"
|
||||
onAction={retryFn}
|
||||
/>
|
||||
<ScrollView contentInsetAdjustmentBehavior="automatic">
|
||||
<SegmentedControl
|
||||
segments={[
|
||||
{ key: "alerts" as const, label: "Alerts" },
|
||||
{ key: "episodes" as const, label: "Episodes" },
|
||||
]}
|
||||
selected={segment}
|
||||
onSelect={setSegment}
|
||||
/>
|
||||
<EmptyState
|
||||
title="Something went wrong"
|
||||
subtitle={
|
||||
segment === "alerts"
|
||||
? "Failed to load alerts. Pull to refresh or try again."
|
||||
: "Failed to load alert episodes. Pull to refresh or try again."
|
||||
}
|
||||
icon="alerts"
|
||||
actionLabel="Retry"
|
||||
onAction={retryFn}
|
||||
/>
|
||||
</ScrollView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1, backgroundColor: theme.colors.backgroundPrimary }}>
|
||||
<SegmentedControl
|
||||
segments={[
|
||||
{ key: "alerts" as const, label: "Alerts" },
|
||||
{ key: "episodes" as const, label: "Episodes" },
|
||||
]}
|
||||
selected={segment}
|
||||
onSelect={setSegment}
|
||||
/>
|
||||
{segment === "alerts" ? (
|
||||
<SectionList
|
||||
sections={alertSections}
|
||||
style={{ flex: 1 }}
|
||||
contentInsetAdjustmentBehavior="automatic"
|
||||
ListHeaderComponent={
|
||||
<SegmentedControl
|
||||
segments={[
|
||||
{ key: "alerts" as const, label: "Alerts" },
|
||||
{ key: "episodes" as const, label: "Episodes" },
|
||||
]}
|
||||
selected={segment}
|
||||
onSelect={setSegment}
|
||||
/>
|
||||
}
|
||||
keyExtractor={(wrapped: ProjectAlertItem) => {
|
||||
return `${wrapped.projectId}-${wrapped.item._id}`;
|
||||
}}
|
||||
@@ -451,6 +459,17 @@ export default function AlertsScreen(): React.JSX.Element {
|
||||
<SectionList
|
||||
sections={episodeSections}
|
||||
style={{ flex: 1 }}
|
||||
contentInsetAdjustmentBehavior="automatic"
|
||||
ListHeaderComponent={
|
||||
<SegmentedControl
|
||||
segments={[
|
||||
{ key: "alerts" as const, label: "Alerts" },
|
||||
{ key: "episodes" as const, label: "Episodes" },
|
||||
]}
|
||||
selected={segment}
|
||||
onSelect={setSegment}
|
||||
/>
|
||||
}
|
||||
keyExtractor={(wrapped: ProjectAlertEpisodeItem) => {
|
||||
return `${wrapped.projectId}-${wrapped.item._id}`;
|
||||
}}
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { useState, useCallback, useMemo } from "react";
|
||||
import {
|
||||
View,
|
||||
SectionList,
|
||||
ScrollView,
|
||||
RefreshControl,
|
||||
Text,
|
||||
SectionListRenderItemInfo,
|
||||
@@ -312,19 +313,21 @@ export default function IncidentsScreen(): React.JSX.Element {
|
||||
<View
|
||||
style={{ flex: 1, backgroundColor: theme.colors.backgroundPrimary }}
|
||||
>
|
||||
<SegmentedControl
|
||||
segments={[
|
||||
{ key: "incidents" as const, label: "Incidents" },
|
||||
{ key: "episodes" as const, label: "Episodes" },
|
||||
]}
|
||||
selected={segment}
|
||||
onSelect={setSegment}
|
||||
/>
|
||||
<View style={{ padding: 16 }}>
|
||||
<SkeletonCard />
|
||||
<SkeletonCard />
|
||||
<SkeletonCard />
|
||||
</View>
|
||||
<ScrollView contentInsetAdjustmentBehavior="automatic">
|
||||
<SegmentedControl
|
||||
segments={[
|
||||
{ key: "incidents" as const, label: "Incidents" },
|
||||
{ key: "episodes" as const, label: "Episodes" },
|
||||
]}
|
||||
selected={segment}
|
||||
onSelect={setSegment}
|
||||
/>
|
||||
<View style={{ padding: 16 }}>
|
||||
<SkeletonCard />
|
||||
<SkeletonCard />
|
||||
<SkeletonCard />
|
||||
</View>
|
||||
</ScrollView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -342,43 +345,48 @@ export default function IncidentsScreen(): React.JSX.Element {
|
||||
<View
|
||||
style={{ flex: 1, backgroundColor: theme.colors.backgroundPrimary }}
|
||||
>
|
||||
<SegmentedControl
|
||||
segments={[
|
||||
{ key: "incidents" as const, label: "Incidents" },
|
||||
{ key: "episodes" as const, label: "Episodes" },
|
||||
]}
|
||||
selected={segment}
|
||||
onSelect={setSegment}
|
||||
/>
|
||||
<EmptyState
|
||||
title="Something went wrong"
|
||||
subtitle={
|
||||
segment === "incidents"
|
||||
? "Failed to load incidents. Pull to refresh or try again."
|
||||
: "Failed to load incident episodes. Pull to refresh or try again."
|
||||
}
|
||||
icon="incidents"
|
||||
actionLabel="Retry"
|
||||
onAction={retryFn}
|
||||
/>
|
||||
<ScrollView contentInsetAdjustmentBehavior="automatic">
|
||||
<SegmentedControl
|
||||
segments={[
|
||||
{ key: "incidents" as const, label: "Incidents" },
|
||||
{ key: "episodes" as const, label: "Episodes" },
|
||||
]}
|
||||
selected={segment}
|
||||
onSelect={setSegment}
|
||||
/>
|
||||
<EmptyState
|
||||
title="Something went wrong"
|
||||
subtitle={
|
||||
segment === "incidents"
|
||||
? "Failed to load incidents. Pull to refresh or try again."
|
||||
: "Failed to load incident episodes. Pull to refresh or try again."
|
||||
}
|
||||
icon="incidents"
|
||||
actionLabel="Retry"
|
||||
onAction={retryFn}
|
||||
/>
|
||||
</ScrollView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1, backgroundColor: theme.colors.backgroundPrimary }}>
|
||||
<SegmentedControl
|
||||
segments={[
|
||||
{ key: "incidents" as const, label: "Incidents" },
|
||||
{ key: "episodes" as const, label: "Episodes" },
|
||||
]}
|
||||
selected={segment}
|
||||
onSelect={setSegment}
|
||||
/>
|
||||
{segment === "incidents" ? (
|
||||
<SectionList
|
||||
sections={incidentSections}
|
||||
style={{ flex: 1 }}
|
||||
contentInsetAdjustmentBehavior="automatic"
|
||||
ListHeaderComponent={
|
||||
<SegmentedControl
|
||||
segments={[
|
||||
{ key: "incidents" as const, label: "Incidents" },
|
||||
{ key: "episodes" as const, label: "Episodes" },
|
||||
]}
|
||||
selected={segment}
|
||||
onSelect={setSegment}
|
||||
/>
|
||||
}
|
||||
keyExtractor={(wrapped: ProjectIncidentItem) => {
|
||||
return `${wrapped.projectId}-${wrapped.item._id}`;
|
||||
}}
|
||||
@@ -457,6 +465,17 @@ export default function IncidentsScreen(): React.JSX.Element {
|
||||
<SectionList
|
||||
sections={episodeSections}
|
||||
style={{ flex: 1 }}
|
||||
contentInsetAdjustmentBehavior="automatic"
|
||||
ListHeaderComponent={
|
||||
<SegmentedControl
|
||||
segments={[
|
||||
{ key: "incidents" as const, label: "Incidents" },
|
||||
{ key: "episodes" as const, label: "Episodes" },
|
||||
]}
|
||||
selected={segment}
|
||||
onSelect={setSegment}
|
||||
/>
|
||||
}
|
||||
keyExtractor={(wrapped: ProjectIncidentEpisodeItem) => {
|
||||
return `${wrapped.projectId}-${wrapped.item._id}`;
|
||||
}}
|
||||
|
||||
@@ -81,7 +81,10 @@ export default function MyOnCallPoliciesScreen(): React.JSX.Element {
|
||||
<View
|
||||
style={{ flex: 1, backgroundColor: theme.colors.backgroundPrimary }}
|
||||
>
|
||||
<ScrollView contentContainerStyle={{ padding: 16, paddingBottom: 44 }}>
|
||||
<ScrollView
|
||||
contentInsetAdjustmentBehavior="automatic"
|
||||
contentContainerStyle={{ padding: 16, paddingBottom: 44 }}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
borderRadius: 24,
|
||||
@@ -123,6 +126,7 @@ export default function MyOnCallPoliciesScreen(): React.JSX.Element {
|
||||
|
||||
return (
|
||||
<ScrollView
|
||||
contentInsetAdjustmentBehavior="automatic"
|
||||
style={{ backgroundColor: theme.colors.backgroundPrimary }}
|
||||
contentContainerStyle={{ padding: 20, paddingBottom: 56 }}
|
||||
refreshControl={
|
||||
|
||||
@@ -22,6 +22,7 @@ const APP_VERSION: string = "1.0.0";
|
||||
interface SettingsRowProps {
|
||||
label: string;
|
||||
value?: string;
|
||||
valueBelowLabel?: string;
|
||||
onPress?: () => void;
|
||||
rightElement?: React.ReactNode;
|
||||
destructive?: boolean;
|
||||
@@ -32,6 +33,7 @@ interface SettingsRowProps {
|
||||
function SettingsRow({
|
||||
label,
|
||||
value,
|
||||
valueBelowLabel,
|
||||
onPress,
|
||||
rightElement,
|
||||
destructive,
|
||||
@@ -43,11 +45,9 @@ function SettingsRow({
|
||||
const content: React.JSX.Element = (
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
paddingHorizontal: 16,
|
||||
minHeight: 52,
|
||||
justifyContent: "center",
|
||||
...(!isLast
|
||||
? {
|
||||
borderBottomWidth: 1,
|
||||
@@ -56,57 +56,79 @@ function SettingsRow({
|
||||
: {}),
|
||||
}}
|
||||
>
|
||||
<View style={{ flexDirection: "row", alignItems: "center", flex: 1 }}>
|
||||
{iconName ? (
|
||||
<View
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<View style={{ flexDirection: "row", alignItems: "center", flex: 1 }}>
|
||||
{iconName ? (
|
||||
<View
|
||||
style={{
|
||||
width: 28,
|
||||
height: 28,
|
||||
borderRadius: 8,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
marginRight: 12,
|
||||
backgroundColor: destructive
|
||||
? theme.colors.statusErrorBg
|
||||
: theme.colors.iconBackground,
|
||||
}}
|
||||
>
|
||||
<Ionicons
|
||||
name={iconName}
|
||||
size={15}
|
||||
color={
|
||||
destructive
|
||||
? theme.colors.actionDestructive
|
||||
: theme.colors.actionPrimary
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
) : null}
|
||||
<Text
|
||||
style={{
|
||||
width: 28,
|
||||
height: 28,
|
||||
borderRadius: 8,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
marginRight: 12,
|
||||
backgroundColor: destructive
|
||||
? theme.colors.statusErrorBg
|
||||
: theme.colors.iconBackground,
|
||||
fontSize: 15,
|
||||
fontWeight: "500",
|
||||
paddingVertical: 12,
|
||||
color: destructive
|
||||
? theme.colors.actionDestructive
|
||||
: theme.colors.textPrimary,
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
</Text>
|
||||
</View>
|
||||
{rightElement ??
|
||||
(value ? (
|
||||
<Text style={{ fontSize: 14, color: theme.colors.textTertiary }}>
|
||||
{value}
|
||||
</Text>
|
||||
) : onPress ? (
|
||||
<Ionicons
|
||||
name={iconName}
|
||||
size={15}
|
||||
color={
|
||||
destructive
|
||||
? theme.colors.actionDestructive
|
||||
: theme.colors.actionPrimary
|
||||
}
|
||||
name="chevron-forward"
|
||||
size={18}
|
||||
color={theme.colors.textTertiary}
|
||||
/>
|
||||
</View>
|
||||
) : null}
|
||||
) : null)}
|
||||
</View>
|
||||
{valueBelowLabel ? (
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 15,
|
||||
fontWeight: "500",
|
||||
paddingVertical: 12,
|
||||
color: destructive
|
||||
? theme.colors.actionDestructive
|
||||
: theme.colors.textPrimary,
|
||||
fontSize: 13,
|
||||
color: theme.colors.textTertiary,
|
||||
paddingBottom: 12,
|
||||
marginTop: -4,
|
||||
}}
|
||||
numberOfLines={1}
|
||||
ellipsizeMode="tail"
|
||||
>
|
||||
{label}
|
||||
{valueBelowLabel}
|
||||
</Text>
|
||||
</View>
|
||||
{rightElement ??
|
||||
(value ? (
|
||||
<Text style={{ fontSize: 14, color: theme.colors.textTertiary }}>
|
||||
{value}
|
||||
</Text>
|
||||
) : onPress ? (
|
||||
<Ionicons
|
||||
name="chevron-forward"
|
||||
size={18}
|
||||
color={theme.colors.textTertiary}
|
||||
/>
|
||||
) : null)}
|
||||
) : null}
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -150,6 +172,7 @@ export default function SettingsScreen(): React.JSX.Element {
|
||||
|
||||
return (
|
||||
<ScrollView
|
||||
contentInsetAdjustmentBehavior="automatic"
|
||||
style={{ backgroundColor: theme.colors.backgroundPrimary }}
|
||||
contentContainerStyle={{ padding: 20, paddingBottom: 120 }}
|
||||
>
|
||||
@@ -229,43 +252,70 @@ export default function SettingsScreen(): React.JSX.Element {
|
||||
<View
|
||||
style={{
|
||||
marginTop: 16,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
borderRadius: 10,
|
||||
backgroundColor: theme.colors.backgroundTertiary,
|
||||
borderWidth: 1,
|
||||
borderColor: theme.colors.borderSubtle,
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 10,
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 11,
|
||||
fontWeight: "600",
|
||||
textTransform: "uppercase",
|
||||
color: theme.colors.textTertiary,
|
||||
letterSpacing: 1,
|
||||
}}
|
||||
>
|
||||
Connected to
|
||||
</Text>
|
||||
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: 10,
|
||||
paddingVertical: 4,
|
||||
borderRadius: 8,
|
||||
backgroundColor: theme.colors.accentCyanBg,
|
||||
borderWidth: 1,
|
||||
borderColor: theme.colors.borderGlass,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 11,
|
||||
fontWeight: "600",
|
||||
color: theme.colors.accentCyan,
|
||||
textTransform: "uppercase",
|
||||
color: theme.colors.textTertiary,
|
||||
letterSpacing: 1,
|
||||
}}
|
||||
>
|
||||
{serverUrl || "oneuptime.com"}
|
||||
Connected to
|
||||
</Text>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
width: 6,
|
||||
height: 6,
|
||||
borderRadius: 3,
|
||||
backgroundColor: theme.colors.statusSuccess,
|
||||
marginRight: 5,
|
||||
}}
|
||||
/>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 11,
|
||||
fontWeight: "600",
|
||||
color: theme.colors.statusSuccess,
|
||||
}}
|
||||
>
|
||||
Online
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 13,
|
||||
fontWeight: "500",
|
||||
color: theme.colors.textSecondary,
|
||||
marginTop: 6,
|
||||
}}
|
||||
numberOfLines={1}
|
||||
ellipsizeMode="middle"
|
||||
>
|
||||
{serverUrl || "oneuptime.com"}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -347,14 +397,98 @@ export default function SettingsScreen(): React.JSX.Element {
|
||||
backgroundColor: theme.colors.backgroundElevated,
|
||||
borderWidth: 1,
|
||||
borderColor: theme.colors.borderGlass,
|
||||
padding: 16,
|
||||
}}
|
||||
>
|
||||
<SettingsRow
|
||||
label="Server URL"
|
||||
iconName="globe-outline"
|
||||
value={serverUrl || "oneuptime.com"}
|
||||
isLast
|
||||
/>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
marginBottom: 12,
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
width: 28,
|
||||
height: 28,
|
||||
borderRadius: 8,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
marginRight: 10,
|
||||
backgroundColor: theme.colors.iconBackground,
|
||||
}}
|
||||
>
|
||||
<Ionicons
|
||||
name="globe-outline"
|
||||
size={15}
|
||||
color={theme.colors.actionPrimary}
|
||||
/>
|
||||
</View>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 15,
|
||||
fontWeight: "500",
|
||||
color: theme.colors.textPrimary,
|
||||
flex: 1,
|
||||
}}
|
||||
>
|
||||
Server URL
|
||||
</Text>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
paddingHorizontal: 8,
|
||||
paddingVertical: 3,
|
||||
borderRadius: 9999,
|
||||
backgroundColor: theme.colors.statusSuccessBg,
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
width: 6,
|
||||
height: 6,
|
||||
borderRadius: 3,
|
||||
backgroundColor: theme.colors.statusSuccess,
|
||||
marginRight: 5,
|
||||
}}
|
||||
/>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 10,
|
||||
fontWeight: "600",
|
||||
color: theme.colors.statusSuccess,
|
||||
letterSpacing: 0.2,
|
||||
}}
|
||||
>
|
||||
Connected
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View
|
||||
style={{
|
||||
borderRadius: 10,
|
||||
backgroundColor: theme.colors.backgroundTertiary,
|
||||
borderWidth: 1,
|
||||
borderColor: theme.colors.borderSubtle,
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 10,
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 13,
|
||||
fontWeight: "500",
|
||||
color: theme.colors.textSecondary,
|
||||
fontFamily: undefined,
|
||||
}}
|
||||
numberOfLines={1}
|
||||
ellipsizeMode="middle"
|
||||
>
|
||||
{serverUrl || "oneuptime.com"}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user