Skip to content

Commit f66ac65

Browse files
committed
feat: expand DCC and IRCv3 parity
1 parent 94c9d99 commit f66ac65

19 files changed

Lines changed: 3441 additions & 14 deletions
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
name: Release Android
2+
3+
on:
4+
workflow_dispatch:
5+
6+
permissions:
7+
contents: write
8+
9+
jobs:
10+
build-android:
11+
name: Build Android APK
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- name: Checkout repository
16+
uses: actions/checkout@v4
17+
with:
18+
fetch-depth: 0
19+
20+
- name: Set up Java
21+
uses: actions/setup-java@v4
22+
with:
23+
distribution: temurin
24+
java-version: '17'
25+
26+
- name: Setup Flutter
27+
uses: subosito/flutter-action@v2
28+
with:
29+
channel: stable
30+
cache: true
31+
32+
- name: Install dependencies
33+
run: flutter pub get
34+
35+
- name: Build release APK
36+
run: flutter build apk --release
37+
38+
- name: Prepare APK artifact
39+
shell: bash
40+
run: |
41+
mkdir -p release-assets
42+
cp build/app/outputs/flutter-apk/app-release.apk release-assets/AndroidIRCx-Flutter-android.apk
43+
44+
- name: Upload workflow artifact
45+
uses: actions/upload-artifact@v4
46+
with:
47+
name: android-release-apk
48+
path: release-assets/AndroidIRCx-Flutter-android.apk
49+
50+
- name: Attach APK to GitHub release
51+
if: startsWith(github.ref, 'refs/tags/')
52+
uses: softprops/action-gh-release@v2
53+
with:
54+
tag_name: ${{ github.ref_name }}
55+
generate_release_notes: true
56+
files: release-assets/AndroidIRCx-Flutter-android.apk
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
name: Release Windows
2+
3+
on:
4+
workflow_dispatch:
5+
6+
permissions:
7+
contents: write
8+
9+
jobs:
10+
build-windows:
11+
name: Build Windows Package
12+
runs-on: windows-latest
13+
14+
steps:
15+
- name: Checkout repository
16+
uses: actions/checkout@v4
17+
with:
18+
fetch-depth: 0
19+
20+
- name: Setup Flutter
21+
uses: subosito/flutter-action@v2
22+
with:
23+
channel: stable
24+
cache: true
25+
26+
- name: Install dependencies
27+
run: flutter pub get
28+
29+
- name: Enable Windows desktop
30+
run: flutter config --enable-windows-desktop
31+
32+
- name: Build Windows release
33+
run: flutter build windows --release
34+
35+
- name: Package Windows release
36+
shell: pwsh
37+
run: |
38+
$artifactDir = "release-assets"
39+
New-Item -ItemType Directory -Force -Path $artifactDir | Out-Null
40+
$source = "build/windows/x64/runner/Release"
41+
$zipPath = Join-Path $artifactDir "AndroidIRCx-Flutter-windows.zip"
42+
if (Test-Path $zipPath) {
43+
Remove-Item $zipPath -Force
44+
}
45+
Compress-Archive -Path "$source/*" -DestinationPath $zipPath
46+
47+
- name: Upload workflow artifact
48+
uses: actions/upload-artifact@v4
49+
with:
50+
name: windows-release-zip
51+
path: release-assets/AndroidIRCx-Flutter-windows.zip
52+
53+
- name: Attach Windows package to GitHub release
54+
if: startsWith(github.ref, 'refs/tags/')
55+
uses: softprops/action-gh-release@v2
56+
with:
57+
tag_name: ${{ github.ref_name }}
58+
generate_release_notes: true
59+
files: release-assets/AndroidIRCx-Flutter-windows.zip

devtools_options.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
description: This file stores settings for Dart & Flutter DevTools.
2+
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
3+
extensions:
4+
- shared_preferences: true

lib/core/models/chat_tab.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
enum ChatTabType { server, channel, query, notice }
1+
enum ChatTabType { server, channel, query, notice, dcc }
22

