Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Merge 4.52.0 into single-server #5881

Merged
merged 10 commits into from
Sep 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -383,8 +383,6 @@ jobs:
docker:
- image: cimg/node:lts
resource_class: large
environment:
CODECOV_TOKEN: caa771ab-3d45-4756-8e2a-e1f25996fef6

steps:
- checkout
Expand All @@ -403,11 +401,6 @@ jobs:
command: |
yarn test --runInBand

- run:
name: Codecov
command: |
yarn codecov

- save_cache: *save-npm-cache-linux

# Android builds
Expand Down
2 changes: 1 addition & 1 deletion android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode VERSIONCODE as Integer
versionName "4.51.0"
versionName "4.52.0"
vectorDrawables.useSupportLibrary = true
if (!isFoss) {
manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String]
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package chat.rocket.reactnative.share

import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import java.io.File
import java.io.FileOutputStream
import java.util.*

class ShareActivity : AppCompatActivity() {

private val appScheme = "rocketchat"

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
handleIntent(intent)
}

private fun handleIntent(intent: Intent?) {
// Check if the intent contains shared content
if (intent?.action == Intent.ACTION_SEND || intent?.action == Intent.ACTION_SEND_MULTIPLE) {
when {
intent.type?.startsWith("text/") == true -> handleText(intent)
intent.type?.startsWith("image/") == true -> handleMedia(intent, "data")
intent.type?.startsWith("video/") == true -> handleMedia(intent, "data")
intent.type?.startsWith("application/") == true -> handleMedia(intent, "data")
intent.type == "*/*" -> handleMedia(intent, "data")
intent.type == "text/plain" -> handleText(intent)
else -> completeRequest() // No matching type, complete the request
}
} else {
completeRequest() // No relevant intent action, complete the request
}
}

private fun handleText(intent: Intent) {
// Handle sharing text
val sharedText = intent.getStringExtra(Intent.EXTRA_TEXT)
if (sharedText != null) {
val encoded = Uri.encode(sharedText)
val url = Uri.parse("$appScheme://shareextension?text=$encoded")
openURL(url)
}
completeRequest()
}

private fun handleMedia(intent: Intent, type: String) {
val mediaUris = StringBuilder()
var valid = true

val uris = when (intent.action) {
Intent.ACTION_SEND -> listOf(intent.getParcelableExtra(Intent.EXTRA_STREAM) as Uri?)
Intent.ACTION_SEND_MULTIPLE -> intent.getParcelableArrayListExtra<Uri>(Intent.EXTRA_STREAM)
else -> null
}

uris?.forEachIndexed { index, uri ->
val mediaUri = uri?.let { handleMediaUri(it, type) }
if (mediaUri != null) {
mediaUris.append(mediaUri)
if (index < uris.size - 1) {
mediaUris.append(",")
}
} else {
valid = false
}
}

if (valid) {
val encoded = Uri.encode(mediaUris.toString())
val url = Uri.parse("$appScheme://shareextension?mediaUris=$encoded")
openURL(url)
}
completeRequest()
}

private fun handleMediaUri(uri: Uri, type: String): String? {
return try {
val inputStream = contentResolver.openInputStream(uri)
val originalFilename = getFileName(uri)
val filename = originalFilename ?: UUID.randomUUID().toString() + getFileExtension(uri, type)
val fileUri = saveDataToCacheDir(inputStream?.readBytes(), filename)
fileUri?.toString()
} catch (e: Exception) {
Log.e("ShareRocketChat", "Failed to process media", e)
null
}
}

private fun getFileName(uri: Uri): String? {
// Attempt to get the original filename from the Uri
val cursor = contentResolver.query(uri, null, null, null, null)
return cursor?.use {
if (it.moveToFirst()) {
val nameIndex = it.getColumnIndex("_display_name")
if (nameIndex != -1) it.getString(nameIndex) else null
} else null
}
}

private fun getFileExtension(uri: Uri, type: String): String {
// Determine the file extension based on the mime type, with fallbacks
val mimeType = contentResolver.getType(uri)
return when {
mimeType?.startsWith("image/") == true -> ".jpeg"
mimeType?.startsWith("video/") == true -> ".mp4"
else -> "" // Ignore the file if the type is not recognized
}
}

