Skip to content

Commit

Permalink
Merge branch 'main' into kevmo314/fix-analyzer-warning-1
Browse files Browse the repository at this point in the history
  • Loading branch information
SputNikPlop authored Aug 13, 2024
2 parents 9c94355 + cfbec06 commit 6f451bb
Show file tree
Hide file tree
Showing 34 changed files with 249 additions and 130 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v3

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/firebase-functions-pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Use Node.js 18
uses: actions/setup-node@v3
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/firebase-functions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
build_and_deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: w9jds/firebase-action@master
with:
args: functions:config:set alchemy.signingkey="$ALCHEMY_SIGNING_KEY" alchemy.ethsigningkey="$ALCHEMY_ETH_SIGNING_KEY" twitch.id="$TWITCH_CLIENT_ID" twitch.secret="$TWITCH_CLIENT_SECRET" express.secret="$EXPRESS_SESSION_SECRET" streamlabs.id="$STREAMLABS_CLIENT_ID" streamlabs.secret="$STREAMLABS_CLIENT_SECRET" streamelements.id="$STREAMELEMENTS_CLIENT_ID" streamelements.secret="$STREAMELEMENTS_CLIENT_SECRET"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/firebase-hosting-pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
if: "${{ github.event.pull_request.head.repo.full_name == github.repository }}"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: FirebaseExtended/action-hosting-deploy@v0
with:
repoToken: "${{ secrets.GITHUB_TOKEN }}"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/firebase-hosting.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
build_and_deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: w9jds/firebase-action@master
with:
args: deploy --only hosting
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/firebase.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
build_and_deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: w9jds/firebase-action@master
with:
args: deploy --only "firestore,database"
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/flutter-analyze.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Set up Flutter
uses: subosito/flutter-action@v1
- run: flutter pub get
- name: Run flutter analyze
uses: invertase/github-action-dart-analyzer@v1
uses: invertase/github-action-dart-analyzer@v3
2 changes: 1 addition & 1 deletion .github/workflows/flutter-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Set up Flutter
uses: subosito/flutter-action@v1
- run: flutter test
2 changes: 1 addition & 1 deletion ios/Flutter/Debug.xcconfig
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 changes: 1 addition & 1 deletion ios/Flutter/Release.xcconfig
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"
5 changes: 3 additions & 2 deletions lib/components/auth/companion_auth.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,16 @@ class _CompanionAuthWidgetState extends State<CompanionAuthWidget> {
@override
void initState() {
super.initState();
final user = Provider.of<UserModel>(context, listen: false);
final navigator = Navigator.of(context);
ProfilesAdapter.instance
.getCompanionAuthToken(sessionUuid: sessionUuid)
.then((token) async {
final user = Provider.of<UserModel>(context, listen: false);
await user.signIn(token);
if (!mounted) {
return;
}
Navigator.of(context).pop();
navigator.pop();
});
}

Expand Down
33 changes: 17 additions & 16 deletions lib/components/autocomplete.dart
Original file line number Diff line number Diff line change
Expand Up @@ -91,28 +91,29 @@ class _AutocompleteWidgetState extends State<AutocompleteWidget> {
return Container();
}
return Row(
mainAxisAlignment: MainAxisAlignment.start,
children: (snapshot.data as List<Emote>)
.where((emote) => emote.code
.toLowerCase()
.startsWith(lastToken.toLowerCase()))
.take(MediaQuery.of(context).size.width ~/ 48)
.map((emote) {
return Expanded(
child: IconButton(
tooltip: emote.code,
onPressed: () {
widget.controller.text = "${text.substring(
0,
text.length - lastToken.length,
)}${emote.code} ";
// move cursor position
widget.controller.selection =
TextSelection.fromPosition(TextPosition(
offset: widget.controller.text.length));
},
splashRadius: 24,
icon: Image(image: ResilientNetworkImage(emote.uri))),
);
return IconButton(
tooltip: emote.code,
onPressed: () {
widget.controller.text = "${text.substring(
0,
text.length - lastToken.length,
)}${emote.code} ";
// move cursor position
widget.controller.selection = TextSelection.fromPosition(
TextPosition(offset: widget.controller.text.length));
},
splashRadius: 24,
icon: Image(
width: 24,
height: 24,
image: ResilientNetworkImage(emote.uri)));
}).toList(),
);
},
Expand Down
19 changes: 6 additions & 13 deletions lib/components/emote_picker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -178,12 +178,14 @@ class _TabbedEmotePickerWidget extends StatelessWidget {

class EmotePickerWidget extends StatelessWidget {
final void Function(Emote?) onEmoteSelected;
final List<Emote> emotes;
final Channel channel;

const EmotePickerWidget({
super.key,
required this.onEmoteSelected,
required this.channel,
required this.emotes,
});

@override
Expand All @@ -199,19 +201,10 @@ class EmotePickerWidget extends StatelessWidget {
},
child: SizedBox(
height: min(48 * rowNumber.toDouble(), maxHeight),
child: FutureBuilder<List<Emote>>(
future: getEmotes(channel),
initialData: const [],
builder: (context, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return const Center(child: CircularProgressIndicator());
}
return _TabbedEmotePickerWidget(
emotes: snapshot.data!,
onEmoteSelected: onEmoteSelected,
channel: channel,
);
},
child: _TabbedEmotePickerWidget(
emotes: emotes,
onEmoteSelected: onEmoteSelected,
channel: channel,
),
),
);
Expand Down
153 changes: 135 additions & 18 deletions lib/components/message_input.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,119 @@ import 'package:rtchat/components/image/resilient_network_image.dart';
import 'package:rtchat/models/adapters/actions.dart';
import 'package:rtchat/models/channels.dart';
import 'package:rtchat/models/commands.dart';
import 'package:rtchat/models/messages/tokens.dart';
import 'package:rtchat/models/messages/twitch/emote.dart';
import 'package:rtchat/models/style.dart';
import 'package:rtchat/share_channel.dart';

class EmoteTextEditingController extends TextEditingController {
List<Emote> emotes;

EmoteTextEditingController(this.emotes);

Iterable<MessageToken> tokenizeEmotes(
String message, List<Emote> emotes) sync* {
final emotesMap = {for (final emote in emotes) emote.code: emote};
var lastParsedStart = 0;
for (var start = 0; start < message.length;) {
final end = message.indexOf(" ", start);
final token =
end == -1 ? message.substring(start) : message.substring(start, end);
final emote = emotesMap[token.trim()];
if (emote != null) {
if (lastParsedStart != start) {
yield TextToken(message.substring(lastParsedStart, start));
}
yield EmoteToken(url: emote.uri, code: emote.code);
lastParsedStart = end == -1 ? message.length : end;
}
start = end == -1 ? message.length : end + 1;
}
if (lastParsedStart != message.length) {
yield TextToken(message.substring(lastParsedStart));
}
}

static Iterable<InlineSpan> render(
BuildContext context, StyleModel styleModel, MessageToken token) sync* {
if (token is TextToken) {
yield TextSpan(text: token.text);
} else if (token is EmoteToken) {
yield* [
TextSpan(text: "\u200B" * (token.code.length - 1)),
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Tooltip(
message: token.code,
preferBelow: false,
child: Image(
height: styleModel.fontSize,
image: ResilientNetworkImage(token.url),
errorBuilder: (context, error, stackTrace) =>
Text(token.code))))
];
} else {
throw Exception("invalid token");
}
}

@override
TextSpan buildTextSpan({
required BuildContext context,
TextStyle? style,
required bool withComposing,
}) {
assert(!value.composing.isValid ||
!withComposing ||
value.isComposingRangeValid);
// If the composing range is out of range for the current text, ignore it to
// preserve the tree integrity, otherwise in release mode a RangeError will
// be thrown and this EditableText will be built with a broken subtree.
final bool composingRegionOutOfRange =
!value.isComposingRangeValid || !withComposing;

final styleModel = Provider.of<StyleModel>(context, listen: false);

if (composingRegionOutOfRange) {
return TextSpan(
style: style,
children: tokenizeEmotes(text, emotes)
.expand((token) => render(context, styleModel, token))
.toList());
}

final TextStyle composingStyle =
style?.merge(const TextStyle(decoration: TextDecoration.underline)) ??
const TextStyle(decoration: TextDecoration.underline);

return TextSpan(
style: style,
children: <TextSpan>[
TextSpan(
children:
tokenizeEmotes(value.composing.textBefore(value.text), emotes)
.expand((token) => render(context, styleModel, token))
.toList()),
TextSpan(
style: composingStyle,
text: value.composing.textInside(value.text),
),
TextSpan(
children:
tokenizeEmotes(value.composing.textAfter(value.text), emotes)
.expand((token) => render(context, styleModel, token))
.toList()),
],
);
}
}

