formatting

This commit is contained in:
Adrian Baumgart 2024-07-25 17:37:17 +02:00
parent 0eea6ee9a2
commit ba058e2af3
No known key found for this signature in database
11 changed files with 216 additions and 192 deletions

View File

@ -14,11 +14,9 @@ enum ConditionDeviceType {
} }
throw ArgumentError("Invalid type $api"); throw ArgumentError("Invalid type $api");
} }
} }
extension ConditionTypeExtension on ConditionDeviceType { extension ConditionTypeExtension on ConditionDeviceType {
String get api { String get api {
switch (this) { switch (this) {
case ConditionDeviceType.IOS: case ConditionDeviceType.IOS:
@ -29,6 +27,7 @@ extension ConditionTypeExtension on ConditionDeviceType {
return "desktop"; return "desktop";
} }
} }
String get humanReadable { String get humanReadable {
switch (this) { switch (this) {
case ConditionDeviceType.IOS: case ConditionDeviceType.IOS:
@ -39,4 +38,4 @@ extension ConditionTypeExtension on ConditionDeviceType {
return "Desktop"; return "Desktop";
} }
} }
} }

View File

@ -9,10 +9,11 @@ class RedirectRule {
RedirectRule(this.longUrl, this.priority, this.conditions); RedirectRule(this.longUrl, this.priority, this.conditions);
RedirectRule.fromJson(Map<String, dynamic> json) RedirectRule.fromJson(Map<String, dynamic> json)
: longUrl = json["longUrl"], : longUrl = json["longUrl"],
priority = json["priority"], priority = json["priority"],
conditions = (json["conditions"] as List<dynamic>).map((e) conditions = (json["conditions"] as List<dynamic>)
=> RedirectRuleCondition.fromJson(e)).toList(); .map((e) => RedirectRuleCondition.fromJson(e))
.toList();
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return { return {
@ -20,4 +21,4 @@ class RedirectRule {
"conditions": conditions.map((e) => e.toJson()).toList() "conditions": conditions.map((e) => e.toJson()).toList()
}; };
} }
} }

View File

@ -5,19 +5,15 @@ class RedirectRuleCondition {
String matchValue; String matchValue;
String? matchKey; String? matchKey;
RedirectRuleCondition(String type, this.matchValue, this.matchKey) : RedirectRuleCondition(String type, this.matchValue, this.matchKey)
type = RedirectRuleConditionType.fromApi(type); : type = RedirectRuleConditionType.fromApi(type);
RedirectRuleCondition.fromJson(Map<String, dynamic> json) RedirectRuleCondition.fromJson(Map<String, dynamic> json)
: type = RedirectRuleConditionType.fromApi(json["type"]), : type = RedirectRuleConditionType.fromApi(json["type"]),
matchValue = json["matchValue"], matchValue = json["matchValue"],
matchKey = json["matchKey"]; matchKey = json["matchKey"];
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return { return {"type": type.api, "matchValue": matchValue, "matchKey": matchKey};
"type": type.api,
"matchValue": matchValue,
"matchKey": matchKey
};
} }
} }

View File