private fun saveDataToCacheDir(data: ByteArray?, filename: String): Uri? {
// Save the shared data to the app's cache directory and return the file URI
return try {
val file = File(cacheDir, filename)
FileOutputStream(file).use { it.write(data) }
Uri.fromFile(file) // Return the file URI with file:// scheme
} catch (e: Exception) {
Log.e("ShareRocketChat", "Failed to save data", e)
null
}
}

private fun openURL(uri: Uri) {
// Open the custom URI in the associated app
val intent = Intent(Intent.ACTION_VIEW, uri)
if (intent.resolveActivity(packageManager) != null) {
startActivity(intent)
}
}

private fun completeRequest() {
// Finish the share activity
finish()
}
}

This file was deleted.

5 changes: 1 addition & 4 deletions app.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
{
"name": "RocketChatRN",
"share": "ShareRocketChatRN",
"server": "https://open.rocket.chat",
"appGroup": "group.ios.chat.rocket",
"appStoreID": "1272915472"
"server": "https://open.rocket.chat"
}
8 changes: 7 additions & 1 deletion app/AppContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import SetUsernameView from './views/SetUsernameView';
import OutsideStack from './stacks/OutsideStack';
import InsideStack from './stacks/InsideStack';
import MasterDetailStack from './stacks/MasterDetailStack';
import ShareExtensionStack from './stacks/ShareExtensionStack';
import { ThemeContext } from './theme';
import { setCurrentScreen } from './lib/methods/helpers/log';

Expand Down Expand Up @@ -57,13 +58,18 @@ const App = memo(({ root, isMasterDetail }: { root: string; isMasterDetail: bool
Navigation.routeNameRef.current = currentRouteName;
}}>
<Stack.Navigator screenOptions={{ headerShown: false, animationEnabled: false }}>
{root === RootEnum.ROOT_LOADING ? <Stack.Screen name='AuthLoading' component={AuthLoadingView} /> : null}
{root === RootEnum.ROOT_LOADING || root === RootEnum.ROOT_LOADING_SHARE_EXTENSION ? (
<Stack.Screen name='AuthLoading' component={AuthLoadingView} />
) : null}
{root === RootEnum.ROOT_OUTSIDE ? <Stack.Screen name='OutsideStack' component={OutsideStack} /> : null}
{root === RootEnum.ROOT_INSIDE && isMasterDetail ? (
<Stack.Screen name='MasterDetailStack' component={MasterDetailStack} />
) : null}
{root === RootEnum.ROOT_INSIDE && !isMasterDetail ? <Stack.Screen name='InsideStack' component={InsideStack} /> : null}
{root === RootEnum.ROOT_SET_USERNAME ? <Stack.Screen name='SetUsernameStack' component={SetUsernameStack} /> : null}
{root === RootEnum.ROOT_SHARE_EXTENSION ? (
<Stack.Screen name='ShareExtensionStack' component={ShareExtensionStack} />
) : null}
</Stack.Navigator>
</NavigationContainer>
);
Expand Down
2 changes: 1 addition & 1 deletion app/actions/actionsTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ function createRequestTypes(base = {}, types = defaultTypes): Record<string, str

