This commit is contained in:
OkaykOrhmn 2024-09-22 15:48:24 +03:30
parent 20643b6075
commit 34b9d46d71
16 changed files with 383 additions and 379 deletions

View File

@ -7,6 +7,7 @@ import 'package:didvan/models/ai/files_model.dart';
import 'package:didvan/services/storage/storage.dart';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
import 'package:http_parser/http_parser.dart';
import 'package:mime/mime.dart';
import 'package:path/path.dart' as p;
import 'package:http_parser/http_parser.dart' as parser;
@ -36,26 +37,11 @@ class AiApiService {
request.fields['edit'] = edite.toString().toLowerCase();
}
if (file != null) {
// final file = file;
// final filePath = file.path;
// final mimeType = filePath != null ? lookupMimeType(filePath) : null;
// final contentType = mimeType != null ? MediaType.parse(mimeType) : null;
// final fileReadStream = file.readStream;
// if (fileReadStream == null) {
// throw Exception('Cannot read file from null stream');
// }
// final stream = http.ByteStream(fileReadStream);
// final multipartFile = http.MultipartFile(
// 'file',
// stream,
// file.size,
// filename: file.name,
// contentType: contentType,
// );
// request.files.add(multipartFile);
Uint8List bytes;
String filename;
String mimeType;
if (kIsWeb) {
// For web platform
if (file.bytes != null) {
bytes = file.bytes!;
} else {
@ -63,42 +49,24 @@ class AiApiService {
final http.Response audioResponse = await http.get(audioUri);
bytes = audioResponse.bodyBytes;
}
request.files.add(http.MultipartFile.fromBytes(
'file',
bytes,
filename: file.isAudio()
? 'wav'
: file.isImage()
? 'png'
: 'pdf', // You can set a filename here
contentType: parser.MediaType.parse(file.isAudio()
? 'audio/mpeg'
: file.isImage()
? 'image/png'
: 'application/pdf'), // Set the MIME type
) // Use MediaType.parse to parse the MIME type
);
} else {
int length = 0;
try {
length = await file.main.length();
// ...
} catch (e) {
// Handle the error or return an error response
}
String? mimeType = lookupMimeType(
file.path); // Use MIME type instead of file extension
mimeType ??= 'application/octet-stream';
if (mimeType.startsWith('audio')) {
mimeType = 'audio/${p.extension(file.path).replaceAll('.', '')}';
}
request.files.add(
http.MultipartFile('file', file.main.readAsBytes().asStream(), length,
filename: file.basename,
contentType: parser.MediaType.parse(
mimeType)), // Use MediaType.parse to parse the MIME type
);
// For other platforms
bytes = await file.main.readAsBytes();
}
filename = file.basename;
mimeType = file.isAudio()
? 'audio/m4a'
: file.isImage()
? 'image/png'
: 'application/pdf';
request.files.add(http.MultipartFile.fromBytes(
'file',
bytes,
filename: filename,
contentType: MediaType.parse(mimeType),
));
}
// print("req: ${request.files}");

View File

@ -22,11 +22,34 @@ class MediaService {
static Duration? get duration => audioPlayer.duration;
static Future<Duration?> getDuration({required String src}) async {
final ap = AudioPlayer();
Duration? duration;
try {
if (src.startsWith('/uploads')) {
duration = await ap.setUrl(
'${RequestHelper.baseUrl + src}?accessToken=${RequestService.token}');
} else if (src.startsWith('blob:')) {
duration = await ap.setAudioSource(AudioSource.uri(Uri.parse(src)));
} else {
duration = await ap.setFilePath(src);
}
} catch (e) {
print('Error setting audio source: $e');
} finally {
await ap.dispose();
}
return duration;
}
static Future<void> handleAudioPlayback({
required dynamic audioSource,
required int id,
bool isNetworkAudio = true,
bool isVoiceMessage = true,
bool isBlob = false,
void Function(bool isNext)? onTrackChanged,
}) async {
try {
@ -67,15 +90,19 @@ class MediaService {
},
);
} else {
audioPlayer.setFilePath(
audioSource,
tag: isVoiceMessage
? null
: {
"artist": 'استودیو دیدوان',
"title": currentPodcast?.title ?? '',
},
);
if (isBlob) {
audioPlayer.setAudioSource(AudioSource.uri(Uri.parse(audioSource)));
} else {
audioPlayer.setFilePath(
audioSource,
tag: isVoiceMessage
? null
: {
"artist": 'استودیو دیدوان',
"title": currentPodcast?.title ?? '',
},
);
}
}
await audioPlayer.play();
// await audioPlayer.open(

View File

@ -0,0 +1,65 @@
import 'package:didvan/services/network/request.dart';
import 'package:didvan/services/network/request_helper.dart';
import 'package:just_audio/just_audio.dart';
class VoiceService {
static final audioPlayer = AudioPlayer();
static int? index;
static ConcatenatingAudioSource? _playlist;
VoiceService({required final List<AudioSource> audios}) {
_playlist = ConcatenatingAudioSource(
// Start loading next item just before reaching it
useLazyPreparation: true,
// Customise the shuffle algorithm
shuffleOrder: DefaultShuffleOrder(),
// Specify the playlist items
children: audios,
);
}
static Future<Duration?> getDuration({
required String src,
}) async {
if (src.startsWith('/uploads')) {
return await audioPlayer.setUrl(
'${RequestHelper.baseUrl + src}?accessToken=${RequestService.token}');
} else if (src.startsWith('blob:')) {
return await audioPlayer.setAudioSource(AudioSource.uri(Uri.parse(src)));
} else {
return await audioPlayer.setFilePath(src);
}
}
static Future<bool> voiceHelper({required int index}) async {
try {
if (VoiceService.index == index) {
if (audioPlayer.playerState ==
PlayerState(true, ProcessingState.ready)) {
await audioPlayer.pause();
} else {
await audioPlayer.play();
}
return true;
}
VoiceService.index = index;
await audioPlayer.setAudioSource(_playlist!,
initialIndex: index, initialPosition: Duration.zero);
await audioPlayer
.setLoopMode(LoopMode.off); // Set playlist to loop (off|all|one)
await audioPlayer.play();
return true;
} catch (e) {
resetVoicePlayer();
// rethrow;
return false;
}
}
static Future<void> resetVoicePlayer() async {
index = null;
await audioPlayer.stop();
}
}

View File

@ -23,6 +23,7 @@ import 'package:didvan/views/widgets/didvan/icon_button.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/marquee_text.dart';
import 'package:didvan/views/widgets/skeleton_image.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@ -234,15 +235,13 @@ class _AiChatPageState extends State<AiChatPage> {
const SizedBox(
height: 24,
),
Container(
SizedBox(
width: MediaQuery.sizeOf(context).height / 5,
height: MediaQuery.sizeOf(context).height / 5,
decoration: BoxDecoration(
borderRadius: DesignConfig.highBorderRadius,
color: Theme.of(context).colorScheme.focused),
padding: const EdgeInsets.all(12),
child: CachedNetworkImage(
imageUrl: widget.args.bot.image.toString(),
child: ClipOval(
child: CachedNetworkImage(
imageUrl: widget.args.bot.image.toString(),
),
),
),
const SizedBox(
@ -425,7 +424,9 @@ class _AiChatPageState extends State<AiChatPage> {
children: [
if (message.role.toString().contains('user') &&
file != null)
(file.isAudio() && (!kIsWeb && !Platform.isIOS))
(file.isAudio()
// && (!kIsWeb && !Platform.isIOS)
)
? Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16, vertical: 8),

View File

@ -234,7 +234,9 @@ class _AiMessageBarState extends State<AiMessageBar> {
}
state.file = kIsWeb
? FilesModel(pickedFile.path,
image: true, audio: false)
name: pickedFile.name,
image: true,
audio: false)
: FilesModel(file!.path,
image: true, audio: false);
openAttach = false;
@ -244,44 +246,45 @@ class _AiMessageBarState extends State<AiMessageBar> {
);
},
),
if (!kIsWeb && !Platform.isIOS)
if (historyState.bot!.attachmentType!
.contains('audio'))
attachBtn(
title: "صوت",
icon: CupertinoIcons.music_note_2,
color: Colors.indigoAccent,
click: () async {
MediaService.onLoadingPickFile(context);
// if (!kIsWeb && !Platform.isIOS)
if (historyState.bot!.attachmentType!
.contains('audio'))
attachBtn(
title: "صوت",
icon: CupertinoIcons.music_note_2,
color: Colors.indigoAccent,
click: () async {
MediaService.onLoadingPickFile(context);
FilePickerResult? result =
await MediaService.pickAudioFile();
if (result != null) {
if (kIsWeb) {
Uint8List bytes = result.files.first
.bytes!; // Access the bytes property
FilePickerResult? result =
await MediaService.pickAudioFile();
if (result != null) {
if (kIsWeb) {
Uint8List? bytes = result.files.first
.bytes; // Access the bytes property
String? name = result.files.first.name;
File file = File.fromRawPath(bytes);
state.file = FilesModel(file.path,
name: result.files.first.name,
bytes: bytes,
audio: true,
image: false);
} else {
state.file = FilesModel(
result.files.single.path!,
audio: true,
image: false);
}
openAttach = false;
state.file = FilesModel(
'', // No need for a file path on web
name: name,
bytes: bytes,
audio: true,
image: false,
);
} else {
state.file = FilesModel(
result.files.single.path!,
audio: true,
image: false);
}
await Future.delayed(
Duration.zero,
() => ActionSheetUtils(context).pop(),
);
},
)
openAttach = false;
}
await Future.delayed(
Duration.zero,
() => ActionSheetUtils(context).pop(),
);
},
)
],
),
)),
@ -320,10 +323,12 @@ class _AiMessageBarState extends State<AiMessageBar> {
Padding(
padding: const EdgeInsets.only(
bottom: 8.0),
child: (!kIsWeb &&
child: (
// !kIsWeb &&
snapshot.hasData &&
snapshot.data! !=
RecordState.stop)
snapshot.data! !=
RecordState
.stop)
? MessageBarBtn(
enable: true,
icon: DidvanIcons
@ -334,6 +339,8 @@ class _AiMessageBarState extends State<AiMessageBar> {
state.file = FilesModel(
path.toString(),
name:
'${DateTime.now().millisecondsSinceEpoch ~/ 1000}.m4a',
isRecorded: true,
audio: true,
image: false);
@ -342,11 +349,11 @@ class _AiMessageBarState extends State<AiMessageBar> {
state.update();
},
)
: (!kIsWeb &&
!Platform
.isIOS) &&
widget.bot
.attachmentType!
:
// (!kIsWeb &&
// !Platform
// .isIOS) &&
widget.bot.attachmentType!
.contains(
'audio') &&
value.isEmpty &&
@ -373,7 +380,7 @@ class _AiMessageBarState extends State<AiMessageBar> {
path = p.join(
downloadDir
.path,
'${DateTime.now().millisecondsSinceEpoch ~/ 1000}.wav');
'${DateTime.now().millisecondsSinceEpoch ~/ 1000}.m4a');
}
record.start(

View File

@ -2,11 +2,17 @@
import 'dart:math';
import 'package:audio_video_progress_bar/audio_video_progress_bar.dart';
import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/services/media/media.dart';
import 'package:didvan/services/network/request.dart';
import 'package:didvan/services/network/request_helper.dart';
import 'package:didvan/utils/date_time.dart';
import 'package:didvan/views/ai/widgets/message_bar_btn.dart';
import 'package:didvan/views/direct/widgets/message.dart';
import 'package:didvan/views/widgets/audio/audio_slider.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@ -25,278 +31,158 @@ class AudioWave extends StatefulWidget {
}
class _AudioWaveState extends State<AudioWave> {
final int itemCount = 35;
final AudioPlayer audioPlayer = AudioPlayer()..setPitch(1);
final ValueNotifier<List<double>> randoms = ValueNotifier([]);
final ValueNotifier<List<double>> randomsDisable = ValueNotifier([]);
bool loading = false;
Duration? totalDuration;
Duration totalDuration = Duration.zero;
double currentPosition = 0;
bool loading = true;
bool faile = false;
bool onChanging = false;
final int id =
DateTime.now().millisecondsSinceEpoch ~/ 1000 * Random().nextInt(1000000);
@override
void initState() {
super.initState();
try {
WidgetsBinding.instance.addPostFrameCallback((_) async {
await init();
WidgetsBinding.instance.addPostFrameCallback((_) async {
await MediaService.getDuration(src: widget.file).then((duration) {
setState(() {
totalDuration = duration;
print(totalDuration!.inSeconds);
});
});
listeners();
} catch (e) {
if (kDebugMode) {
print('Error occurred: $e');
}
rethrow;
}
}
void setRandoms() {
for (var i = 0; i < itemCount; i++) {
randoms.value.add(0);
randomsDisable.value.add(1 + Random().nextDouble() * (38 - 1));
}
}
Future<void> init() async {
try {
final path = widget.file;
if (widget.file.startsWith('blob:')) {
totalDuration = await audioPlayer
.setAudioSource(AudioSource.uri(Uri.parse(path))) ??
Duration.zero;
} else if (widget.file.startsWith('/uploads')) {
AudioSource.uri(Uri.parse(
'${RequestHelper.baseUrl + path}?accessToken=${RequestService.token}'));
totalDuration = await audioPlayer.setUrl(
'${RequestHelper.baseUrl + path}?accessToken=${RequestService.token}') ??
Duration.zero;
} else {
totalDuration = await audioPlayer.setFilePath(path) ?? Duration.zero;
}
setRandoms();
setState(() {
loading = false;
});
} catch (e) {
setState(() {
faile = true;
loading = false;
});
if (kDebugMode) {
print('Error occurred: $e');
}
}
}
Future<void> listeners() async {
audioPlayer.positionStream.listen((position) async {
if (randomsDisable.value.isEmpty || onChanging) return;
try {
for (var i = 0; i < itemCount; i++) {
if (i < randomsDisable.value.length &&
i <
((position.inMilliseconds * 40) /
totalDuration.inMilliseconds)) {
final ran = randomsDisable.value[i];
randoms.value[i] = ran;
} else {
randoms.value[i] = 0;
}
}
} catch (e) {
e.printError(info: 'listener Error');
}
if (position.inMilliseconds >= totalDuration.inMilliseconds) {
audioPlayer.stop();
audioPlayer.seek(Duration.zero);
}
});
// listeners();
}
@override
void dispose() async {
await audioPlayer.stop();
audioPlayer.dispose();
void dispose() {
MediaService.resetAudioPlayer();
super.dispose();
}
@override
Widget build(BuildContext context) {
return SizedBox(
height: 46,
child: loading
? Padding(
padding:
EdgeInsets.symmetric(vertical: widget.loadingPaddingSize),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(
5,
(index) => SpinKitWave(
color: Theme.of(context)
.colorScheme
.primary
.withOpacity(0.4),
size: 32,
itemCount: 10,
))),
)
: Directionality(
textDirection: TextDirection.ltr,
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
StreamBuilder<PlayerState>(
stream: audioPlayer.playerStateStream,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const SizedBox();
}
return MessageBarBtn(
enable: true,
icon: faile
? DidvanIcons.refresh_solid
: snapshot.data!.playing
? DidvanIcons.pause_solid
: DidvanIcons.play_solid,
click: () async {
if (faile) {
randoms.value.clear();
randomsDisable.value.clear();
setState(() {
loading = true;
faile = false;
});
init();
return;
}
if (snapshot.data!.playing) {
await audioPlayer.pause();
} else {
await audioPlayer.play();
}
},
);
}),
faile
? const Padding(
padding: EdgeInsets.symmetric(horizontal: 8.0),
child: DidvanText(
'خطا در بارگزاری فایل صوتی',
fontSize: 12,
),
)
: StreamBuilder<Duration>(
stream: audioPlayer.positionStream,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const SizedBox();
}
currentPosition =
snapshot.data!.inMilliseconds.toDouble();
return Expanded(
child: Row(
children: [
ValueListenableBuilder(
valueListenable: randoms,
builder: (context, value, child) {
return Expanded(
child: Stack(
alignment: Alignment.center,
children: [
noise(values: randoms.value),
noise(
values: randomsDisable.value,
color: Theme.of(context)
.colorScheme
.primary
.withOpacity(0.4)),
if (totalDuration != Duration.zero)
Positioned.fill(
child: Opacity(
opacity: 0,
child: Theme(
data: Theme.of(context)
.copyWith(
sliderTheme:
SliderThemeData(
thumbShape:
SliderComponentShape
.noThumb,
minThumbSeparation: 0,
),
splashColor:
Colors.transparent,
),
child: Slider(
value: currentPosition,
max: totalDuration
.inMilliseconds
.toDouble() +
const Duration(
milliseconds:
10)
.inMilliseconds
.toDouble(),
onChangeStart: (value) {
// audioPlayer.pause();
},
onChanged: (value) {
// for (var i = 0;
// i < itemCount;
// i++) {
// if (i <
// ((value * 40) /
// totalDuration
// .inMilliseconds)) {
// final ran =
// randomsDisable
// .value[i];
// randoms.value[i] =
// ran;
// } else {
// randoms.value[i] = 0;
// }
// }
setState(() {
currentPosition = value;
});
},
onChangeEnd: (value) {
audioPlayer.seek(Duration(
milliseconds:
value.round()));
audioPlayer.play();
},
),
),
),
),
],
));
},
height: 46,
child: Directionality(
textDirection: TextDirection.ltr,
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
StreamBuilder<PlayerState>(
stream: MediaService.audioPlayer.playerStateStream,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const SizedBox();
}
// if (snapshot.data!.processingState ==
// ProcessingState.completed) {
// MediaService.audioPlayer.pause();
// MediaService.audioPlayer.seek(Duration.zero);
// }
print(snapshot.data);
return MessageBarBtn(
enable: true,
icon: snapshot.data!.playing &&
snapshot.data!.processingState !=
ProcessingState.completed &&
MediaService.audioPlayerTag == 'message-$id'
? DidvanIcons.pause_solid
: DidvanIcons.play_solid,
click: () async {
await MediaService.handleAudioPlayback(
audioSource: widget.file,
id: id,
isNetworkAudio: widget.file.startsWith('/uploads'),
isVoiceMessage: true,
isBlob: widget.file.startsWith('blob:'));
MediaService.audioPlayerTag = 'message-$id';
},
);
}),
StreamBuilder<Duration>(
stream: MediaService.audioPlayer.positionStream,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const SizedBox();
}
print("Position: ${snapshot.data}");
return Expanded(
child: totalDuration == null
? Padding(
padding: EdgeInsets.symmetric(
vertical: widget.loadingPaddingSize),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(
5,
(index) => SpinKitWave(
color: Theme.of(context)
.colorScheme
.primary
.withOpacity(0.4),
size: 32,
itemCount: 10,
))),
)
: Row(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 12.0),
child: Expanded(
child: ProgressBar(
thumbColor:
Theme.of(context).colorScheme.title,
progressBarColor: DesignConfig.isDark
? Theme.of(context).colorScheme.title
: Theme.of(context)
.colorScheme
.primary,
baseBarColor:
Theme.of(context).colorScheme.border,
bufferedBarColor:
Theme.of(context).colorScheme.splash,
total: totalDuration!,
progress: MediaService.audioPlayerTag ==
'message-$id'
? snapshot.data ?? Duration.zero
: Duration.zero,
thumbRadius: 6,
barHeight: 3,
// timeLabelTextStyle: TextStyle(
// fontSize: showTimer ? null : 0,
// height: showTimer ? 3 : 0,
// color:
// Theme.of(context).colorScheme.text,
// fontFamily:
// DesignConfig.fontFamily.replaceAll(
// '-FA',
// '',
// ),
// ),
onSeek: (value) {
if (MediaService.audioPlayerTag ==
'message-$id') {
MediaService.audioPlayer.seek(
Duration(
milliseconds:
value.inMilliseconds));
}
},
),
),
DidvanText(
DateTimeUtils.normalizeTimeDuration(
snapshot.data! == Duration.zero
? totalDuration
: snapshot.data!)),
],
),
),
);
},
)
],
),
),
);
DidvanText(DateTimeUtils.normalizeTimeDuration(
snapshot.data ?? Duration.zero)),
],
),
);
},
)
],
),
));
}
Row noise({required final List<double> values, final Color? color}) {

View File

@ -25,6 +25,6 @@ class DownloadableAudioWidget extends StatelessWidget {
Widget build(BuildContext context) {
return SizedBox(
width: MediaQuery.sizeOf(context).width / 1.6,
child: AudioWave(file: audioUrl != null ? audioUrl! : audioFile!.path));
child: AudioWave(file: audioUrl ?? audioFile!.path));
}
}