class MessageInputWidget extends StatefulWidget {
final Channel channel;
final List<Emote> emotes; // TODO: decouple this from the twitch emote model.

const MessageInputWidget({super.key, required this.channel});
const MessageInputWidget(
{super.key, required this.channel, required this.emotes});

@override
State<MessageInputWidget> createState() => _MessageInputWidgetState();
Expand All @@ -38,7 +145,7 @@ const _greyscale = ColorFilter.matrix([
]);

class _MessageInputWidgetState extends State<MessageInputWidget> {
final _textEditingController = TextEditingController();
late final EmoteTextEditingController _textEditingController;
final _chatInputFocusNode = FocusNode();
var _isEmotePickerVisible = false;
var _isKeyboardVisible = false;
Expand All @@ -51,6 +158,7 @@ class _MessageInputWidgetState extends State<MessageInputWidget> {
void initState() {
super.initState();
final keyboardVisibilityController = KeyboardVisibilityController();
_textEditingController = EmoteTextEditingController(widget.emotes);
// Subscribe to keyboard visibility changes.
keyboardSubscription =
keyboardVisibilityController.onChange.listen((visible) {
Expand All @@ -66,6 +174,12 @@ class _MessageInputWidgetState extends State<MessageInputWidget> {
..getSharedText().then(_handleSharedData);
}

@override
void didUpdateWidget(MessageInputWidget oldWidget) {
super.didUpdateWidget(oldWidget);
_textEditingController.emotes = widget.emotes;
}

// Handles any shared data we may receive.
void _handleSharedData(String sharedData) {
setState(() {
Expand Down Expand Up @@ -134,21 +248,6 @@ class _MessageInputWidgetState extends State<MessageInputWidget> {
]);
}

Widget _buildEmotePicker(BuildContext context) {
return EmotePickerWidget(
channel: widget.channel,
onEmoteSelected: (emote) {
if (emote == null) {
setState(() {
_isEmotePickerVisible = false;
});
return;
}
_textEditingController.text =
"${_textEditingController.text} ${emote.code}";
});
}

@override
Widget build(BuildContext context) {
return Material(
Expand Down Expand Up @@ -252,7 +351,25 @@ class _MessageInputWidgetState extends State<MessageInputWidget> {
),
),
),
_isEmotePickerVisible ? _buildEmotePicker(context) : Container(),
_isEmotePickerVisible
? EmotePickerWidget(
channel: widget.channel,
emotes: widget.emotes,
onEmoteSelected: (emote) {
if (emote == null) {
setState(() {
_isEmotePickerVisible = false;
});
return;
}
if (_textEditingController.text.isNotEmpty) {
_textEditingController.text =
"${_textEditingController.text} ${emote.code} ";
} else {
_textEditingController.text = "${emote.code} ";
}
})
: Container(),
]),
);
}
Expand Down
Loading

0 comments on commit 6f451bb

Please sign in to comment.