// Login events
export const LOGIN = createRequestTypes('LOGIN', [...defaultTypes, 'SET_SERVICES', 'SET_PREFERENCE', 'SET_LOCAL_AUTHENTICATED']);
export const SHARE = createRequestTypes('SHARE', ['SELECT_SERVER', 'SET_USER', 'SET_SETTINGS', 'SET_SERVER_INFO']);
export const SHARE = createRequestTypes('SHARE', ['SET_PARAMS']);
export const USER = createRequestTypes('USER', ['SET', 'CLEAR']);
export const ROOMS = createRequestTypes('ROOMS', [
...defaultTypes,
Expand Down
36 changes: 7 additions & 29 deletions app/actions/share.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,17 @@
import { Action } from 'redux';

import { IShareServer, IShareUser, TShareSettings } from '../reducers/share';
import { TShareParams } from '../reducers/share';
import { SHARE } from './actionsTypes';

interface IShareSelectServer extends Action {
server: IShareServer;
interface IShareSetParams extends Action {
params: TShareParams;
}

interface IShareSetSettings extends Action {
settings: TShareSettings;
}

interface IShareSetUser extends Action {
user: IShareUser;
}

export type TActionsShare = IShareSelectServer & IShareSetSettings & IShareSetUser;

export function shareSelectServer(server: IShareServer): IShareSelectServer {
return {
type: SHARE.SELECT_SERVER,
server
};
}

export function shareSetSettings(settings: TShareSettings): IShareSetSettings {
return {
type: SHARE.SET_SETTINGS,
settings
};
}
export type TActionsShare = IShareSetParams;

export function shareSetUser(user: IShareUser): IShareSetUser {
export function shareSetParams(params: TShareParams): IShareSetParams {
return {
type: SHARE.SET_USER,
user
type: SHARE.SET_PARAMS,
params
};
}
11 changes: 4 additions & 7 deletions app/containers/Avatar/AvatarContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ const AvatarContainer = ({
isStatic,
rid
}: IAvatar): React.ReactElement => {
const server = useSelector((state: IApplicationState) => state.share.server.server || state.server.server);
const serverVersion = useSelector((state: IApplicationState) => state.share.server.version || state.server.version);
const server = useSelector((state: IApplicationState) => state.server.server);
const serverVersion = useSelector((state: IApplicationState) => state.server.version);
const { id, token, username } = useSelector(
(state: IApplicationState) => ({
id: getUserSelector(state).id,
Expand All @@ -38,11 +38,8 @@ const AvatarContainer = ({
cdnPrefix: state.settings.CDN_PREFIX as string
}));
const blockUnauthenticatedAccess = useSelector(
(state: IApplicationState) =>
(state.share.settings?.Accounts_AvatarBlockUnauthenticatedAccess as boolean) ??
state.settings.Accounts_AvatarBlockUnauthenticatedAccess ??
true
);
(state: IApplicationState) => state.settings.Accounts_AvatarBlockUnauthenticatedAccess ?? true
) as boolean;

const { avatarETag } = useAvatarETag({ username, text, type, rid, id });

Expand Down
2 changes: 1 addition & 1 deletion app/containers/Button/Button.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Button from '.';

const buttonProps = {
title: 'Press me!',
type: 'primary',
type: 'primary' as const,
onPress: () => {},
testID: 'testButton'
};
Expand Down
4 changes: 2 additions & 2 deletions app/containers/Button/Button.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const onPressMock = jest.fn();

const testProps = {
title: 'Press me!',
type: 'primary',
type: 'primary' as const,
onPress: onPressMock,
testID: 'testButton',
initialText: 'Initial text',
Expand All @@ -19,7 +19,7 @@ const TestButton = ({ loading = false, disabled = false }) => (
<View>
<Button
title={testProps.title}
type={testProps.title}
type={testProps.type}
onPress={testProps.onPress}
testID={testProps.testID}
accessibilityLabel={testProps.title}
Expand Down
2 changes: 1 addition & 1 deletion app/containers/Button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import ActivityIndicator from '../ActivityIndicator';
interface IButtonProps extends PlatformTouchableProps {
title: string;
onPress: () => void;
type?: string; // primary | secondary
type?: 'primary' | 'secondary';
backgroundColor?: string;
loading?: boolean;
color?: string;
Expand Down
2 changes: 1 addition & 1 deletion app/containers/CallHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const CallHeader = ({ mic, cam, setCam, setMic, title, avatar, uid, name,
if (enabled) return { button: colors.buttonBackgroundSecondaryDisabled, icon: colors.strokeExtraDark };
return { button: 'transparent', icon: colors.strokeLight };
}
if (enabled) return { button: colors.strokeHighlight, icon: colors.surfaceLight };
if (enabled) return { button: colors.buttonBackgroundPrimaryDefault, icon: colors.surfaceLight };
return { button: 'transparent', icon: colors.strokeExtraDark };
};

Expand Down
2 changes: 1 addition & 1 deletion app/containers/Check.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const styles = StyleSheet.create({

const Check = React.memo(() => {
const { theme } = useTheme();
return <CustomIcon style={styles.icon} color={themes[theme].badgeBackgroundLevel2} size={22} name='check' />;
return <CustomIcon style={styles.icon} color={themes[theme].fontInfo} size={22} name='check' />;
});

export default Check;
Loading