View File

@ -4,6 +4,7 @@ import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/models/message_data/message_data.dart';
import 'package:didvan/utils/date_time.dart';
import 'package:didvan/views/direct/direct_state.dart';
import 'package:didvan/views/direct/widgets/audio_widget.dart';
import 'package:didvan/views/direct/widgets/downloadable_audio_widget.dart';
import 'package:didvan/views/widgets/didvan/divider.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
@ -84,7 +85,7 @@ class Message extends StatelessWidget {
children: [
if (message.text != null) DidvanText(message.text!),
if (message.audio != null || message.audioFile != null)
DownloadableAudioWidget(
AudioWidget(
audioFile: message.audioFile,
audioUrl: message.audio,
id: message.id,

View File

@ -3,6 +3,7 @@ import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/views/ai/widgets/message_bar_btn.dart';
import 'package:didvan/views/direct/direct_state.dart';
import 'package:didvan/views/direct/widgets/audio_widget.dart';
import 'package:didvan/views/direct/widgets/downloadable_audio_widget.dart';
import 'package:didvan/views/widgets/didvan/icon_button.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
@ -227,7 +228,7 @@ class _RecordChecking extends StatelessWidget {
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: DownloadableAudioWidget(
child: AudioWidget(
audioFile: state.recordedFile!,
id: 0,
// deleteClidk: () => state.deleteRecordedFile,

View File

@ -134,11 +134,10 @@ class HomeState extends CoreProvier {
final categoryFilters = [
CategoryData(id: 1, label: 'پویش افق'),
CategoryData(id: 2, label: 'دنیای فولاد'),
CategoryData(id: 3, label: 'ویدیوکست'),
CategoryData(id: 4, label: 'پادکست'),
CategoryData(id: 3, label: 'استودیو آینده'),
CategoryData(id: 5, label: 'رادارهای استراتژیک'),
CategoryData(id: 6, label: 'سها'),
CategoryData(id: 7, label: 'هوشان'),
CategoryData(id: 7, label: 'اینفوگرافی'),
];
void refresh() async {

View File

@ -1,3 +1,5 @@
import 'dart:math';
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/models/overview_data.dart';
@ -68,9 +70,44 @@ class SearchResultItem extends StatelessWidget {
if (item.type == 'delphi') {
return DidvanIcons.saha_light;
}
if (item.type == 'infography') {
return DidvanIcons.infography_regular;
}
return DidvanIcons.radar_light;
}
void _openInteractiveViewer(BuildContext context, String image) {
showDialog(
context: context,
builder: (context) => Stack(
children: [
Positioned.fill(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: InteractiveViewer(
child: Center(
child: SkeletonImage(
width: min(MediaQuery.of(context).size.width,
MediaQuery.of(context).size.height),
imageUrl: image,
),
),
),
),
),
Positioned(
right: 24,
top: 24,
child: Container(
decoration: const BoxDecoration(
color: Colors.white, shape: BoxShape.circle),
child: const BackButton()),
),
],
),
);
}
@override
Widget build(BuildContext context) {
return DidvanCard(
@ -90,7 +127,9 @@ class SearchResultItem extends StatelessWidget {
);
return;
}
if (_targetPageRouteName == null && item.link != null) {
if (item.type == "infography") {
_openInteractiveViewer(context, item.image);
} else if (_targetPageRouteName == null && item.link != null) {
AppInitializer.openWebLink(
context,
'${item.link!}?accessToken=${RequestService.token}',

View File

@ -87,7 +87,7 @@ class _PodcastsState extends State<Podcasts> {
Padding(
padding: const EdgeInsets.all(16.0),
child: SearchField(
title: state.videosSelected ? 'ویدیو' : 'پادکست',
title: state.videosSelected ? 'ویدیوکست' : 'پادکست',
onChanged: _onChanged,
focusNode: _focusNode,
isFiltered: false,

View File

@ -30,7 +30,7 @@ class StudioTabBar extends StatelessWidget {
child: _StudioTypeButton(
icon: DidvanIcons.video_solid,
selectedColor: Theme.of(context).colorScheme.focusedBorder,
title: 'ویدیو',
title: 'ویدیوکست',
onTap: () => state.videosSelected = true,
isSelected: state.videosSelected,
),

View File

@ -105,10 +105,10 @@ class _RadarState extends State<Radar> {
),
),
),
if (state.searching || state.filtering)
if (state.isColapsed || state.searching || state.filtering)
const SliverToBoxAdapter(
child: SizedBox(
height: 72,
height: 160,
),
),
SliverStateHandler<RadarState>(

View File

@ -21,6 +21,7 @@ class AudioSlider extends StatelessWidget {
@override
Widget build(BuildContext context) {
print(MediaService.audioPlayerTag);
return IgnorePointer(
ignoring: !_isPlaying,
child: Directionality(

View File

@ -1,6 +1,7 @@
import 'dart:async';
import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/models/category.dart';
import 'package:didvan/models/view/action_sheet_data.dart';
import 'package:didvan/utils/action_sheet.dart';
import 'package:didvan/views/home/home_state.dart';
@ -143,9 +144,17 @@ class SearchAppBar extends StatelessWidget implements PreferredSizeWidget {
onChanged: (value) {
if (value) {
state.selectedCats.add(state.categoryFilters[i]);
if (state.categoryFilters[i].id == 3) {
state.selectedCats
.add(CategoryData(id: 4, label: 'پادکست'));
}
return;
}
state.selectedCats.remove(state.categoryFilters[i]);
if (state.categoryFilters[i].id == 3) {
state.selectedCats
.remove(CategoryData(id: 4, label: 'پادکست'));
}
},
),
),