33
class ChatTab {
44
const ChatTab({

lib/core/models/dcc_session.dart

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
enum DccSessionType { chat, send, unknown }
2+
3+
enum DccSessionStatus { pending, offering, connecting, connected, closed, failed }
4+
5+
class DccSession {
6+
const DccSession({
7+
required this.id,
8+
required this.tabId,
9+
required this.peerNick,
10+
required this.type,
11+
required this.status,
12+
required this.direction,
13+
this.filename,
14+
this.host,
15+
this.port,
16+
this.size,
17+
this.token,
18+
this.filePath,
19+
this.bytesTransferred = 0,
20+
this.error,
21+
});
22+
23+
final String id;
24+
final String tabId;
25+
final String peerNick;
26+
final DccSessionType type;
27+
final DccSessionStatus status;
28+
final String direction;
29+
final String? filename;
30+
final String? host;
31+
final int? port;
32+
final int? size;
33+
final String? token;
34+
final String? filePath;
35+
final int bytesTransferred;
36+
final String? error;
37+
38+
DccSession copyWith({
39+
String? id,
40+
String? tabId,
41+
String? peerNick,
42+
DccSessionType? type,
43+
DccSessionStatus? status,
44+
String? direction,
45+
String? filename,
46+
String? host,
47+
int? port,
48+
int? size,
49+
String? token,
50+
String? filePath,
51+
int? bytesTransferred,
52+
String? error,
53+
}) {
54+
return DccSession(
55+
id: id ?? this.id,
56+
tabId: tabId ?? this.tabId,
57+
peerNick: peerNick ?? this.peerNick,
58+
type: type ?? this.type,
59+
status: status ?? this.status,
60+
direction: direction ?? this.direction,
61+
filename: filename ?? this.filename,
62+
host: host ?? this.host,
63+
port: port ?? this.port,
64+
size: size ?? this.size,
65+
token: token ?? this.token,
66+
filePath: filePath ?? this.filePath,
67+
bytesTransferred: bytesTransferred ?? this.bytesTransferred,
68+
error: error ?? this.error,
69+
);
70+
}
71+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import 'dcc_file_store_stub.dart' if (dart.library.io) 'dcc_file_store_io.dart';
2+
3+
abstract class DccFileSink {
4+
void add(List<int> bytes);
5+
6+
Future<void> flush();
7+
8+
Future<void> close();
9+
}
10+
11+
typedef DccTempFile = ({String path, DccFileSink sink});
12+
typedef DccSourceFile = ({
13+
String path,
14+
String fileName,
15+
int size,
16+
Future<List<int>> Function() readAllBytes,
17+
});
18+
19+
Future<DccTempFile> createDccTempFile(String fileName) => createPlatformDccTempFile(fileName);
20+
21+
Future<DccSourceFile> openDccSourceFile(String path) => openPlatformDccSourceFile(path);
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import 'dart:io';
2+
3+
import 'dcc_file_store.dart';
4+
5+
class _IoDccFileSink implements DccFileSink {
6+
_IoDccFileSink(this._sink);
7+
8+
final IOSink _sink;
9+
10+
@override
11+
void add(List<int> bytes) {
12+
_sink.add(bytes);
13+
}
14+
15+
@override
16+
Future<void> close() => _sink.close();
17+
18+
@override
19+
Future<void> flush() => _sink.flush();
20+
}
21+
22+
Future<DccTempFile> createPlatformDccTempFile(String fileName) async {
23+
final sanitized = fileName.replaceAll(RegExp(r'[\\/:*?"<>|]'), '_');
24+
final path = '${Directory.systemTemp.path}/$sanitized';
25+
final file = File(path);
26+
final sink = file.openWrite(mode: FileMode.writeOnly);
27+
return (path: path, sink: _IoDccFileSink(sink));
28+
}
29+
30+
Future<DccSourceFile> openPlatformDccSourceFile(String path) async {
31+
final file = File(path);
32+
final exists = await file.exists();
33+
if (!exists) {
34+
throw FileSystemException('DCC source file does not exist.', path);
35+
}
36+
37+
final stat = await file.stat();
38+
return (
39+
path: file.path,
40+
fileName: file.uri.pathSegments.isEmpty ? file.path : file.uri.pathSegments.last,
41+
size: stat.size,
42+
readAllBytes: file.readAsBytes,
43+
);
44+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import 'dcc_file_store.dart';
2+
3+
Future<DccTempFile> createPlatformDccTempFile(String fileName) async {
4+
throw UnsupportedError('DCC file storage is only supported on IO platforms.');
5+
}
6+
7+
Future<DccSourceFile> openPlatformDccSourceFile(String path) async {
8+
throw UnsupportedError('Outgoing DCC SEND is only supported on IO platforms.');
9+
}

0 commit comments

Comments
 (0)