@ -14,11 +14,9 @@ enum RedirectRuleConditionType {
} }
throw ArgumentError("Invalid type $api"); throw ArgumentError("Invalid type $api");
} }
} }
extension ConditionTypeExtension on RedirectRuleConditionType { extension ConditionTypeExtension on RedirectRuleConditionType {
String get api { String get api {
switch (this) { switch (this) {
case RedirectRuleConditionType.DEVICE: case RedirectRuleConditionType.DEVICE:
@ -29,6 +27,7 @@ extension ConditionTypeExtension on RedirectRuleConditionType {
return "query-param"; return "query-param";
} }
} }
String get humanReadable { String get humanReadable {
switch (this) { switch (this) {
case RedirectRuleConditionType.DEVICE: case RedirectRuleConditionType.DEVICE:
@ -39,4 +38,4 @@ extension ConditionTypeExtension on RedirectRuleConditionType {
return "Query parameter"; return "Query parameter";
} }
} }
} }

View File

@ -12,8 +12,9 @@ FutureOr<Either<List<RedirectRule>, Failure>> apiGetRedirectRules(
String? serverUrl, String? serverUrl,
String apiVersion) async { String apiVersion) async {
try { try {
final response = final response = await http.get(
await http.get(Uri.parse("$serverUrl/rest/v$apiVersion/short-urls/$shortCode/redirect-rules"), Uri.parse(
"$serverUrl/rest/v$apiVersion/short-urls/$shortCode/redirect-rules"),
headers: { headers: {
"X-Api-Key": apiKey ?? "", "X-Api-Key": apiKey ?? "",
}); });
@ -22,9 +23,10 @@ FutureOr<Either<List<RedirectRule>, Failure>> apiGetRedirectRules(
var jsonBody = jsonDecode(response.body) as Map<String, dynamic>; var jsonBody = jsonDecode(response.body) as Map<String, dynamic>;
// convert json array to object array // convert json array to object array
List<RedirectRule> redirectRules = (jsonBody["redirectRules"] List<RedirectRule> redirectRules =
as List<dynamic>).map((e) (jsonBody["redirectRules"] as List<dynamic>)
=> RedirectRule.fromJson(e)).toList(); .map((e) => RedirectRule.fromJson(e))
.toList();
return left(redirectRules); return left(redirectRules);
} else { } else {
@ -43,4 +45,4 @@ FutureOr<Either<List<RedirectRule>, Failure>> apiGetRedirectRules(
} catch (reqErr) { } catch (reqErr) {
return right(RequestFailure(0, reqErr.toString())); return right(RequestFailure(0, reqErr.toString()));
} }
} }

View File

@ -14,13 +14,16 @@ FutureOr<Either<bool, Failure>> apiSetRedirectRules(
String apiVersion) async { String apiVersion) async {
try { try {
Map<String, dynamic> body = {}; Map<String, dynamic> body = {};
List<Map<String, dynamic>> redirectRulesJson = redirectRules.map((e) => e.toJson()).toList(); List<Map<String, dynamic>> redirectRulesJson =
redirectRules.map((e) => e.toJson()).toList();
body["redirectRules"] = redirectRulesJson; body["redirectRules"] = redirectRulesJson;
final response = final response = await http.post(
await http.post(Uri.parse("$serverUrl/rest/v$apiVersion/short-urls/$shortCode/redirect-rules"), Uri.parse(
"$serverUrl/rest/v$apiVersion/short-urls/$shortCode/redirect-rules"),
headers: { headers: {
"X-Api-Key": apiKey ?? "", "X-Api-Key": apiKey ?? "",
}, body: jsonEncode(body)); },
body: jsonEncode(body));
if (response.statusCode == 200) { if (response.statusCode == 200) {
return left(true); return left(true);
} else { } else {
@ -39,4 +42,4 @@ FutureOr<Either<bool, Failure>> apiSetRedirectRules(
} catch (reqErr) { } catch (reqErr) {
return right(RequestFailure(0, reqErr.toString())); return right(RequestFailure(0, reqErr.toString()));
} }
} }

View File

@ -127,6 +127,7 @@ class ServerManager {
FutureOr<Either<List<ShortURL>, Failure>> getRecentShortUrls() async { FutureOr<Either<List<ShortURL>, Failure>> getRecentShortUrls() async {
return apiGetRecentShortUrls(apiKey, serverUrl, apiVersion); return apiGetRecentShortUrls(apiKey, serverUrl, apiVersion);
} }
/// Gets redirect rules for a given short URL (code) /// Gets redirect rules for a given short URL (code)
FutureOr<Either<List<RedirectRule>, Failure>> getRedirectRules( FutureOr<Either<List<RedirectRule>, Failure>> getRedirectRules(
String shortCode) async { String shortCode) async {
@ -136,7 +137,8 @@ class ServerManager {
/// Sets redirect rules for a given short URL (code) /// Sets redirect rules for a given short URL (code)
FutureOr<Either<bool, Failure>> setRedirectRules( FutureOr<Either<bool, Failure>> setRedirectRules(
String shortCode, List<RedirectRule> redirectRules) async { String shortCode, List<RedirectRule> redirectRules) async {
return apiSetRedirectRules(shortCode, redirectRules, apiKey, serverUrl, apiVersion); return apiSetRedirectRules(
shortCode, redirectRules, apiKey, serverUrl, apiVersion);
} }
} }

View File

@ -11,13 +11,13 @@ void main() {
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({super.key}); const MyApp({super.key});
static final ColorScheme _defaultLightColorScheme = ColorScheme static final ColorScheme _defaultLightColorScheme =
.fromSeed(seedColor: Colors.blue); ColorScheme.fromSeed(seedColor: Colors.blue);
static final _defaultDarkColorScheme = ColorScheme.fromSeed( static final _defaultDarkColorScheme = ColorScheme.fromSeed(
brightness: Brightness.dark, brightness: Brightness.dark,
seedColor: Colors.blue, seedColor: Colors.blue,
background: Colors.black); background: Colors.black);
// This widget is the root of your application. // This widget is the root of your application.
@override @override

View File

@ -50,7 +50,8 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''',
version: r'^1.0.5', version: r'^1.0.5',
homepage: null, homepage: null,
repository: r'https://github.com/flutter/packages/tree/main/third_party/packages/cupertino_icons', repository:
r'https://github.com/flutter/packages/tree/main/third_party/packages/cupertino_icons',
), ),
const License( const License(
name: r'dartz', name: r'dartz',
@ -286,7 +287,8 @@ SOFTWARE.
''', ''',
version: r'^1.6.6', version: r'^1.6.6',
homepage: null, homepage: null,
repository: r'https://github.com/material-foundation/flutter-packages/tree/main/packages/dynamic_color', repository:
r'https://github.com/material-foundation/flutter-packages/tree/main/packages/dynamic_color',
), ),
const License( const License(
name: r'flutter', name: r'flutter',
@ -346,7 +348,8 @@ SOFTWARE.
''', ''',
version: r'^0.13.1', version: r'^0.13.1',
homepage: r'https://github.com/fluttercommunity/flutter_launcher_icons', homepage: r'https://github.com/fluttercommunity/flutter_launcher_icons',
repository: r'https://github.com/fluttercommunity/flutter_launcher_icons/', repository:
r'https://github.com/fluttercommunity/flutter_launcher_icons/',
), ),
const License( const License(
name: r'flutter_lints', name: r'flutter_lints',
@ -378,36 +381,37 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
''', ''',
version: r'^3.0.1', version: r'^3.0.1',
homepage: null, homepage: null,
repository: r'https://github.com/flutter/packages/tree/main/packages/flutter_lints', repository:
r'https://github.com/flutter/packages/tree/main/packages/flutter_lints',
), ),
const License( const License(
name: r'flutter_process_text', name: r'flutter_process_text',
license: r'''BSD 3-Clause License license: r'''BSD 3-Clause License
(c) Copyright 2021 divshekhar (Divyanshu Shekhar) (c) Copyright 2021 divshekhar (Divyanshu Shekhar)
Redistribution and use in source and binary forms, with or without modification, Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met: are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, 1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer. this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, 2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution. and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors 3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without may be used to endorse or promote products derived from this software without
specific prior written permission. specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
version: r'^1.1.2', version: r'^1.1.2',
homepage: null, homepage: null,
@ -446,7 +450,8 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
version: r'^9.0.0', version: r'^9.0.0',
homepage: null, homepage: null,
repository: r'https://github.com/mogol/flutter_secure_storage/tree/develop/flutter_secure_storage', repository:
r'https://github.com/mogol/flutter_secure_storage/tree/develop/flutter_secure_storage',
), ),
const License( const License(
name: r'flutter_sharing_intent', name: r'flutter_sharing_intent',
@ -652,7 +657,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License.''', limitations under the License.''',
version: r'^1.1.1', version: r'^1.1.1',
homepage: r'https://github.com/bhagat-techind/flutter_sharing_intent.git', homepage:
r'https://github.com/bhagat-techind/flutter_sharing_intent.git',
repository: null, repository: null,
), ),
const License( const License(
@ -815,7 +821,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
''', ''',
version: r'^4.0.2', version: r'^4.0.2',
homepage: r'https://plus.fluttercommunity.dev/', homepage: r'https://plus.fluttercommunity.dev/',
repository: r'https://github.com/fluttercommunity/plus_plugins/tree/main/packages/package_info_plus/package_info_plus', repository:
r'https://github.com/fluttercommunity/plus_plugins/tree/main/packages/package_info_plus/package_info_plus',
), ),
const License( const License(
name: r'qr_flutter', name: r'qr_flutter',
@ -883,7 +890,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
''', ''',
version: r'^2.2.2', version: r'^2.2.2',
homepage: null, homepage: null,
repository: r'https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences', repository:
r'https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences',
), ),
const License( const License(
name: r'tuple', name: r'tuple',
@ -943,7 +951,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
''', ''',
version: r'^6.2.4', version: r'^6.2.4',
homepage: null, homepage: null,
repository: r'https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher', repository:
r'https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher',
), ),
]; ];
} }

View File

@ -13,7 +13,8 @@ class RedirectRulesDetailView extends StatefulWidget {
final ShortURL shortURL; final ShortURL shortURL;
@override @override
State<RedirectRulesDetailView> createState() => _RedirectRulesDetailViewState(); State<RedirectRulesDetailView> createState() =>
_RedirectRulesDetailViewState();
} }
class _RedirectRulesDetailViewState extends State<RedirectRulesDetailView> { class _RedirectRulesDetailViewState extends State<RedirectRulesDetailView> {
@ -30,7 +31,8 @@ class _RedirectRulesDetailViewState extends State<RedirectRulesDetailView> {
} }
Future<void> loadRedirectRules() async { Future<void> loadRedirectRules() async {
final response = await globals.serverManager.getRedirectRules(widget.shortURL.shortCode); final response =
await globals.serverManager.getRedirectRules(widget.shortURL.shortCode);
response.fold((l) { response.fold((l) {
setState(() { setState(() {
redirectRules = l; redirectRules = l;
@ -56,7 +58,8 @@ class _RedirectRulesDetailViewState extends State<RedirectRulesDetailView> {
} }
void _saveRedirectRules() async { void _saveRedirectRules() async {
final response = await globals.serverManager.setRedirectRules(widget.shortURL.shortCode, redirectRules); final response = await globals.serverManager
.setRedirectRules(widget.shortURL.shortCode, redirectRules);
response.fold((l) { response.fold((l) {
Navigator.pop(context); Navigator.pop(context);
}, (r) { }, (r) {
@ -97,7 +100,7 @@ class _RedirectRulesDetailViewState extends State<RedirectRulesDetailView> {
spacing: 16, spacing: 16,
children: [ children: [
FloatingActionButton( FloatingActionButton(
onPressed: () { onPressed: () {
if (!isSaving & redirectRulesLoaded) { if (!isSaving & redirectRulesLoaded) {
setState(() { setState(() {
isSaving = true; isSaving = true;
@ -106,10 +109,10 @@ class _RedirectRulesDetailViewState extends State<RedirectRulesDetailView> {
} }
}, },
child: isSaving child: isSaving
? const Padding( ? const Padding(
padding: EdgeInsets.all(16), padding: EdgeInsets.all(16),
child: CircularProgressIndicator(strokeWidth: 3)) child: CircularProgressIndicator(strokeWidth: 3))
: const Icon(Icons.save)) : const Icon(Icons.save))
], ],
), ),
body: CustomScrollView( body: CustomScrollView(
@ -131,16 +134,14 @@ class _RedirectRulesDetailViewState extends State<RedirectRulesDetailView> {
const Text( const Text(
"No Redirect Rules", "No Redirect Rules",
style: TextStyle( style: TextStyle(
fontSize: 24, fontSize: 24, fontWeight: FontWeight.bold),
fontWeight: FontWeight.bold),
), ),
Padding( Padding(
padding: const EdgeInsets.only(top: 8), padding: const EdgeInsets.only(top: 8),
child: Text( child: Text(
'Adding redirect rules will be supported soon!', 'Adding redirect rules will be supported soon!',
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16, color: Colors.grey[600]),
color: Colors.grey[600]),
), ),
) )
], ],
@ -149,30 +150,34 @@ class _RedirectRulesDetailViewState extends State<RedirectRulesDetailView> {
SliverList( SliverList(
delegate: SliverChildBuilderDelegate( delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) { (BuildContext context, int index) {
return _ListCell( return _ListCell(
redirectRule: redirectRules[index], redirectRule: redirectRules[index],
moveUp: index == 0 ? null : () { moveUp: index == 0
setState(() { ? null
redirectRules[index].priority -= 1; : () {
redirectRules[index - 1].priority += 1; setState(() {
}); redirectRules[index].priority -= 1;
_sortListByPriority(); redirectRules[index - 1].priority += 1;
}, });
moveDown: index == (redirectRules.length - 1) ? null : () { _sortListByPriority();
setState(() { },
redirectRules[index].priority += 1; moveDown: index == (redirectRules.length - 1)
redirectRules[index + 1].priority -= 1; ? null
}); : () {
_sortListByPriority(); setState(() {
}, redirectRules[index].priority += 1;
delete: () { redirectRules[index + 1].priority -= 1;
setState(() { });
redirectRules.removeAt(index); _sortListByPriority();
}); },
_fixPriorities(); delete: () {
}, setState(() {
); redirectRules.removeAt(index);
}, childCount: redirectRules.length)) });
_fixPriorities();
},
);
}, childCount: redirectRules.length))
], ],
), ),
); );
@ -180,11 +185,12 @@ class _RedirectRulesDetailViewState extends State<RedirectRulesDetailView> {
} }
class _ListCell extends StatefulWidget { class _ListCell extends StatefulWidget {
const _ListCell({super.key, const _ListCell(
required this.redirectRule, {super.key,
required this.moveUp, required this.redirectRule,
required this.moveDown, required this.moveUp,
required this.delete}); required this.moveDown,
required this.delete});
final VoidCallback? moveUp; final VoidCallback? moveUp;
final VoidCallback? moveDown; final VoidCallback? moveDown;
@ -196,7 +202,6 @@ class _ListCell extends StatefulWidget {
} }
class _ListCellState extends State<_ListCell> { class _ListCellState extends State<_ListCell> {
String _conditionToTagString(RedirectRuleCondition condition) { String _conditionToTagString(RedirectRuleCondition condition) {
switch (condition.type) { switch (condition.type) {
case RedirectRuleConditionType.DEVICE: case RedirectRuleConditionType.DEVICE:
@ -210,76 +215,84 @@ class _ListCellState extends State<_ListCell> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Padding(padding: EdgeInsets.only( return Padding(
left: 8, right: 8 padding: EdgeInsets.only(left: 8, right: 8),
), child: Container( child: Container(
padding: EdgeInsets.only(left: 8, right: 8, top: 16, bottom: 16), padding: EdgeInsets.only(left: 8, right: 8, top: 16, bottom: 16),
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border( border: Border(
bottom: BorderSide( bottom: BorderSide(
color: MediaQuery.of(context).platformBrightness == color: MediaQuery.of(context).platformBrightness ==
Brightness.dark Brightness.dark
? Colors.grey[800]! ? Colors.grey[800]!
: Colors.grey[300]!)), : Colors.grey[300]!)),
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( Row(
children: [ children: [
const Text("Long URL ", style: TextStyle(fontWeight: FontWeight.bold)), const Text("Long URL ",
Text(widget.redirectRule.longUrl) style: TextStyle(fontWeight: FontWeight.bold)),
], Text(widget.redirectRule.longUrl)
), ],
Text("Conditions:", style: TextStyle(fontWeight: FontWeight.bold)),
Row(
children: [
Expanded(
child: Wrap(
children: widget.redirectRule.conditions.map((condition) {
return Padding(
padding: const EdgeInsets.only(right: 4, top: 4),
child: Container(
padding:
const EdgeInsets.only(top: 4, bottom: 4, left: 12, right: 12),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
color: MediaQuery.of(context).platformBrightness == Brightness.dark ?
Colors.grey[900] : Colors.grey[300],
),
child: Text(
_conditionToTagString(condition)
),
),
);
}).toList(),
), ),
) Text("Conditions:",
], style: TextStyle(fontWeight: FontWeight.bold)),
), Row(
Wrap( children: [
children: [ Expanded(
IconButton( child: Wrap(
disabledColor: MediaQuery.of(context).platformBrightness children:
== Brightness.dark ? Colors.grey[700] : Colors.grey[400], widget.redirectRule.conditions.map((condition) {
onPressed: widget.moveUp, return Padding(
icon: Icon(Icons.arrow_upward), padding: const EdgeInsets.only(right: 4, top: 4),
), child: Container(
IconButton( padding: const EdgeInsets.only(
disabledColor: MediaQuery.of(context).platformBrightness top: 4, bottom: 4, left: 12, right: 12),
== Brightness.dark ? Colors.grey[700] : Colors.grey[400], decoration: BoxDecoration(
onPressed: widget.moveDown, borderRadius: BorderRadius.circular(4),
icon: Icon(Icons.arrow_downward), color:
), MediaQuery.of(context).platformBrightness ==
IconButton( Brightness.dark
onPressed: widget.delete, ? Colors.grey[900]
icon: Icon(Icons.delete, color: Colors.red), : Colors.grey[300],
) ),
child: Text(_conditionToTagString(condition)),
], ),
) );
], }).toList(),
) ),
)); )
],
),
Wrap(
children: [
IconButton(
disabledColor:
MediaQuery.of(context).platformBrightness ==
Brightness.dark
? Colors.grey[700]
: Colors.grey[400],
onPressed: widget.moveUp,
icon: Icon(Icons.arrow_upward),
),
IconButton(
disabledColor:
MediaQuery.of(context).platformBrightness ==
Brightness.dark
? Colors.grey[700]
: Colors.grey[400],
onPressed: widget.moveDown,
icon: Icon(Icons.arrow_downward),
),
IconButton(
onPressed: widget.delete,
icon: Icon(Icons.delete, color: Colors.red),
)
],
)
],
)));
} }
} }

View File

@ -130,7 +130,9 @@ class _URLDetailViewState extends State<URLDetailView> {
title: "Short URL", content: shortURL.shortUrl, isUrl: true), title: "Short URL", content: shortURL.shortUrl, isUrl: true),
_ListCell(title: "Long URL", content: shortURL.longUrl, isUrl: true), _ListCell(title: "Long URL", content: shortURL.longUrl, isUrl: true),
_ListCell(title: "Creation Date", content: shortURL.dateCreated), _ListCell(title: "Creation Date", content: shortURL.dateCreated),
_ListCell(title: "Redirect Rules", content: null, _ListCell(
title: "Redirect Rules",
content: null,
clickableDetailView: RedirectRulesDetailView(shortURL: shortURL)), clickableDetailView: RedirectRulesDetailView(shortURL: shortURL)),
const _ListCell(title: "Visits", content: ""), const _ListCell(title: "Visits", content: ""),
_ListCell( _ListCell(
@ -189,10 +191,8 @@ class _ListCellState extends State<_ListCell> {
child: GestureDetector( child: GestureDetector(
onTap: () async { onTap: () async {
if (widget.clickableDetailView != null) { if (widget.clickableDetailView != null) {
Navigator.of(context) Navigator.of(context).push(MaterialPageRoute(
.push(MaterialPageRoute( builder: (context) => widget.clickableDetailView!));
builder: (context) =>
widget.clickableDetailView!));
} else if (widget.content is String) { } else if (widget.content is String) {
Uri? parsedUrl = Uri.tryParse(widget.content); Uri? parsedUrl = Uri.tryParse(widget.content);
if (widget.isUrl && if (widget.isUrl &&
@ -258,7 +258,7 @@ class _ListCellState extends State<_ListCell> {
Text(DateFormat('yyyy-MM-dd - HH:mm') Text(DateFormat('yyyy-MM-dd - HH:mm')
.format(widget.content)) .format(widget.content))
else if (widget.clickableDetailView != null) else if (widget.clickableDetailView != null)
const Icon(Icons.chevron_right) const Icon(Icons.chevron_right)
else else
const Text("N/A") const Text("N/A")
], ],