10 KiB
10 KiB
Contributing to My ActivityPub
Thank you for your interest in contributing to My ActivityPub! This document provides guidelines and instructions for contributing to the project.
Table of Contents
- Code of Conduct
- Getting Started
- Development Setup
- How to Contribute
- Coding Standards
- Commit Guidelines
- Pull Request Process
- Testing
- Documentation
Code of Conduct
Our Pledge
We are committed to providing a welcoming and inspiring community for all. Please be respectful and constructive in your interactions.
Expected Behavior
- Use welcoming and inclusive language
- Be respectful of differing viewpoints
- Accept constructive criticism gracefully
- Focus on what is best for the community
- Show empathy towards other community members
Getting Started
Prerequisites
Before you begin, ensure you have:
- Android Studio Hedgehog (2023.1.1) or newer
- JDK 11 or higher
- Git installed and configured
- Basic knowledge of Kotlin and Jetpack Compose
- Familiarity with the Mastodon API
Finding Issues to Work On
- Check the Issues page
- Look for issues labeled
good first issueorhelp wanted - Comment on the issue to let others know you're working on it
- Wait for maintainer approval before starting work
Development Setup
1. Fork and Clone
# Fork the repository on GitHub, then clone your fork
git clone https://github.com/YOUR_USERNAME/MyActivityPub.git
cd MyActivityPub
# Add the upstream repository
git remote add upstream https://github.com/ORIGINAL_OWNER/MyActivityPub.git
2. Create a Branch
# Create a new branch for your feature or bugfix
git checkout -b feature/your-feature-name
# Or for a bugfix
git checkout -b fix/bug-description
3. Set Up the Project
# Sync Gradle files
./gradlew build
# Run the app
./gradlew installDebug
How to Contribute
Reporting Bugs
Before creating a bug report:
- Check if the bug has already been reported
- Verify the bug exists in the latest version
- Collect relevant information (device, Android version, logs)
Bug Report Template:
**Description**
A clear description of the bug.
**Steps to Reproduce**
1. Step one
2. Step two
3. Step three
**Expected Behavior**
What should happen.
**Actual Behavior**
What actually happens.
**Environment**
- Device: [e.g., Pixel 6]
- Android Version: [e.g., Android 13]
- App Version: [e.g., 1.0.0]
**Logs**
Paste relevant logs here
**Screenshots**
If applicable, add screenshots.
Suggesting Enhancements
Enhancement suggestions are welcome! Please provide:
- Clear description of the enhancement
- Use cases and benefits
- Possible implementation approach
- Mockups or examples (if applicable)
Contributing Code
- Small Changes: Typos, bug fixes, small improvements can be submitted directly
- Large Changes: Open an issue first to discuss the change
- New Features: Must be discussed and approved before implementation
Coding Standards
Kotlin Style Guide
Follow the Official Kotlin Coding Conventions:
Naming
// Classes: PascalCase
class StatusCard { }
// Functions and variables: camelCase
fun loadTimeline() { }
val statusCount = 10
// Constants: UPPER_SNAKE_CASE
const val MAX_RETRIES = 3
// Private properties: leading underscore
private val _uiState = MutableStateFlow()
Formatting
// Use 4 spaces for indentation
class Example {
fun method() {
if (condition) {
// code here
}
}
}
// Line length: max 120 characters
// Break long function signatures:
fun longFunctionName(
parameter1: String,
parameter2: Int,
parameter3: Boolean
): ReturnType {
// implementation
}
Comments and Documentation
/**
* KDoc for public APIs
*
* @param userId The user identifier
* @return The user's timeline
*/
suspend fun getUserTimeline(userId: String): Result<List<Status>> {
// Implementation comments for complex logic
val result = apiService.getTimeline(userId)
return parseResult(result)
}
Compose Best Practices
// Composable function names: PascalCase
@Composable
fun StatusCard(status: Status, modifier: Modifier = Modifier) {
// Always provide Modifier parameter
// Default to Modifier
}
// Extract complex composables
@Composable
private fun StatusHeader(account: Account) {
// Smaller, focused components
}
// Use remember for expensive operations
val formattedDate = remember(timestamp) {
formatDate(timestamp)
}
// Use derivedStateOf for computed values
val isExpanded by remember {
derivedStateOf { height > maxHeight }
}
Architecture Guidelines
- Separation of Concerns: Each class has a single responsibility
- MVVM Pattern: Follow the established architecture
- Repository Pattern: All data access through repositories
- State Management: Use StateFlow for UI state
- Error Handling: Always handle errors gracefully
File Organization
app/src/main/java/com/manalejandro/myactivitypub/
├── MainActivity.kt # Entry point
├── data/
│ ├── api/ # API interfaces
│ ├── models/ # Data models
│ └── repository/ # Repository implementations
└── ui/
├── components/ # Reusable UI components
├── screens/ # Full screen composables
├── viewmodel/ # ViewModels
└── theme/ # Theme configuration
Commit Guidelines
Commit Message Format
<type>(<scope>): <subject>
<body>
<footer>
Types
feat: New featurefix: Bug fixdocs: Documentation changesstyle: Code style changes (formatting, missing semicolons, etc.)refactor: Code refactoringtest: Adding or updating testschore: Maintenance tasks
Examples
feat(timeline): add pull-to-refresh functionality
Implemented SwipeRefresh composable for the timeline screen.
Users can now pull down to refresh the timeline.
Closes #123
---
fix(statuscard): correct avatar image loading
Fixed issue where avatar images weren't loading correctly
due to missing Coil configuration.
Fixes #456
---
docs(readme): update installation instructions
Added more detailed steps for building the project
and troubleshooting common issues.
Guidelines
- Use present tense ("add feature" not "added feature")
- Keep subject line under 50 characters
- Capitalize the subject line
- Don't end the subject line with a period
- Use the body to explain what and why, not how
- Reference issues and pull requests in the footer
Pull Request Process
Before Submitting
- Test your changes: Ensure the app builds and runs
- Run lint checks:
./gradlew lint - Update documentation: If you changed APIs or features
- Add tests: For new features or bug fixes
- Update CHANGELOG: Add your changes to the unreleased section
Submitting a Pull Request
- Push your branch:
git push origin feature/your-feature-name
-
Create Pull Request on GitHub with:
- Clear title describing the change
- Detailed description of what and why
- Link to related issues
- Screenshots/recordings for UI changes
- Test instructions
-
PR Template:
## Description
Brief description of changes.
## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update
## Related Issues
Closes #123
## Testing
- [ ] Tested on physical device
- [ ] Tested on emulator
- [ ] Added unit tests
- [ ] Added UI tests
## Screenshots
[Add screenshots if applicable]
## Checklist
- [ ] Code follows style guidelines
- [ ] Self-reviewed the code
- [ ] Commented complex code
- [ ] Updated documentation
- [ ] No new warnings
- [ ] Added tests
- [ ] All tests pass
Review Process
- Maintainer will review your PR
- Address any requested changes
- Once approved, your PR will be merged
- Delete your branch after merge
Testing
Running Tests
# Run all tests
./gradlew test
# Run unit tests
./gradlew testDebugUnitTest
# Run instrumented tests
./gradlew connectedAndroidTest
Writing Tests
Unit Tests
class TimelineViewModelTest {
@Test
fun `loadTimeline updates state to Success on successful fetch`() = runTest {
// Arrange
val mockRepository = mock<MastodonRepository>()
val testStatuses = listOf(/* test data */)
whenever(mockRepository.getPublicTimeline())
.thenReturn(Result.success(testStatuses))
val viewModel = TimelineViewModel(mockRepository)
// Act
viewModel.loadTimeline()
// Assert
val state = viewModel.uiState.value
assertTrue(state is TimelineUiState.Success)
assertEquals(testStatuses, (state as TimelineUiState.Success).statuses)
}
}
Compose UI Tests
class StatusCardTest {
@get:Rule
val composeTestRule = createComposeRule()
@Test
fun statusCard_displays_username() {
val testStatus = Status(/* test data */)
composeTestRule.setContent {
StatusCard(status = testStatus)
}
composeTestRule
.onNodeWithText(testStatus.account.username)
.assertIsDisplayed()
}
}
Documentation
Code Documentation
- Add KDoc comments for all public APIs
- Comment complex algorithms
- Use meaningful variable and function names
- Update README.md for user-facing changes
Documentation Files
- README.md: User documentation, setup, features
- ARCHITECTURE.md: Architecture and design decisions
- API.md: API integration details
- CONTRIBUTING.md: This file
Questions?
If you have questions:
- Check existing documentation
- Search closed issues
- Ask in discussions
- Open a new issue with the
questionlabel
Recognition
Contributors will be recognized in:
- CONTRIBUTORS.md file
- Release notes
- Project README
Thank you for contributing to My ActivityPub! 🎉