Merge pull request #120 from refilc/dev

dev to master (beta v5.0.1)
This commit is contained in:
Márton Kiss 2024-06-13 21:30:32 +02:00 committed by GitHub
commit 7842aa447f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
72 changed files with 2854 additions and 609 deletions

56
Jenkinsfile vendored Normal file
View File

@ -0,0 +1,56 @@
pipeline {
agent any
environment {
ANDROID_SDK = '/home/jenkins/flutter_things/android-sdk'
ANDROID_PATH="$ANDROID_SDK/tools:$ANDROID_SDK/platform-tools"
FLUTTER = '/home/jenkins/flutter_things/flutter/bin'
PATH = "$PATH:$ANDROID_PATH:$FLUTTER"
//TODO: need to fix flutter
}
stages {
stage('Copy Key Properties') {
steps {
// Copy the key.properties file
sh 'cp /home/jenkins/key.properties refilc/android/key.properties'
}
}
stage('Flutter Doctor') {
steps {
// Ensure Flutter is set up correctly
sh 'flutter doctor'
}
}
stage('Dependencies') {
steps {
// Get Flutter dependencies
sh 'cd refilc && flutter pub get'
}
}
stage('Build') {
steps {
// Build the Flutter project
sh 'cd refilc && flutter build apk --release'
}
}
stage('Archive') {
steps {
// Archive the APK
archiveArtifacts artifacts: 'build/app/outputs/flutter-apk/app-release.apk', fingerprint: true
}
}
}
post {
always {
// Clean up workspace after build
cleanWs()
}
}
}

View File

@ -20,7 +20,7 @@
android:gravity="center"
android:text="1."
android:textColor="@color/filc"
android:textColorLink="#ff3D7BF4"
android:textColorLink="#ff052460"
android:textSize="30sp"
android:textStyle="bold"
tools:ignore="HardcodedText" />
@ -110,4 +110,4 @@
android:textColor="@color/white"
tools:ignore="HardcodedText" />
</RelativeLayout>
</RelativeLayout>

View File

@ -27,7 +27,7 @@
<color name="yellow_light">#ffFFCC00</color>
<color name="light_yellow_light">#40FFD60A</color>
<color name="green_light">#ff34C759</color>
<color name="filc_light">#ff3D7BF4</color>
<color name="filc_light">#ff052460</color>
<color name="teal_light">#ff5AC8FA</color>
<color name="blue_light">#ff007AFF</color>
<color name="indigo_light">#ff5856D6</color>
@ -49,8 +49,8 @@
<color name="yellow">#ffFFD60A</color>
<color name="light_yellow">#40FFD60A</color>
<color name="green">#ff32D74B</color>
<color name="filc">#ff3D7BF4</color>
<color name="filc_gradient">#ff3D93F5</color>
<color name="filc">#ff052460</color>
<color name="filc_gradient">#ff06348f</color>
<color name="teal">#ff64D2FF</color>
<color name="blue">#ff0A84FF</color>
<color name="indigo">#ff5E5CE6</color>
@ -66,4 +66,4 @@
<color name="pink_shade300">#FFF06292</color>
<color name="purple_shade300">#FFBA68C8</color>
<color name="teal_shade300">#FF22AC9B</color>
</resources>
</resources>

View File

@ -0,0 +1,43 @@
<svg width="375" height="174" viewBox="0 0 375 174" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1575_5258)">
<g style="mix-blend-mode:color-burn">
<rect x="187.881" y="36.8047" width="50.4482" height="50.4482" stroke="#858585"/>
<rect x="238.186" y="36.8047" width="50.4482" height="50.4482" stroke="#858585"/>
<rect x="187.881" y="-13.5" width="50.4482" height="50.4482" stroke="#858585"/>
<rect x="238.186" y="-13.5" width="50.4482" height="50.4482" stroke="#858585"/>
<rect x="288.49" y="36.8047" width="49.3049" height="50.4482" stroke="#858585"/>
<rect x="337.652" y="36.8047" width="50.4482" height="50.4482" stroke="#858585"/>
<rect x="288.49" y="-13.5" width="49.3049" height="50.4482" stroke="#858585"/>
<rect x="337.652" y="-13.5" width="50.4482" height="50.4482" stroke="#858585"/>
<rect x="187.881" y="136.271" width="50.4482" height="50.4482" stroke="#858585"/>
<rect x="238.186" y="136.271" width="50.4482" height="50.4482" stroke="#858585"/>
<rect x="187.881" y="87.1099" width="50.4482" height="49.3049" stroke="#858585"/>
<rect x="238.186" y="87.1099" width="50.4482" height="49.3049" stroke="#858585"/>
<rect x="288.49" y="136.271" width="49.3049" height="50.4482" stroke="#858585"/>
<rect x="337.652" y="136.271" width="50.4482" height="50.4482" stroke="#858585"/>
<rect x="288.49" y="87.1099" width="49.3049" height="49.3049" stroke="#858585"/>
<rect x="337.652" y="87.1099" width="50.4482" height="49.3049" stroke="#858585"/>
<rect x="-12.1953" y="36.8047" width="50.4482" height="50.4482" stroke="#858585"/>
<rect x="-12.1953" y="-13.5" width="50.4482" height="50.4482" stroke="#858585"/>
<rect x="38.1094" y="36.8047" width="49.3049" height="50.4482" stroke="#858585"/>
<rect x="87.2705" y="36.8047" width="50.4482" height="50.4482" stroke="#858585"/>
<rect x="38.1094" y="-13.5" width="49.3049" height="50.4482" stroke="#858585"/>
<rect x="87.2705" y="-13.5" width="50.4482" height="50.4482" stroke="#858585"/>
<rect x="-12.1953" y="136.271" width="50.4482" height="50.4482" stroke="#858585"/>
<rect x="-12.1953" y="87.1099" width="50.4482" height="49.3049" stroke="#858585"/>
<rect x="38.1094" y="136.271" width="49.3049" height="50.4482" stroke="#858585"/>
<rect x="87.2705" y="136.271" width="50.4482" height="50.4482" stroke="#858585"/>
<rect x="38.1094" y="87.1099" width="49.3049" height="49.3049" stroke="#858585"/>
<rect x="87.2705" y="87.1099" width="50.4482" height="49.3049" stroke="#858585"/>
<rect x="137.576" y="36.8047" width="50.4482" height="50.4482" stroke="#858585"/>
<rect x="137.576" y="-13.5" width="50.4482" height="50.4482" stroke="#858585"/>
<rect x="137.576" y="136.271" width="50.4482" height="50.4482" stroke="#858585"/>
<rect x="137.576" y="87.1099" width="50.4482" height="49.3049" stroke="#858585"/>
</g>
</g>
<defs>
<clipPath id="clip0_1575_5258">
<rect width="375" height="173.78" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -0,0 +1,27 @@
<svg width="375" height="175" viewBox="0 0 375 175" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1575_5259)">
<g style="mix-blend-mode:color-burn">
<mask id="path-1-inside-1_1575_5259" fill="white">
<path d="M0 37H375V89H0V37Z"/>
</mask>
<path d="M0 38H375V36H0V38Z" fill="#858585" mask="url(#path-1-inside-1_1575_5259)"/>
<mask id="path-3-inside-2_1575_5259" fill="white">
<path d="M0 -13H375V38H0V-13Z"/>
</mask>
<path d="M0 -12H375V-14H0V-12Z" fill="#858585" mask="url(#path-3-inside-2_1575_5259)"/>
<mask id="path-5-inside-3_1575_5259" fill="white">
<path d="M0 137H375V188H0V137Z"/>
</mask>
<path d="M0 138H375V136H0V138Z" fill="#858585" mask="url(#path-5-inside-3_1575_5259)"/>
<mask id="path-7-inside-4_1575_5259" fill="white">
<path d="M0 87H375V138H0V87Z"/>
</mask>
<path d="M0 88H375V86H0V88Z" fill="#858585" mask="url(#path-7-inside-4_1575_5259)"/>
</g>
</g>
<defs>
<clipPath id="clip0_1575_5259">
<rect width="375" height="173.78" fill="white" transform="translate(0 0.780518)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,47 @@
<svg width="375" height="175" viewBox="0 0 375 175" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1575_5260)">
<g style="mix-blend-mode:color-burn">
<circle cx="17" cy="19.561" r="1" fill="#858585"/>
<circle cx="59" cy="19.561" r="1" fill="#858585"/>
<circle cx="101" cy="19.561" r="1" fill="#858585"/>
<circle cx="143" cy="19.561" r="1" fill="#858585"/>
<circle cx="185" cy="19.561" r="1" fill="#858585"/>
<circle cx="227" cy="19.561" r="1" fill="#858585"/>
<circle cx="269" cy="19.561" r="1" fill="#858585"/>
<circle cx="311" cy="19.561" r="1" fill="#858585"/>
<circle cx="353" cy="19.561" r="1" fill="#858585"/>
<circle cx="17" cy="61.561" r="1" fill="#858585"/>
<circle cx="59" cy="61.561" r="1" fill="#858585"/>
<circle cx="101" cy="61.561" r="1" fill="#858585"/>
<circle cx="143" cy="61.561" r="1" fill="#858585"/>
<circle cx="185" cy="61.561" r="1" fill="#858585"/>
<circle cx="227" cy="61.561" r="1" fill="#858585"/>
<circle cx="269" cy="61.561" r="1" fill="#858585"/>
<circle cx="311" cy="61.561" r="1" fill="#858585"/>
<circle cx="353" cy="61.561" r="1" fill="#858585"/>
<circle cx="17" cy="103.561" r="1" fill="#858585"/>
<circle cx="59" cy="103.561" r="1" fill="#858585"/>
<circle cx="101" cy="103.561" r="1" fill="#858585"/>
<circle cx="143" cy="103.561" r="1" fill="#858585"/>
<circle cx="185" cy="103.561" r="1" fill="#858585"/>
<circle cx="227" cy="103.561" r="1" fill="#858585"/>
<circle cx="269" cy="103.561" r="1" fill="#858585"/>
<circle cx="311" cy="103.561" r="1" fill="#858585"/>
<circle cx="353" cy="103.561" r="1" fill="#858585"/>
<circle cx="17" cy="145.561" r="1" fill="#858585"/>
<circle cx="59" cy="145.561" r="1" fill="#858585"/>
<circle cx="101" cy="145.561" r="1" fill="#858585"/>
<circle cx="143" cy="145.561" r="1" fill="#858585"/>
<circle cx="185" cy="145.561" r="1" fill="#858585"/>
<circle cx="227" cy="145.561" r="1" fill="#858585"/>
<circle cx="269" cy="145.561" r="1" fill="#858585"/>
<circle cx="311" cy="145.561" r="1" fill="#858585"/>
<circle cx="353" cy="145.561" r="1" fill="#858585"/>
</g>
</g>
<defs>
<clipPath id="clip0_1575_5260">
<rect width="375" height="173.78" fill="white" transform="translate(0 0.561035)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,28 @@
<svg width="375" height="175" viewBox="0 0 375 175" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1575_5261)">
<g style="mix-blend-mode:color-burn">
<path d="M0 63.0001H375" stroke="#858585"/>
<path d="M0 75.0001H375" stroke="#858585"/>
<path d="M0 87.0001H375" stroke="#858585"/>
<path d="M0 99.0001H375" stroke="#858585"/>
<path d="M0 111H375" stroke="#858585"/>
</g>
<g style="mix-blend-mode:color-burn">
<path d="M0 135H375" stroke="#858585"/>
<path d="M0 147H375" stroke="#858585"/>
<path d="M0 159H375" stroke="#858585"/>
<path d="M0 171H375" stroke="#858585"/>
</g>
<g style="mix-blend-mode:color-burn">
<path d="M0 3.00012H375" stroke="#858585"/>
<path d="M0 15.0001H375" stroke="#858585"/>
<path d="M0 27.0001H375" stroke="#858585"/>
<path d="M0 39.0001H375" stroke="#858585"/>
</g>
</g>
<defs>
<clipPath id="clip0_1575_5261">
<rect width="375" height="173.78" fill="white" transform="translate(0 0.341431)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 973 B

View File

@ -1,43 +0,0 @@
<svg width="375" height="174" viewBox="0 0 375 174" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_581_2504)">
<g style="mix-blend-mode:color-burn">
<rect x="138.881" y="36.8047" width="50.4482" height="50.4482" stroke="black"/>
<rect x="189.186" y="36.8047" width="50.4482" height="50.4482" stroke="black"/>
<rect x="138.881" y="-13.5" width="50.4482" height="50.4482" stroke="black"/>
<rect x="189.186" y="-13.5" width="50.4482" height="50.4482" stroke="black"/>
<rect x="239.49" y="36.8047" width="49.3049" height="50.4482" stroke="black"/>
<rect x="288.652" y="36.8047" width="50.4482" height="50.4482" stroke="black"/>
<rect x="239.49" y="-13.5" width="49.3049" height="50.4482" stroke="black"/>
<rect x="288.652" y="-13.5" width="50.4482" height="50.4482" stroke="black"/>
<rect x="138.881" y="136.271" width="50.4482" height="50.4482" stroke="black"/>
<rect x="189.186" y="136.271" width="50.4482" height="50.4482" stroke="black"/>
<rect x="138.881" y="87.1099" width="50.4482" height="49.3049" stroke="black"/>
<rect x="189.186" y="87.1099" width="50.4482" height="49.3049" stroke="black"/>
<rect x="239.49" y="136.271" width="49.3049" height="50.4482" stroke="black"/>
<rect x="288.652" y="136.271" width="50.4482" height="50.4482" stroke="black"/>
<rect x="239.49" y="87.1099" width="49.3049" height="49.3049" stroke="black"/>
<rect x="288.652" y="87.1099" width="50.4482" height="49.3049" stroke="black"/>
<rect x="338.957" y="36.8047" width="50.4482" height="50.4482" stroke="black"/>
<rect x="338.957" y="-13.5" width="50.4482" height="50.4482" stroke="black"/>
<rect x="338.957" y="136.271" width="50.4482" height="50.4482" stroke="black"/>
<rect x="338.957" y="87.1099" width="50.4482" height="49.3049" stroke="black"/>
<rect x="-10.8906" y="36.8047" width="49.3049" height="50.4482" stroke="black"/>
<rect x="38.2705" y="36.8047" width="50.4482" height="50.4482" stroke="black"/>
<rect x="-10.8906" y="-13.5" width="49.3049" height="50.4482" stroke="black"/>
<rect x="38.2705" y="-13.5" width="50.4482" height="50.4482" stroke="black"/>
<rect x="-10.8906" y="136.271" width="49.3049" height="50.4482" stroke="black"/>
<rect x="38.2705" y="136.271" width="50.4482" height="50.4482" stroke="black"/>
<rect x="-10.8906" y="87.1099" width="49.3049" height="49.3049" stroke="black"/>
<rect x="38.2705" y="87.1099" width="50.4482" height="49.3049" stroke="black"/>
<rect x="88.5762" y="36.8047" width="50.4482" height="50.4482" stroke="black"/>
<rect x="88.5762" y="-13.5" width="50.4482" height="50.4482" stroke="black"/>
<rect x="88.5762" y="136.271" width="50.4482" height="50.4482" stroke="black"/>
<rect x="88.5762" y="87.1099" width="50.4482" height="49.3049" stroke="black"/>
</g>
</g>
<defs>
<clipPath id="clip0_581_2504">
<rect width="375" height="173.78" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -52,14 +52,31 @@ struct LockScreenLiveActivityView: View {
VStack(alignment: .center) {
// Jelenlegi óra
VStack {
Text(context.state.index + " " + context.state.title)
.font(.body)
.bold()
.multilineTextAlignment(.center)
if(context.state.title.contains("Az első órádig")) {
Text(context.state.title)
.font(.system(size: 15))
.bold()
.multilineTextAlignment(.center)
} else if(context.state.title == "Szünet") {
Text(context.state.title)
.font(.body)
.bold()
.padding(.trailing, 90)
Text("Terem: \(context.state.subtitle)")
.italic()
.font(.caption)
} else {
MultilineTextView(text: "\(context.state.index) \(context.state.title)", limit: 25)
.font(.body)
.bold()
.multilineTextAlignment(.center)
}
//Terem
if (!context.state.subtitle.isEmpty) {
Text(context.state.subtitle)
.italic()
.bold()
.font(.system(size: 13))
}
}
// Leírás
@ -69,6 +86,7 @@ struct LockScreenLiveActivityView: View {
}
// Következő óra
if(context.state.nextSubject != "" && context.state.nextRoom != "") {
HStack {
Image(systemName: "arrow.right")
.resizable()
@ -80,18 +98,24 @@ struct LockScreenLiveActivityView: View {
.font(.caption2)
}
.multilineTextAlignment(.center)
} else {
Spacer(minLength: 5)
Text("Ez az utolsó óra! Kitartást!")
.font(.system(size: 15))
}
}
.padding(15)
Spacer()
// Visszaszámláló
Text(timerInterval: context.state.date, countsDown: true)
.multilineTextAlignment(.center)
.frame(width: 85)
.font(.title2)
.monospacedDigit()
.padding(.trailing, CGFloat(24))
.padding(.trailing)
}
.activityBackgroundTint(
context.state.color != "#676767"
@ -109,7 +133,7 @@ struct LiveCardWidget: Widget {
LockScreenLiveActivityView(context: context)
/// Dynamic Island
} dynamicIsland: { context in
/// Expanded
return DynamicIsland {
DynamicIslandExpandedRegion(.leading) {
@ -133,39 +157,95 @@ struct LiveCardWidget: Widget {
).progressViewStyle(.circular)
}
}
DynamicIslandExpandedRegion(.center) {
VStack(alignment: .center) {
Text(context.state.index + context.state.title)
.lineLimit(1)
.font(.body)
.bold()
Text(context.state.subtitle)
.lineLimit(1)
.font(.subheadline)
Spacer()
Text(context.state.description)
.lineLimit(2)
.font(.caption)
}.padding(EdgeInsets(top: 0.0, leading: 5.0, bottom: 0.0, trailing: 0.0))
}
DynamicIslandExpandedRegion(.center) {
VStack(alignment: .center) {
// Első óra előtti expanded DynamicIsland
if(context.state.title.contains("Az első órádig")) {
Text("Az első órád:")
.font(.body)
.bold()
.padding(.trailing, -15)
MultilineTextView(text: "\(context.state.nextSubject)", limit: 25)
.font(.body)
.padding(.trailing, -25)
Text("Ebben a teremben:")
.font(.body)
.bold()
.padding(.leading, 15)
Text(context.state.nextRoom)
.font(.body)
.padding(.leading, 15)
} else if(context.state.title == "Szünet") {
// Amikor szünet van, expanded DynamicIsland
Text(context.state.title)
.lineLimit(1)
.font(.body)
.bold()
.padding(.leading, 15)
Spacer(minLength: 5)
Text("Következő óra és terem:")
.font(.system(size: 13))
.padding(.leading, 25)
Text(context.state.nextSubject)
.font(.caption)
.padding(.leading, 15)
Text(context.state.nextRoom)
.font(.caption2)
.padding(.leading, 15)
} else {
// Amikor óra van, expanded DynamicIsland
MultilineTextView(text: "\(context.state.index) \(context.state.title)", limit: 25)
.lineLimit(1)
.font(.body)
.bold()
.padding(.trailing, -35)
Text(context.state.subtitle)
.lineLimit(1)
.italic()
.bold()
.font(.system(size: 13))
.padding(.trailing, -50)
Spacer(minLength: 5)
if(context.state.nextRoom != "" && context.state.nextSubject != "") {
Text("Következő óra és terem:")
.font(.system(size: 14))
.padding(.trailing, -35)
Spacer(minLength: 2)
Text(context.state.nextSubject)
.modifier(DynamicFontSizeModifier(text: context.state.nextSubject))
.padding(.trailing, -35)
Text(context.state.nextRoom)
// ignore: based on nextSubject characters, I check that the font size of the room is the same as the next subject.
.modifier(DynamicFontSizeModifier(text: context.state.nextSubject))
.padding(.trailing, -35)
} else {
Text("Ez az utolsó óra! Kitartást!")
.font(.system(size: 14))
.padding(.trailing, -30)
}
}
}.padding(EdgeInsets(top: 0.0, leading: 5.0, bottom: 0.0, trailing: 0.0))
}
/// Compact
} compactLeading: {
Label {
Text(context.state.title)
} icon: {
Image(systemName: context.state.icon)
}
.font(.caption2)
Image(systemName: context.state.icon)
}
compactTrailing: {
Text(timerInterval: context.state.date, countsDown: true)
.multilineTextAlignment(.center)
.frame(width: 40)
.font(.caption2)
/// Collapsed
} minimal: {
VStack(alignment: .center, content: {
@ -191,7 +271,59 @@ struct LiveCardWidget: Widget {
context.state.color != "#676767"
? Color(hex: context.state.color)
: Color.clear
)
)
}
}
}
struct MultilineTextView: View {
var text: String
var limit: Int = 20 // default is 20 character
var body: some View {
let words = text.split(separator: " ")
var currentLine = ""
var lines: [String] = []
for word in words {
if (currentLine.count + word.count + 1) > limit {
lines.append(currentLine)
currentLine = ""
}
if !currentLine.isEmpty {
currentLine += " "
}
currentLine += word
}
if !currentLine.isEmpty {
lines.append(currentLine)
}
return VStack(alignment: .center) {
ForEach(lines, id: \.self) { line in
Text(line)
}
Spacer(minLength: 1)
}
}
}
struct DynamicFontSizeModifier: ViewModifier {
var text: String
func body(content: Content) -> some View {
content
.font(.system(size: fontSize(for: text)))
}
private func fontSize(for text: String) -> CGFloat {
let length = text.count
if length < 10 {
return 12
} else if length < 20 {
return 12
} else {
return 11
}
}
}

View File

@ -336,6 +336,8 @@ class FilcAPI {
if (res.statusCode == 200) {
return (jsonDecode(res.body) as Map);
} else if (res.statusCode == 404) {
return {"public_id": ""};
} else {
throw "HTTP ${res.statusCode}: ${res.body}";
}

View File

@ -88,24 +88,24 @@ class LiveCardProvider extends ChangeNotifier {
case LiveCardState.morning:
return {
"color":
'#${_settings.liveActivityColor.toString().substring(10, 16)}',
'#${_settings.liveActivityColor.toString().substring(10, 16)}',
"icon": nextLesson != null
? SubjectIcon.resolveName(subject: nextLesson?.subject)
: "book",
"title": "Első órádig:",
"title": "Jó reggelt! Az első órádig:",
"subtitle": "",
"description": "",
"startDate": storeFirstRunDate != null
? ((storeFirstRunDate?.millisecondsSinceEpoch ?? 0) -
(_delay.inMilliseconds))
.toString()
(_delay.inMilliseconds))
.toString()
: "",
"endDate": ((nextLesson?.start.millisecondsSinceEpoch ?? 0) -
_delay.inMilliseconds)
_delay.inMilliseconds)
.toString(),
"nextSubject": nextLesson != null
? nextLesson?.subject.renamedTo ??
ShortSubject.resolve(subject: nextLesson?.subject).capital()
ShortSubject.resolve(subject: nextLesson?.subject).capital()
: "",
"nextRoom": nextLesson?.room.replaceAll("_", " ") ?? "",
};
@ -113,24 +113,24 @@ class LiveCardProvider extends ChangeNotifier {
case LiveCardState.afternoon:
return {
"color":
'#${_settings.liveActivityColor.toString().substring(10, 16)}',
'#${_settings.liveActivityColor.toString().substring(10, 16)}',
"icon": nextLesson != null
? SubjectIcon.resolveName(subject: nextLesson?.subject)
: "book",
"title": "Első órádig:",
"title": "Jó napot! Az első órádig:",
"subtitle": "",
"description": "",
"startDate": storeFirstRunDate != null
? ((storeFirstRunDate?.millisecondsSinceEpoch ?? 0) -
(_delay.inMilliseconds))
.toString()
(_delay.inMilliseconds))
.toString()
: "",
"endDate": ((nextLesson?.start.millisecondsSinceEpoch ?? 0) -
_delay.inMilliseconds)
_delay.inMilliseconds)
.toString(),
"nextSubject": nextLesson != null
? nextLesson?.subject.renamedTo ??
ShortSubject.resolve(subject: nextLesson?.subject).capital()
ShortSubject.resolve(subject: nextLesson?.subject).capital()
: "",
"nextRoom": nextLesson?.room.replaceAll("_", " ") ?? "",
};
@ -138,24 +138,24 @@ class LiveCardProvider extends ChangeNotifier {
case LiveCardState.night:
return {
"color":
'#${_settings.liveActivityColor.toString().substring(10, 16)}',
'#${_settings.liveActivityColor.toString().substring(10, 16)}',
"icon": nextLesson != null
? SubjectIcon.resolveName(subject: nextLesson?.subject)
: "book",
"title": "Első órádig:",
"title": "Jó estét! Az első órádig:",
"subtitle": "",
"description": "",
"startDate": storeFirstRunDate != null
? ((storeFirstRunDate?.millisecondsSinceEpoch ?? 0) -
(_delay.inMilliseconds))
.toString()
(_delay.inMilliseconds))
.toString()
: "",
"endDate": ((nextLesson?.start.millisecondsSinceEpoch ?? 0) -
_delay.inMilliseconds)
_delay.inMilliseconds)
.toString(),
"nextSubject": nextLesson != null
? nextLesson?.subject.renamedTo ??
ShortSubject.resolve(subject: nextLesson?.subject).capital()
ShortSubject.resolve(subject: nextLesson?.subject).capital()
: "",
"nextRoom": nextLesson?.room.replaceAll("_", " ") ?? "",
};
@ -163,28 +163,28 @@ class LiveCardProvider extends ChangeNotifier {
case LiveCardState.duringLesson:
return {
"color":
'#${_settings.liveActivityColor.toString().substring(10, 16)}',
'#${_settings.liveActivityColor.toString().substring(10, 16)}',
"icon": currentLesson != null
? SubjectIcon.resolveName(subject: currentLesson?.subject)
: "book",
"index":
currentLesson != null ? '${currentLesson!.lessonIndex}. ' : "",
currentLesson != null ? '${currentLesson!.lessonIndex}. ' : "",
"title": currentLesson != null
? currentLesson?.subject.renamedTo ??
ShortSubject.resolve(subject: currentLesson?.subject)
.capital()
ShortSubject.resolve(subject: currentLesson?.subject)
.capital()
: "",
"subtitle": currentLesson?.room.replaceAll("_", " ") ?? "",
"subtitle": "Terem: ${currentLesson?.room.replaceAll("_", " ") ?? ""}",
"description": currentLesson?.description ?? "",
"startDate": ((currentLesson?.start.millisecondsSinceEpoch ?? 0) -
_delay.inMilliseconds)
_delay.inMilliseconds)
.toString(),
"endDate": ((currentLesson?.end.millisecondsSinceEpoch ?? 0) -
_delay.inMilliseconds)
_delay.inMilliseconds)
.toString(),
"nextSubject": nextLesson != null
? nextLesson?.subject.renamedTo ??
ShortSubject.resolve(subject: nextLesson?.subject).capital()
ShortSubject.resolve(subject: nextLesson?.subject).capital()
: "",
"nextRoom": nextLesson?.room.replaceAll("_", " ") ?? "",
};
@ -200,23 +200,23 @@ class LiveCardProvider extends ChangeNotifier {
return {
"color":
'#${_settings.liveActivityColor.toString().substring(10, 16)}',
'#${_settings.liveActivityColor.toString().substring(10, 16)}',
"icon": iconFloorMap[diff] ?? "cup.and.saucer",
"title": "Szünet",
"description": "go $diff".i18n.fill([
diff != "to room" ? (nextLesson!.getFloor() ?? 0) : nextLesson!.room
]),
"startDate": ((prevLesson?.end.millisecondsSinceEpoch ?? 0) -
_delay.inMilliseconds)
_delay.inMilliseconds)
.toString(),
"endDate": ((nextLesson?.start.millisecondsSinceEpoch ?? 0) -
_delay.inMilliseconds)
_delay.inMilliseconds)
.toString(),
"nextSubject": (nextLesson != null
? nextLesson?.subject.renamedTo ??
ShortSubject.resolve(subject: nextLesson?.subject)
.capital()
: "")
? nextLesson?.subject.renamedTo ??
ShortSubject.resolve(subject: nextLesson?.subject)
.capital()
: "")
.capital(),
"nextRoom": nextLesson?.room.replaceAll("_", " ") ?? "",
"index": "",
@ -243,8 +243,8 @@ class LiveCardProvider extends ChangeNotifier {
DateTime now = _now().add(_delay);
if ((currentState == LiveCardState.morning ||
currentState == LiveCardState.afternoon ||
currentState == LiveCardState.night) &&
currentState == LiveCardState.afternoon ||
currentState == LiveCardState.night) &&
storeFirstRunDate == null) {
storeFirstRunDate = now;
}
@ -253,9 +253,9 @@ class LiveCardProvider extends ChangeNotifier {
// Filter label lessons #128
today = today
.where((lesson) =>
lesson.status?.name != "Elmaradt" &&
lesson.subject.id != '' &&
!lesson.isEmpty)
lesson.status?.name != "Elmaradt" &&
lesson.subject.id != '' &&
!lesson.isEmpty)
.toList();
if (today.isNotEmpty) {
@ -263,7 +263,7 @@ class LiveCardProvider extends ChangeNotifier {
today.sort((a, b) => a.start.compareTo(b.start));
final _lesson = today.firstWhere(
(l) => l.start.isBefore(now) && l.end.isAfter(now),
(l) => l.start.isBefore(now) && l.end.isAfter(now),
orElse: () => Lesson.fromJson({}));
if (_lesson.start.year != 0) {
@ -328,7 +328,7 @@ class LiveCardProvider extends ChangeNotifier {
hasActivityStarted = true;
} else if (!hasActivityStarted &&
((currentState == LiveCardState.duringLesson &&
currentLesson != null) ||
currentLesson != null) ||
currentState == LiveCardState.duringBreak)) {
debugPrint("Óra van, vagy szünet, de nincs LiveActivity. létrehozás...");
PlatformChannel.createLiveActivity(toMap());
@ -362,7 +362,12 @@ class LiveCardProvider extends ChangeNotifier {
}
//END
if (hasActivityStarted &&
if ((currentState == LiveCardState.afternoon || currentState == LiveCardState.morning || currentState == LiveCardState.night) && hasActivityStarted && nextLesson != null &&
nextLesson!.start.difference(now).inMinutes > 60) {
debugPrint("Több, mint 1 óra van az első óráig. Befejezés...");
PlatformChannel.endLiveActivity();
hasActivityStarted = false;
} else if (hasActivityStarted &&
!hasDayEnd &&
nextLesson == null &&
now.isAfter(prevLesson!.end)) {
@ -384,4 +389,4 @@ class LiveCardProvider extends ChangeNotifier {
List<Lesson> _today(TimetableProvider p) => (p.getWeek(Week.current()) ?? [])
.where((l) => _sameDate(l.date, _now()))
.toList();
}
}

View File

@ -7,17 +7,23 @@ import 'package:provider/provider.dart';
class SelfNoteProvider with ChangeNotifier {
late List<SelfNote> _notes;
late List<TodoItem> _todoItems;
late BuildContext _context;
List<SelfNote> get notes => _notes;
List<TodoItem> get todos => _todoItems;
SelfNoteProvider({
List<SelfNote> initialNotes = const [],
List<TodoItem> initialTodoItems = const [],
required BuildContext context,
}) {
_notes = List.castFrom(initialNotes);
_todoItems = List.castFrom(initialTodoItems);
_context = context;
if (_notes.isEmpty) restore();
if (_todoItems.isEmpty) restoreTodo();
}
// restore self notes from db
@ -38,6 +44,24 @@ class SelfNoteProvider with ChangeNotifier {
}
}
// restore todo items from db
Future<void> restoreTodo() async {
String? userId = Provider.of<UserProvider>(_context, listen: false).id;
// await Provider.of<DatabaseProvider>(_context, listen: false)
// .userStore
// .storeSelfNotes([], userId: userId!);
// load self notes from db
if (userId != null) {
var dbTodo = await Provider.of<DatabaseProvider>(_context, listen: false)
.userQuery
.getTodoItems(userId: userId);
_todoItems = dbTodo;
notifyListeners();
}
}
// fetches fresh data from api (not needed, cuz no api for that)
// Future<void> fetch() async {
// }
@ -54,4 +78,17 @@ class SelfNoteProvider with ChangeNotifier {
_notes = notes;
notifyListeners();
}
// store todo items in db
Future<void> storeTodo(List<TodoItem> todos) async {
User? user = Provider.of<UserProvider>(_context, listen: false).user;
if (user == null) throw "Cannot store Self Notes for User null";
String userId = user.id;
await Provider.of<DatabaseProvider>(_context, listen: false)
.userStore
.storeSelfTodoItems(todos, userId: userId);
_todoItems = todos;
notifyListeners();
}
}

View File

@ -23,6 +23,7 @@ class UserProvider with ChangeNotifier {
String? get nickname => user?.nickname;
String get picture => user?.picture ?? "";
String? get displayName => user?.displayName;
int? get gradeStreak => user?.gradeStreak;
final SettingsProvider _settings;

View File

@ -45,6 +45,7 @@ const settingsDB = DatabaseStruct("settings", {
// more
"show_breaks": int,
"font_family": String,
"title_only_font": int,
"plus_session_id": String,
"cal_sync_room_location": String, "cal_sync_show_exams": int,
"cal_sync_show_teacher": int, "cal_sync_renamed": int,
@ -52,6 +53,7 @@ const settingsDB = DatabaseStruct("settings", {
"nav_shadow": int,
"new_colors": int,
"uwu_mode": int,
"new_popups": int,
// quick settings
"q_timetable_lesson_num": int, "q_timetable_sub_tiles": int,
"q_subjects_sub_tiles": int,
@ -61,7 +63,8 @@ const settingsDB = DatabaseStruct("settings", {
const usersDB = DatabaseStruct("users", {
"id": String, "name": String, "username": String, "password": String,
"institute_code": String, "student": String, "role": int,
"nickname": String, "picture": String // premium only
"nickname": String, "picture": String, // premium only (it's now plus btw)
"grade_streak": int,
});
const userDataDB = DatabaseStruct("user_data", {
"id": String, "grades": String, "timetable": String, "exams": String,
@ -84,12 +87,13 @@ const userDataDB = DatabaseStruct("user_data", {
"goal_befores": String,
"goal_pin_dates": String,
// todo and notes
"todo_items": String, "self_notes": String,
"todo_items": String, "self_notes": String, "self_todo": String,
// v5 shit
"roundings": String,
"grade_rarities": String,
"linked_accounts": String,
"custom_lesson_desc": String,
"watch_data": String,
});
Future<void> createTable(Database db, DatabaseStruct struct) =>
@ -129,7 +133,12 @@ Future<Database> initDB(DatabaseProvider database) async {
await migrateDB(
db,
struct: usersDB,
defaultValues: {"role": 0, "nickname": "", "picture": ""},
defaultValues: {
"role": 0,
"nickname": "",
"picture": "",
"grade_streak": 0
},
);
await migrateDB(db, struct: userDataDB, defaultValues: {
"grades": "[]", "timetable": "[]", "exams": "[]", "homework": "[]",
@ -152,12 +161,13 @@ Future<Database> initDB(DatabaseProvider database) async {
"goal_befores": "{}",
"goal_pin_dates": "{}",
// todo and notes
"todo_items": "{}", "self_notes": "[]",
"todo_items": "{}", "self_notes": "[]", "self_todo": "[]",
// v5 shit
"roundings": "{}",
"grade_rarities": "{}",
"linked_accounts": "[]",
"custom_lesson_desc": "{}",
"watch_data": "{}",
});
} catch (error) {
print("ERROR: migrateDB: $error");

View File

@ -317,6 +317,18 @@ class UserDatabaseQuery {
return selfNotes;
}
Future<List<TodoItem>> getTodoItems({required String userId}) async {
List<Map> userData =
await db.query("user_data", where: "id = ?", whereArgs: [userId]);
if (userData.isEmpty) return [];
String? todoItemsJson = userData.elementAt(0)["self_todo"] as String?;
if (todoItemsJson == null) return [];
List<TodoItem> todoItems = (jsonDecode(todoItemsJson) as List)
.map((e) => TodoItem.fromJson(e))
.toList();
return todoItems;
}
// v5
Future<Map<String, String>> getRoundings({required String userId}) async {
List<Map> userData =

View File

@ -196,6 +196,13 @@ class UserDatabaseStore {
where: "id = ?", whereArgs: [userId]);
}
Future<void> storeSelfTodoItems(List<TodoItem> todoItems,
{required String userId}) async {
String todoItemsJson = jsonEncode(todoItems.map((e) => e.json).toList());
await db.update("user_data", {"self_todo": todoItemsJson},
where: "id = ?", whereArgs: [userId]);
}
// v5
Future<void> storeRoundings(Map<String, String> roundings,
{required String userId}) async {

View File

@ -212,7 +212,7 @@ nem lesz tőle használhatatlan az app, de kikommenteltem, mert még a végén k
),
notificationDetails,
payload: "grades");
} else if (settingsProvider.gradeOpeningFun) {
} else if (settingsProvider.gradeOpeningFun) {
// if surprise grades are enabled, show a notification without the grade
await flutterLocalNotificationsPlugin.show(
grade.id.hashCode,
@ -485,9 +485,10 @@ nem lesz tőle használhatatlan az app, de kikommenteltem, mert még a végén k
lesson.lessonIndex,
lesson.name,
dayTitle(lesson.date),
lesson.substituteTeacher!.isRenamed
? lesson.substituteTeacher!.renamedTo!
: lesson.substituteTeacher!.name
((lesson.substituteTeacher?.isRenamed ?? false)
? lesson.substituteTeacher?.renamedTo!
: lesson.substituteTeacher?.name) ??
'',
],
),
notificationDetails,
@ -505,9 +506,10 @@ nem lesz tőle használhatatlan az app, de kikommenteltem, mert még a végén k
dayTitle(lesson.date),
lesson.lessonIndex,
lesson.name,
lesson.substituteTeacher!.isRenamed
? lesson.substituteTeacher!.renamedTo!
: lesson.substituteTeacher!.name
((lesson.substituteTeacher?.isRenamed ?? false)
? lesson.substituteTeacher?.renamedTo!
: lesson.substituteTeacher?.name) ??
'',
],
),
notificationDetails,
@ -525,9 +527,10 @@ nem lesz tőle használhatatlan az app, de kikommenteltem, mert még a végén k
lesson.lessonIndex,
lesson.name,
dayTitle(lesson.date),
lesson.substituteTeacher!.isRenamed
? lesson.substituteTeacher!.renamedTo!
: lesson.substituteTeacher!.name
((lesson.substituteTeacher?.isRenamed ?? false)
? lesson.substituteTeacher?.renamedTo!
: lesson.substituteTeacher?.name) ??
'',
],
),
notificationDetails,
@ -608,9 +611,10 @@ nem lesz tőle használhatatlan az app, de kikommenteltem, mert még a végén k
lesson.lessonIndex,
lesson.name,
dayTitle(lesson.date),
lesson.substituteTeacher!.isRenamed
? lesson.substituteTeacher!.renamedTo!
: lesson.substituteTeacher!.name
((lesson.substituteTeacher?.isRenamed ?? false)
? lesson.substituteTeacher?.renamedTo!
: lesson.substituteTeacher?.name) ??
'',
],
),
notificationDetails,
@ -629,9 +633,10 @@ nem lesz tőle használhatatlan az app, de kikommenteltem, mert még a végén k
dayTitle(lesson.date),
lesson.lessonIndex,
lesson.name,
lesson.substituteTeacher!.isRenamed
? lesson.substituteTeacher!.renamedTo!
: lesson.substituteTeacher!.name
((lesson.substituteTeacher?.isRenamed ?? false)
? lesson.substituteTeacher?.renamedTo!
: lesson.substituteTeacher?.name) ??
'',
],
),
notificationDetails,

View File

@ -292,3 +292,105 @@ class ShortSubject {
return subject?.name.capital() ?? subjectName?.capital() ?? "?";
}
}
// new v5 thingie
class SubjectBooklet {
// static String resolveName({GradeSubject? subject, String? subjectName}) =>
// _resolve(subject: subject, subjectName: subjectName).name;
static String resolveVariant(
{GradeSubject? subject,
String? subjectName,
required BuildContext context}) =>
_resolve(subject: subject, subjectName: subjectName);
static String _resolve({GradeSubject? subject, String? subjectName}) {
assert(!(subject == null && subjectName == null));
String name = (subject?.name ?? subjectName ?? "")
.toLowerCase()
.specialChars()
.trim();
String category =
subject?.category.description.toLowerCase().specialChars() ?? "";
String basePath = "assets/svg/cover_arts";
// todo: check for categories
if (RegExp("mate(k|matika)").hasMatch(name) || category == "matematika") {
return "$basePath/grid.svg";
} else if (RegExp("magyar nyelv|nyelvtan").hasMatch(name)) {
return "$basePath/line.svg";
} else if (RegExp("irodalom").hasMatch(name)) {
return "$basePath/line.svg";
} else if (RegExp("tor(i|tenelem)").hasMatch(name)) {
return "$basePath/line.svg";
} else if (RegExp("foldrajz").hasMatch(name)) {
return "$basePath/plain.svg";
} else if (RegExp("rajz|muvtori|muveszet|vizualis").hasMatch(name)) {
return "$basePath/plain.svg";
} else if (RegExp("fizika").hasMatch(name)) {
return "$basePath/plain.svg";
} else if (RegExp("^enek|zene|szolfezs|zongora|korus").hasMatch(name)) {
return "$basePath/vocal.svg";
} else if (RegExp("^tes(i|tneveles)|sport|edzeselmelet").hasMatch(name)) {
return "$basePath/plain.svg";
} else if (RegExp("kemia").hasMatch(name)) {
return "$basePath/plain.svg";
} else if (RegExp("biologia").hasMatch(name)) {
return "$basePath/plain.svg";
} else if (RegExp(
"kornyezet|termeszet ?(tudomany|ismeret)|hon( es nep)?ismeret")
.hasMatch(name)) {
return "$basePath/plain.svg";
} else if (RegExp("(hit|erkolcs)tan|vallas|etika|bibliaismeret")
.hasMatch(name)) {
return "$basePath/line.svg";
} else if (RegExp("penzugy").hasMatch(name)) {
return "$basePath/plain.svg";
} else if (RegExp("informatika|szoftver|iroda|digitalis").hasMatch(name)) {
return "$basePath/grid.svg";
} else if (RegExp("prog").hasMatch(name)) {
return "$basePath/grid.svg";
} else if (RegExp("halozat").hasMatch(name)) {
return "$basePath/grid.svg";
} else if (RegExp("szinhaz").hasMatch(name)) {
return "$basePath/vocal.svg";
} else if (RegExp("film|media").hasMatch(name)) {
return "$basePath/plain.svg";
} else if (RegExp("elektro(tech)?nika").hasMatch(name)) {
return "$basePath/grid.svg";
} else if (RegExp("gepesz|mernok|ipar").hasMatch(name)) {
return "$basePath/grid.svg";
} else if (RegExp("technika").hasMatch(name)) {
return "$basePath/plain.svg";
} else if (RegExp("tanc").hasMatch(name)) {
return "$basePath/vocal.svg";
} else if (RegExp("filozofia").hasMatch(name)) {
return "$basePath/line.svg";
} else if (RegExp("osztaly(fonoki|kozosseg)|kozossegi|neveles")
.hasMatch(name) ||
name == "ofo") {
return "$basePath/plain.svg";
} else if (RegExp("gazdasag").hasMatch(name)) {
return "$basePath/line.svg";
} else if (RegExp("szorgalom").hasMatch(name)) {
return "$basePath/plain.svg";
} else if (RegExp("magatartas").hasMatch(name)) {
return "$basePath/plain.svg";
} else if (RegExp(
"angol|nemet|francia|olasz|orosz|spanyol|latin|kinai|nyelv")
.hasMatch(name)) {
return "$basePath/line.svg";
} else if (RegExp("linux").hasMatch(name)) {
return "$basePath/plain.svg";
} else if (RegExp("adatbazis").hasMatch(name)) {
return "$basePath/grid.svg";
} else if (RegExp("asztali alkalmazasok").hasMatch(name)) {
return "$basePath/grid.svg";
} else if (RegExp("projekt").hasMatch(name)) {
return "$basePath/plain.svg";
}
return "$basePath/plain.svg";
}
}

View File

@ -33,3 +33,37 @@ class SelfNote {
'note_type': noteType == NoteType.image ? 'image' : 'text',
};
}
class TodoItem {
String id;
String title;
String content;
bool done;
Map? json;
TodoItem({
required this.id,
required this.title,
required this.content,
required this.done,
this.json,
});
factory TodoItem.fromJson(Map json) {
return TodoItem(
id: json['id'],
title: json['title'],
content: json['content'],
done: json['done'],
json: json,
);
}
get toJson => {
'id': id,
'title': title,
'content': content,
'done': done,
};
}

View File

@ -96,6 +96,7 @@ class SettingsProvider extends ChangeNotifier {
// more
bool _showBreaks;
String _fontFamily;
bool _titleOnlyFont;
String _plusSessionId;
String _calSyncRoomLocation;
bool _calSyncShowExams;
@ -105,6 +106,7 @@ class SettingsProvider extends ChangeNotifier {
bool _navShadow;
bool _newColors;
bool _uwuMode;
bool _newPopups;
// quick settings
bool _qTimetableLessonNum;
bool _qTimetableSubTiles;
@ -167,6 +169,7 @@ class SettingsProvider extends ChangeNotifier {
required String pinSetNotify,
required String pinSetExtras,
required String fontFamily,
required bool titleOnlyFont,
required String plusSessionId,
required String calSyncRoomLocation,
required bool calSyncShowExams,
@ -176,6 +179,7 @@ class SettingsProvider extends ChangeNotifier {
required bool navShadow,
required bool newColors,
required bool uwuMode,
required bool newPopups,
required bool qTimetableLessonNum,
required bool qTimetableSubTiles,
required bool qSubjectsSubTiles,
@ -235,6 +239,7 @@ class SettingsProvider extends ChangeNotifier {
_pinSetNotify = pinSetNotify,
_pinSetExtras = pinSetExtras,
_fontFamily = fontFamily,
_titleOnlyFont = titleOnlyFont,
_plusSessionId = plusSessionId,
_calSyncRoomLocation = calSyncRoomLocation,
_calSyncShowExams = calSyncShowExams,
@ -244,6 +249,7 @@ class SettingsProvider extends ChangeNotifier {
_navShadow = navShadow,
_newColors = newColors,
_uwuMode = uwuMode,
_newPopups = newPopups,
_qTimetableLessonNum = qTimetableLessonNum,
_qTimetableSubTiles = qTimetableSubTiles,
_qSubjectsSubTiles = qSubjectsSubTiles;
@ -322,6 +328,7 @@ class SettingsProvider extends ChangeNotifier {
pinSetNotify: map['notify_s_pin'],
pinSetExtras: map['extras_s_pin'],
fontFamily: map['font_family'],
titleOnlyFont: map['title_only_font'] == 1,
plusSessionId: map['plus_session_id'],
calSyncRoomLocation: map['cal_sync_room_location'],
calSyncShowExams: map['cal_sync_show_exams'] == 1,
@ -331,6 +338,7 @@ class SettingsProvider extends ChangeNotifier {
navShadow: map['nav_shadow'] == 1,
newColors: map['new_colors'] == 1,
uwuMode: map['uwu_mode'] == 1,
newPopups: map['new_popups'] == 1,
qTimetableLessonNum: map['q_timetable_lesson_num'] == 1,
qTimetableSubTiles: map['q_timetable_sub_tiles'] == 1,
qSubjectsSubTiles: map['q_subjects_sub_tiles'] == 1,
@ -397,6 +405,7 @@ class SettingsProvider extends ChangeNotifier {
"notify_s_pin": _pinSetNotify,
"extras_s_pin": _pinSetExtras,
"font_family": _fontFamily,
"title_only_font": _titleOnlyFont ? 1 : 0,
"plus_session_id": _plusSessionId,
"cal_sync_room_location": _calSyncRoomLocation,
"cal_sync_show_exams": _calSyncShowExams ? 1 : 0,
@ -406,6 +415,7 @@ class SettingsProvider extends ChangeNotifier {
"nav_shadow": _navShadow ? 1 : 0,
"new_colors": _newColors ? 1 : 0,
"uwu_mode": _uwuMode ? 1 : 0,
"new_popups": _newPopups ? 1 : 0,
"q_timetable_lesson_num": _qTimetableLessonNum ? 1 : 0,
"q_timetable_sub_tiles": _qTimetableSubTiles ? 1 : 0,
"q_subjects_sub_tiles": _qSubjectsSubTiles ? 1 : 0,
@ -476,6 +486,7 @@ class SettingsProvider extends ChangeNotifier {
pinSetNotify: '',
pinSetExtras: '',
fontFamily: '',
titleOnlyFont: false,
plusSessionId: '',
calSyncRoomLocation: 'location',
calSyncShowExams: true,
@ -485,6 +496,7 @@ class SettingsProvider extends ChangeNotifier {
navShadow: true,
newColors: true,
uwuMode: false,
newPopups: true,
qTimetableLessonNum: true,
qTimetableSubTiles: true,
qSubjectsSubTiles: true,
@ -546,6 +558,7 @@ class SettingsProvider extends ChangeNotifier {
String get currentThemeCreator => _currentThemeCreator;
bool get showBreaks => _showBreaks;
String get fontFamily => _fontFamily;
bool get titleOnlyFont => _titleOnlyFont;
String get plusSessionId => _plusSessionId;
String get calSyncRoomLocation => _calSyncRoomLocation;
bool get calSyncShowExams => _calSyncShowExams;
@ -555,6 +568,7 @@ class SettingsProvider extends ChangeNotifier {
bool get navShadow => _navShadow;
bool get newColors => _newColors;
bool get uwuMode => _uwuMode;
bool get newPopups => _newPopups;
bool get qTimetableLessonNum => _qTimetableLessonNum;
bool get qTimetableSubTiles => _qTimetableSubTiles;
bool get qSubjectsSubTiles => _qSubjectsSubTiles;
@ -612,6 +626,7 @@ class SettingsProvider extends ChangeNotifier {
String? currentThemeCreator,
bool? showBreaks,
String? fontFamily,
bool? titleOnlyFont,
String? plusSessionId,
String? calSyncRoomLocation,
bool? calSyncShowExams,
@ -621,6 +636,7 @@ class SettingsProvider extends ChangeNotifier {
bool? navShadow,
bool? newColors,
bool? uwuMode,
bool? newPopups,
bool? qTimetableLessonNum,
bool? qTimetableSubTiles,
bool? qSubjectsSubTiles,
@ -777,6 +793,9 @@ class SettingsProvider extends ChangeNotifier {
if (fontFamily != null && fontFamily != _fontFamily) {
_fontFamily = fontFamily;
}
if (titleOnlyFont != null && titleOnlyFont != _titleOnlyFont) {
_titleOnlyFont = titleOnlyFont;
}
if (plusSessionId != null && plusSessionId != _plusSessionId) {
_plusSessionId = plusSessionId;
}
@ -806,6 +825,9 @@ class SettingsProvider extends ChangeNotifier {
if (uwuMode != null && uwuMode != _uwuMode) {
_uwuMode = uwuMode;
}
if (newPopups != null && newPopups != _newPopups) {
_newPopups = newPopups;
}
if (qTimetableLessonNum != null &&
qTimetableLessonNum != _qTimetableLessonNum) {
_qTimetableLessonNum = qTimetableLessonNum;

View File

@ -16,8 +16,10 @@ class User {
Role role;
String nickname;
String picture;
int gradeStreak;
String get displayName => nickname != '' ? nickname : name;
bool get hasStreak => gradeStreak > 0;
User({
String? id,
@ -29,6 +31,7 @@ class User {
required this.role,
this.nickname = "",
this.picture = "",
this.gradeStreak = 0,
}) {
if (id != null) {
this.id = id;
@ -57,6 +60,7 @@ class User {
role: Role.values[map["role"] ?? 0],
nickname: map["nickname"] ?? "",
picture: map["picture"] ?? "",
gradeStreak: map["grade_streak"] ?? 0,
);
}

View File

@ -104,9 +104,10 @@ class AppTheme {
brightness: Brightness.light,
useMaterial3: true,
fontFamily: _defaultFontFamily,
textTheme:
googleFontsMap[settings.fontFamily]?.apply(bodyColor: textColor) ??
const TextTheme().apply(bodyColor: textColor),
textTheme: !settings.titleOnlyFont
? (googleFontsMap[settings.fontFamily]?.apply(bodyColor: textColor) ??
const TextTheme().apply(bodyColor: textColor))
: null,
scaffoldBackgroundColor: backgroundColor,
primaryColor: lightColors.filc,
dividerColor: const Color(0x00000000),
@ -229,8 +230,9 @@ class AppTheme {
brightness: Brightness.dark,
useMaterial3: true,
fontFamily: _defaultFontFamily,
textTheme:
googleFontsMap[settings.fontFamily]?.apply(bodyColor: textColor),
textTheme: !settings.titleOnlyFont
? (googleFontsMap[settings.fontFamily]?.apply(bodyColor: textColor))
: null,
scaffoldBackgroundColor: backgroundColor,
primaryColor: darkColors.filc,
dividerColor: const Color(0x00000000),

View File

@ -192,6 +192,8 @@ class FilcColorPickerState extends State<FilcColorPicker> {
@override
Widget build(BuildContext context) {
bool advOverride = widget.colorMode == CustomColorMode.grade ? true : false;
if (MediaQuery.of(context).orientation == Orientation.portrait ||
widget.portraitOnly) {
return Column(
@ -218,7 +220,7 @@ class FilcColorPickerState extends State<FilcColorPicker> {
child: colorPickerSlider(TrackType.saturation),
),
),
if (isAdvancedView)
if (isAdvancedView || advOverride)
Padding(
padding: const EdgeInsets.only(left: 12.0, right: 12.0),
child: SizedBox(
@ -230,7 +232,7 @@ class FilcColorPickerState extends State<FilcColorPicker> {
],
),
),
if (isAdvancedView &&
if ((isAdvancedView || advOverride) &&
widget.colorMode != CustomColorMode.theme &&
widget.colorMode != CustomColorMode.enterId)
Padding(
@ -295,7 +297,8 @@ class FilcColorPickerState extends State<FilcColorPicker> {
],
),
),
if (widget.colorMode != CustomColorMode.enterId)
if (widget.colorMode != CustomColorMode.enterId &&
widget.colorMode != CustomColorMode.grade)
SizedBox(
height: 70 * (widget.colorMode == CustomColorMode.theme ? 2 : 1),
child: BlockPicker(
@ -347,7 +350,8 @@ class FilcColorPickerState extends State<FilcColorPicker> {
),
),
if (widget.colorMode != CustomColorMode.theme &&
widget.colorMode != CustomColorMode.enterId)
widget.colorMode != CustomColorMode.enterId &&
!advOverride)
Material(
color: Colors.transparent,
child: InkWell(

View File

@ -1,3 +1,4 @@
import 'package:i18n_extension/i18n_extension.dart';
import 'package:refilc/models/settings.dart';
import 'package:refilc_kreta_api/providers/exam_provider.dart';
import 'package:refilc_kreta_api/providers/homework_provider.dart';
@ -8,7 +9,8 @@ import 'package:refilc_kreta_api/models/lesson.dart';
import 'package:refilc/utils/format.dart';
import 'package:refilc_mobile_ui/common/panel/panel.dart';
import 'package:refilc_mobile_ui/common/round_border_icon.dart';
import 'package:refilc_mobile_ui/common/widgets/exam/exam_view.dart';
// import 'package:refilc_mobile_ui/common/widgets/exam/exam_view.dart';
import 'package:refilc_mobile_ui/common/widgets/exam/exam_viewable.dart';
import 'package:refilc_mobile_ui/common/widgets/homework/homework_view.dart';
import 'package:flutter/material.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
@ -109,7 +111,8 @@ class LessonTile extends StatelessWidget {
title: exam.description != ""
? exam.description
: exam.mode?.description ?? "exam".i18n,
onPressed: () => ExamView.show(exam, context: context),
// onPressed: () => ExamView.show(exam, context: context),
onPressed: () => ExamPopup.show(context: context, exam: exam),
));
}
}
@ -190,7 +193,7 @@ class LessonTile extends StatelessWidget {
: Transform.translate(
offset: const Offset(0, -2.0),
child: Text(
"${DateFormat("H:mm").format(lesson.start)}-${DateFormat("H:mm").format(lesson.end)}",
"${DateFormat("E, H:mm", I18n.of(context).locale.toString()).format(lesson.start)}-${DateFormat("H:mm").format(lesson.end)}",
textAlign: TextAlign.start,
style: TextStyle(
fontWeight: FontWeight.w600,

View File

@ -3,7 +3,7 @@ description: "Egy nem hivatalos e-KRÉTA kliens, diákoktól diákoknak."
homepage: https://refilc.hu
publish_to: "none"
version: 5.0.0+257
version: 5.0.1+265
environment:
sdk: ">=2.17.0 <=3.3.2"
@ -104,6 +104,7 @@ flutter:
- assets/svg/menu_icons/
- assets/other/dirtywords.xml
- assets/svg/
- assets/svg/cover_arts/
fonts:
- family: FilcIcons

View File

@ -268,7 +268,7 @@ class GradesPageState extends State<GradesPage> {
title: Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Text(
"Grades".i18n,
"page_title_grades".i18n,
style: TextStyle(
color: AppColors.of(context).text,
fontSize: 32.0,

View File

@ -11,6 +11,7 @@ class Student {
String? address;
String? groupId;
List<String> parents;
// List<String> parentsPhone;
String? className;
Student({
@ -21,6 +22,7 @@ class Student {
required this.yearId,
this.address,
required this.parents,
// required this.parentsPhone,
this.json,
});

View File

@ -50,7 +50,8 @@ class GradeProvider with ChangeNotifier {
String? userId = _user.id;
if (userId != null) {
final userStore = _database.userStore;
userStore.storeLastSeen(DateTime.now(), userId: userId, category: LastSeenCategory.surprisegrade);
userStore.storeLastSeen(DateTime.now(),
userId: userId, category: LastSeenCategory.surprisegrade);
_lastSeen = DateTime.now();
}
}
@ -59,7 +60,8 @@ class GradeProvider with ChangeNotifier {
String? userId = _user.id;
if (userId != null) {
final userStore = _database.userStore;
userStore.storeLastSeen(DateTime(1969), userId: userId, category: LastSeenCategory.surprisegrade);
userStore.storeLastSeen(DateTime(1969),
userId: userId, category: LastSeenCategory.surprisegrade);
_lastSeen = DateTime(1969);
}
}
@ -73,9 +75,11 @@ class GradeProvider with ChangeNotifier {
_grades = await userQuery.getGrades(userId: userId);
await convertBySettings();
await getGradeStreak();
_groupAvg = await userQuery.getGroupAverages(userId: userId);
notifyListeners();
DateTime lastSeenDB = await userQuery.lastSeen(userId: userId, category: LastSeenCategory.surprisegrade);
DateTime lastSeenDB = await userQuery.lastSeen(
userId: userId, category: LastSeenCategory.surprisegrade);
if (lastSeenDB.millisecondsSinceEpoch == 0 ||
lastSeenDB.year == 0 ||
!_settings.gradeOpeningFun) {
@ -133,6 +137,30 @@ class GradeProvider with ChangeNotifier {
notifyListeners();
}
// get current grade streak and set it to the user
Future<void> getGradeStreak() async {
User? user = _user.user;
if (user == null) throw "Cannot get Grade Streak for User null";
// streak magic
int gradeStreak = 0;
List<Grade> grs = _grades;
grs.sort((a, b) => -a.date.compareTo(b.date));
for (Grade grade in grs) {
if (grade.value.value == 5) {
gradeStreak++;
} else {
break;
}
}
print(gradeStreak);
user.gradeStreak = gradeStreak;
notifyListeners();
}
// Fetches Grades from the Kreta API then stores them in the database
Future<void> fetch() async {
// test cucc
@ -173,6 +201,7 @@ class GradeProvider with ChangeNotifier {
await _database.userStore.storeGrades(grades, userId: userId);
_grades = grades;
await convertBySettings();
await getGradeStreak();
}
Future<void> storeGroupAvg(List<GroupAverage> groupAvgs) async {

View File

@ -19,11 +19,13 @@ class ShareProvider extends ChangeNotifier {
// }
// themes
Future<SharedTheme> shareCurrentTheme(BuildContext context,
{bool isPublic = false,
bool shareNick = true,
required SharedGradeColors gradeColors,
String displayName = ''}) async {
Future<SharedTheme> shareCurrentTheme(
BuildContext context, {
bool isPublic = false,
bool shareNick = true,
required SharedGradeColors gradeColors,
String displayName = '',
}) async {
final SettingsProvider settings =
Provider.of<SettingsProvider>(context, listen: false);
@ -69,7 +71,25 @@ class ShareProvider extends ChangeNotifier {
if (gradeColorsJson != null) {
SharedTheme theme = SharedTheme.fromJson(
themeJson, SharedGradeColors.fromJson(gradeColorsJson));
themeJson,
SharedGradeColors.fromJson(gradeColorsJson["public_id"] != ''
? gradeColorsJson
: {
"public_id": "0",
"is_public": false,
"nickname": "Anonymous",
"five_color":
SettingsProvider.defaultSettings().gradeColors[4].value,
"four_color":
SettingsProvider.defaultSettings().gradeColors[3].value,
"three_color":
SettingsProvider.defaultSettings().gradeColors[2].value,
"two_color":
SettingsProvider.defaultSettings().gradeColors[1].value,
"one_color":
SettingsProvider.defaultSettings().gradeColors[0].value,
}),
);
return theme;
}
}
@ -93,7 +113,25 @@ class ShareProvider extends ChangeNotifier {
if (gradeColorsJson != null) {
SharedTheme theme = SharedTheme.fromJson(
t, SharedGradeColors.fromJson(gradeColorsJson));
t,
SharedGradeColors.fromJson(gradeColorsJson["public_id"] != ''
? gradeColorsJson
: {
"public_id": "0",
"is_public": false,
"nickname": "Anonymous",
"five_color":
SettingsProvider.defaultSettings().gradeColors[4].value,
"four_color":
SettingsProvider.defaultSettings().gradeColors[3].value,
"three_color":
SettingsProvider.defaultSettings().gradeColors[2].value,
"two_color":
SettingsProvider.defaultSettings().gradeColors[1].value,
"one_color":
SettingsProvider.defaultSettings().gradeColors[0].value,
}),
);
themes.add(theme);
}
@ -134,8 +172,25 @@ class ShareProvider extends ChangeNotifier {
Map? gradeColorsJson = await FilcAPI.getSharedGradeColors(id);
if (gradeColorsJson != null) {
SharedGradeColors gradeColors =
SharedGradeColors.fromJson(gradeColorsJson);
SharedGradeColors gradeColors = SharedGradeColors.fromJson(
gradeColorsJson["public_id"] != ''
? gradeColorsJson
: {
"public_id": "0",
"is_public": false,
"nickname": "Anonymous",
"five_color":
SettingsProvider.defaultSettings().gradeColors[4].value,
"four_color":
SettingsProvider.defaultSettings().gradeColors[3].value,
"three_color":
SettingsProvider.defaultSettings().gradeColors[2].value,
"two_color":
SettingsProvider.defaultSettings().gradeColors[1].value,
"one_color":
SettingsProvider.defaultSettings().gradeColors[0].value,
},
);
return gradeColors;
}

View File

@ -1,13 +1,22 @@
import 'package:dotted_border/dotted_border.dart';
import 'package:refilc/theme/colors/colors.dart';
import 'package:refilc/ui/widgets/grade/grade_tile.dart';
import 'package:flutter/material.dart';
import 'package:i18n_extension/i18n_extension.dart';
class AverageDisplay extends StatelessWidget {
const AverageDisplay({super.key, this.average = 0.0, this.border = false});
const AverageDisplay({
super.key,
this.average = 0.0,
this.border = false,
this.dashed = false,
this.scale = 1.0,
});
final double average;
final bool border;
final bool dashed;
final double scale;
@override
Widget build(BuildContext context) {
@ -20,25 +29,44 @@ class AverageDisplay extends StatelessWidget {
averageText = averageText.replaceAll(".", ",");
}
Widget txtWidget = Text(
average == 0.0 ? "-" : averageText,
textAlign: TextAlign.center,
style: TextStyle(
color: color, fontWeight: FontWeight.w600, fontSize: scale * 15.0),
maxLines: 1,
);
return Container(
width: border ? 57.0 : 54.0,
padding: EdgeInsets.symmetric(
horizontal: 6.0 - (border ? 2 : 0), vertical: 5.0 - (border ? 2 : 0)),
width: (border ? 57.0 : 54.0) * scale,
padding: (border && dashed)
? null
: EdgeInsets.symmetric(
horizontal: (6.0 - (border ? 2 : 0)) * scale,
vertical: (5.0 - (border ? 2 : 0))) *
scale,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(45.0),
border: border
borderRadius:
(border && dashed) ? null : BorderRadius.circular(45.0 * scale),
border: border && !dashed
? Border.fromBorderSide(
BorderSide(color: color.withOpacity(.5), width: 1.0))
BorderSide(color: color.withOpacity(.5), width: 1.0 * scale))
: null,
color: !border ? color.withOpacity(average == 0.0 ? .15 : .25) : null,
),
child: Text(
average == 0.0 ? "-" : averageText,
textAlign: TextAlign.center,
style: TextStyle(
color: color, fontWeight: FontWeight.w600, fontSize: 15.0),
maxLines: 1,
),
child: (border && dashed)
? DottedBorder(
strokeWidth: 1.0 * scale,
padding: EdgeInsets.all(4.0 * scale),
color: color.withOpacity(.5),
dashPattern: const [6, 6],
radius: Radius.circular(45.0 * scale),
borderType: BorderType.RRect,
child: Center(
child: txtWidget,
),
)
: txtWidget,
);
}
}

View File

@ -1,4 +1,5 @@
import 'package:flutter_svg/svg.dart';
import 'package:refilc/helpers/subject.dart';
import 'package:refilc/theme/colors/colors.dart';
import 'package:flutter/material.dart';
import 'package:refilc/theme/colors/utils.dart';
@ -113,7 +114,9 @@ class HeroScrollViewState extends State<HeroScrollView> {
Padding(
padding: const EdgeInsets.only(top: 26.0),
child: SvgPicture.asset(
"assets/svg/mesh_bg.svg",
// "assets/svg/mesh_bg.svg",
SubjectBooklet.resolveVariant(
context: context, subjectName: widget.title),
// ignore: deprecated_member_use
color: ColorsUtils()
.darken(

View File

@ -49,6 +49,8 @@ extension Localization on String {
"d_npc":
"You're such a non-player character, we couldn't give you a personality.",
"s_npc": "In-game playtime (hours)",
// other
"year_index": "Lesson Number",
},
"hu_hu": {
// main
@ -96,6 +98,8 @@ extension Localization on String {
"d_npc":
"Egy akkora nagy non-player character vagy, hogy neked semmilyen személyiség nem jutott ezen kívül.",
"s_npc": "In-game playtime (óra)",
// other
"year_index": "Éves óraszám",
},
"de_de": {
// main
@ -144,6 +148,8 @@ extension Localization on String {
"d_npc":
"Du bist einfach so sehr wie ein Computer, dass wir dir nicht einmal eine Persönlichkeit geben konnten.",
"s_npc": "Spielzeit (Stunden)",
// other
"year_index": "Ordinalzahl",
}
};

View File

@ -65,6 +65,7 @@ class ProfileButton extends StatelessWidget {
radius: child.radius,
badge: child.badge,
role: child.role,
gradeStreak: child.gradeStreak,
profilePictureString: child.profilePictureString,
onTap: () {
showSlidingBottomSheet(

View File

@ -21,6 +21,7 @@ class ProfileImage extends StatefulWidget {
this.censored = false,
this.profilePictureString = "",
this.isNotePfp = false,
this.gradeStreak = false,
});
final void Function()? onTap;
@ -35,6 +36,7 @@ class ProfileImage extends StatefulWidget {
final bool censored;
final String profilePictureString;
final bool isNotePfp;
final bool gradeStreak;
@override
State<ProfileImage> createState() => _ProfileImageState();
@ -145,6 +147,20 @@ class _ProfileImageState extends State<ProfileImage> {
color: roleColor, size: widget.radius / 1.3),
),
),
// streak indicator
// if (widget.gradeStreak)
// SizedBox(
// height: widget.radius * 2,
// width: widget.radius * 2,
// child: Container(
// alignment: Alignment.topLeft,
// child: Text(
// '🔥',
// style: TextStyle(fontSize: widget.radius * 0.9),
// ),
// ),
// ),
],
);
}
@ -238,6 +254,29 @@ class _ProfileImageState extends State<ProfileImage> {
),
),
// streak indicator
if (widget.gradeStreak)
Hero(
tag: "${widget.heroTag!}streak_indicator",
child: FittedBox(
fit: BoxFit.fitHeight,
child: SizedBox(
height: widget.radius * 2,
width: widget.radius * 2,
child: Transform.translate(
offset: Offset(-widget.radius / 4, -widget.radius / 4),
child: Container(
alignment: Alignment.topLeft,
child: Text(
'🔥',
style: TextStyle(fontSize: widget.radius * 0.8),
),
),
),
),
),
),
Material(
color: Colors.transparent,
clipBehavior: Clip.hardEdge,

View File

@ -1,6 +1,7 @@
// ignore_for_file: use_build_context_synchronously
import 'package:flutter_svg/svg.dart';
import 'package:i18n_extension/i18n_extension.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:refilc/helpers/subject.dart';
@ -13,8 +14,11 @@ import 'package:refilc_kreta_api/models/exam.dart';
import 'package:refilc_kreta_api/models/lesson.dart';
import 'package:refilc_mobile_ui/common/bottom_sheet_menu/rounded_bottom_sheet.dart';
import 'package:refilc_mobile_ui/common/round_border_icon.dart';
import 'package:refilc_mobile_ui/common/viewable.dart';
import 'package:refilc_mobile_ui/common/widgets/card_handle.dart';
import 'package:refilc_mobile_ui/common/widgets/exam/exam_tile.dart';
import 'package:flutter/material.dart';
import 'package:refilc_mobile_ui/common/widgets/exam/exam_view.dart';
class ExamViewable extends StatelessWidget {
const ExamViewable(this.exam,
@ -26,22 +30,25 @@ class ExamViewable extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => ExamPopup.show(context: context, exam: exam),
child: ExamTile(
if (Provider.of<SettingsProvider>(context).newPopups) {
return GestureDetector(
onTap: () => ExamPopup.show(context: context, exam: exam),
child: ExamTile(
exam,
showSubject: showSubject,
padding: tilePadding,
),
);
}
return Viewable(
tile: ExamTile(
exam,
showSubject: showSubject,
padding: tilePadding,
),
view: CardHandle(child: ExamView(exam)),
);
// return Viewable(
// tile: ExamTile(
// exam,
// showSubject: showSubject,
// padding: tilePadding,
// ),
// view: CardHandle(child: ExamView(exam)),
// );
}
}
@ -98,7 +105,9 @@ class ExamPopup extends StatelessWidget {
Stack(
children: [
SvgPicture.asset(
"assets/svg/mesh_bg.svg",
// "assets/svg/mesh_bg.svg",
SubjectBooklet.resolveVariant(
context: context, subject: exam.subject),
// ignore: deprecated_member_use
color: ColorsUtils()
.fade(context, Theme.of(context).colorScheme.secondary,
@ -210,13 +219,19 @@ class ExamPopup extends StatelessWidget {
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
exam.description.capital(),
style: TextStyle(
color:
AppColors.of(context).text.withOpacity(0.9),
fontSize: 16.0,
fontWeight: FontWeight.w600,
SizedBox(
width: MediaQuery.of(context).size.width * 0.7,
child: Text(
exam.description.capital(),
style: TextStyle(
color: AppColors.of(context)
.text
.withOpacity(0.9),
fontSize: 16.0,
fontWeight: FontWeight.w600,
),
maxLines: 3,
overflow: TextOverflow.ellipsis,
),
),
Text(
@ -288,7 +303,7 @@ class ExamPopup extends StatelessWidget {
],
),
Text(
'${DateFormat('H:mm').format(lesson!.start)} - ${DateFormat('H:mm').format(lesson!.end)}',
'${DateFormat('MMM d, H:mm', I18n.locale.countryCode).format(lesson!.start).capital()} - ${DateFormat('H:mm').format(lesson!.end)}',
style: TextStyle(
color:
AppColors.of(context).text.withOpacity(0.85),

View File

@ -12,6 +12,7 @@ extension Localization on String {
"l_desc": "Description...",
"done": "Done",
"cancel": "Cancel",
"year_index": "Lesson Number",
},
"hu_hu": {
"Room": "Terem",
@ -22,6 +23,7 @@ extension Localization on String {
"l_desc": "Leírás...",
"done": "Kész",
"cancel": "Mégse",
"year_index": "Éves óraszám",
},
"de_de": {
"Room": "Raum",
@ -32,6 +34,7 @@ extension Localization on String {
"l_desc": "Beschreibung...",
"done": "Erledigt",
"cancel": "Abbrechen",
"year_index": "Ordinalzahl",
}
};

View File

@ -1,3 +1,4 @@
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:flutter_svg/svg.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
@ -8,11 +9,21 @@ import 'package:refilc/models/settings.dart';
import 'package:refilc/theme/colors/colors.dart';
import 'package:refilc/theme/colors/utils.dart';
import 'package:refilc/utils/format.dart';
import 'package:refilc_kreta_api/models/exam.dart';
import 'package:refilc_kreta_api/models/lesson.dart';
import 'package:refilc_kreta_api/providers/exam_provider.dart';
import 'package:refilc_mobile_ui/common/bottom_sheet_menu/rounded_bottom_sheet.dart';
import 'package:refilc_mobile_ui/common/panel/panel_button.dart';
import 'package:refilc_mobile_ui/common/round_border_icon.dart';
import 'package:refilc/ui/widgets/lesson/lesson_tile.dart';
import 'package:flutter/material.dart';
import 'package:refilc_mobile_ui/common/viewable.dart';
import 'package:refilc_mobile_ui/common/widgets/card_handle.dart';
import 'package:refilc_mobile_ui/common/widgets/lesson/lesson_view.dart';
import 'package:refilc_plus/models/premium_scopes.dart';
import 'package:refilc_plus/providers/plus_provider.dart';
import 'package:refilc_plus/ui/mobile/plus/upsell.dart';
import 'lesson_view.i18n.dart';
class LessonViewable extends StatefulWidget {
const LessonViewable(
@ -59,174 +70,169 @@ class LessonViewableState extends State<LessonViewable> {
if (lsn.subject.id == '' || tile.lesson.isEmpty) return tile;
return GestureDetector(
onTap: () => TimetableLessonPopup.show(
context: context,
lesson: lsn,
),
child: LessonTile(
lsn,
swapDesc: widget.swapDesc,
showSubTiles: widget.showSubTiles,
// onTap: () => TimetableLessonPopup.show(
// context: context,
// lesson: lsn,
// ),
),
// check if new popup needed
if (Provider.of<SettingsProvider>(context).newPopups) {
return GestureDetector(
onTap: () => TimetableLessonPopup.show(
context: context,
lesson: lsn,
),
child: tile,
);
}
return Viewable(
tile: tile,
view: CardHandle(child: LessonView(lsn)),
actions: [
PanelButton(
background: true,
title: Text(
"edit_lesson".i18n,
textAlign: TextAlign.center,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
onPressed: () {
Navigator.of(context, rootNavigator: true).pop();
if (!Provider.of<PlusProvider>(context, listen: false)
.hasScope(PremiumScopes.timetableNotes)) {
PlusLockedFeaturePopup.show(
context: context, feature: PremiumFeature.timetableNotes);
return;
}
showDialog(
context: context,
builder: (context) => StatefulBuilder(builder: (context, setS) {
return AlertDialog(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(14.0))),
title: Text("edit_lesson".i18n),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
// description
TextField(
controller: _descTxt,
decoration: InputDecoration(
border: OutlineInputBorder(
borderSide: const BorderSide(
color: Colors.grey, width: 1.5),
borderRadius: BorderRadius.circular(12.0),
),
focusedBorder: OutlineInputBorder(
borderSide: const BorderSide(
color: Colors.grey, width: 1.5),
borderRadius: BorderRadius.circular(12.0),
),
contentPadding:
const EdgeInsets.symmetric(horizontal: 12.0),
hintText: 'l_desc'.i18n,
suffixIcon: IconButton(
icon: const Icon(
FeatherIcons.x,
color: Colors.grey,
size: 18.0,
),
onPressed: () {
setState(() {
_descTxt.text = '';
});
},
),
),
),
// const SizedBox(
// height: 14.0,
// ),
// // class
// TextField(
// controller: _descTxt,
// onEditingComplete: () async {
// // SharedTheme? theme = await shareProvider.getThemeById(
// // context,
// // id: _paintId.text.replaceAll(' ', ''),
// // );
// // if (theme != null) {
// // // set theme variable
// // newThemeByID = theme;
// // _paintId.clear();
// // } else {
// // ScaffoldMessenger.of(context).showSnackBar(
// // CustomSnackBar(
// // content: Text("theme_not_found".i18n,
// // style: const TextStyle(color: Colors.white)),
// // backgroundColor: AppColors.of(context).red,
// // context: context,
// // ),
// // );
// // }
// },
// decoration: InputDecoration(
// border: OutlineInputBorder(
// borderSide: const BorderSide(
// color: Colors.grey, width: 1.5),
// borderRadius: BorderRadius.circular(12.0),
// ),
// focusedBorder: OutlineInputBorder(
// borderSide: const BorderSide(
// color: Colors.grey, width: 1.5),
// borderRadius: BorderRadius.circular(12.0),
// ),
// contentPadding:
// const EdgeInsets.symmetric(horizontal: 12.0),
// hintText: 'l_desc'.i18n,
// suffixIcon: IconButton(
// icon: const Icon(
// FeatherIcons.x,
// color: Colors.grey,
// size: 18.0,
// ),
// onPressed: () {
// setState(() {
// _descTxt.text = '';
// });
// },
// ),
// ),
// ),
],
),
actions: [
TextButton(
child: Text(
"cancel".i18n,
style: const TextStyle(fontWeight: FontWeight.w500),
),
onPressed: () {
Navigator.of(context).maybePop();
},
),
TextButton(
child: Text(
"done".i18n,
style: const TextStyle(fontWeight: FontWeight.w500),
),
onPressed: () async {
saveLesson();
Navigator.of(context).pop();
setState(() {});
},
),
],
);
}),
);
},
),
],
);
// return Viewable(
// tile: tile,
// view: CardHandle(child: LessonView(lsn)),
// actions: [
// PanelButton(
// background: true,
// title: Text(
// "edit_lesson".i18n,
// textAlign: TextAlign.center,
// maxLines: 2,
// overflow: TextOverflow.ellipsis,
// ),
// onPressed: () {
// Navigator.of(context, rootNavigator: true).pop();
// if (!Provider.of<PlusProvider>(context, listen: false)
// .hasScope(PremiumScopes.timetableNotes)) {
// PlusLockedFeaturePopup.show(
// context: context, feature: PremiumFeature.timetableNotes);
// return;
// }
// showDialog(
// context: context,
// builder: (context) => StatefulBuilder(builder: (context, setS) {
// return AlertDialog(
// shape: const RoundedRectangleBorder(
// borderRadius: BorderRadius.all(Radius.circular(14.0))),
// title: Text("edit_lesson".i18n),
// content: Column(
// mainAxisSize: MainAxisSize.min,
// children: [
// // description
// TextField(
// controller: _descTxt,
// decoration: InputDecoration(
// border: OutlineInputBorder(
// borderSide: const BorderSide(
// color: Colors.grey, width: 1.5),
// borderRadius: BorderRadius.circular(12.0),
// ),
// focusedBorder: OutlineInputBorder(
// borderSide: const BorderSide(
// color: Colors.grey, width: 1.5),
// borderRadius: BorderRadius.circular(12.0),
// ),
// contentPadding:
// const EdgeInsets.symmetric(horizontal: 12.0),
// hintText: 'l_desc'.i18n,
// suffixIcon: IconButton(
// icon: const Icon(
// FeatherIcons.x,
// color: Colors.grey,
// size: 18.0,
// ),
// onPressed: () {
// setState(() {
// _descTxt.text = '';
// });
// },
// ),
// ),
// ),
// // const SizedBox(
// // height: 14.0,
// // ),
// // // class
// // TextField(
// // controller: _descTxt,
// // onEditingComplete: () async {
// // // SharedTheme? theme = await shareProvider.getThemeById(
// // // context,
// // // id: _paintId.text.replaceAll(' ', ''),
// // // );
// // // if (theme != null) {
// // // // set theme variable
// // // newThemeByID = theme;
// // // _paintId.clear();
// // // } else {
// // // ScaffoldMessenger.of(context).showSnackBar(
// // // CustomSnackBar(
// // // content: Text("theme_not_found".i18n,
// // // style: const TextStyle(color: Colors.white)),
// // // backgroundColor: AppColors.of(context).red,
// // // context: context,
// // // ),
// // // );
// // // }
// // },
// // decoration: InputDecoration(
// // border: OutlineInputBorder(
// // borderSide: const BorderSide(
// // color: Colors.grey, width: 1.5),
// // borderRadius: BorderRadius.circular(12.0),
// // ),
// // focusedBorder: OutlineInputBorder(
// // borderSide: const BorderSide(
// // color: Colors.grey, width: 1.5),
// // borderRadius: BorderRadius.circular(12.0),
// // ),
// // contentPadding:
// // const EdgeInsets.symmetric(horizontal: 12.0),
// // hintText: 'l_desc'.i18n,
// // suffixIcon: IconButton(
// // icon: const Icon(
// // FeatherIcons.x,
// // color: Colors.grey,
// // size: 18.0,
// // ),
// // onPressed: () {
// // setState(() {
// // _descTxt.text = '';
// // });
// // },
// // ),
// // ),
// // ),
// ],
// ),
// actions: [
// TextButton(
// child: Text(
// "cancel".i18n,
// style: const TextStyle(fontWeight: FontWeight.w500),
// ),
// onPressed: () {
// Navigator.of(context).maybePop();
// },
// ),
// TextButton(
// child: Text(
// "done".i18n,
// style: const TextStyle(fontWeight: FontWeight.w500),
// ),
// onPressed: () async {
// saveLesson();
// Navigator.of(context).pop();
// setState(() {});
// },
// ),
// ],
// );
// }),
// );
// },
// ),
// ],
// );
}
void saveLesson() async {
@ -283,6 +289,18 @@ class TimetableLessonPopup extends StatelessWidget {
@override
Widget build(BuildContext context) {
Exam? lessonExam;
if (lesson.exam != "") {
Exam exam = Provider.of<ExamProvider>(context, listen: false)
.exams
.firstWhere((t) => t.id == lesson.exam,
orElse: () => Exam.fromJson({}));
if (exam.id != "") {
lessonExam = exam;
}
}
return Container(
decoration: BoxDecoration(
color: Theme.of(context).scaffoldBackgroundColor,
@ -295,7 +313,9 @@ class TimetableLessonPopup extends StatelessWidget {
Stack(
children: [
SvgPicture.asset(
"assets/svg/mesh_bg.svg",
// "assets/svg/mesh_bg.svg",
SubjectBooklet.resolveVariant(
context: context, subject: lesson.subject),
// ignore: deprecated_member_use
color: ColorsUtils()
.fade(context, Theme.of(context).colorScheme.secondary,
@ -319,13 +339,13 @@ class TimetableLessonPopup extends StatelessWidget {
.withOpacity(0.1),
Theme.of(context).scaffoldBackgroundColor,
],
stops: const [0.1, 0.5, 0.7, 1.0],
stops: const [0.0, 0.3, 0.6, 0.95],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
),
width: MediaQuery.of(context).size.width,
height: 175.0,
height: 200.0,
),
],
),
@ -340,16 +360,27 @@ class TimetableLessonPopup extends StatelessWidget {
width: 40,
height: 4,
decoration: BoxDecoration(
color: ColorsUtils()
.fade(
context, Theme.of(context).colorScheme.secondary,
darkenAmount: 0.1, lightenAmount: 0.1)
.withOpacity(0.33),
color: Theme.of(context).scaffoldBackgroundColor,
borderRadius: BorderRadius.circular(
2.0,
),
),
child: Container(
width: 40,
height: 4,
decoration: BoxDecoration(
color: ColorsUtils()
.fade(context,
Theme.of(context).colorScheme.secondary,
darkenAmount: 0.1, lightenAmount: 0.1)
.withOpacity(0.33),
borderRadius: BorderRadius.circular(
2.0,
),
),
),
),
const SizedBox(
height: 38.0,
),
@ -387,11 +418,9 @@ class TimetableLessonPopup extends StatelessWidget {
width: double.infinity,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background,
borderRadius: BorderRadius.vertical(
top: const Radius.circular(12.0),
bottom: (lesson.description.replaceAll(' ', '') != '')
? const Radius.circular(6.0)
: const Radius.circular(12.0),
borderRadius: const BorderRadius.vertical(
top: Radius.circular(12.0),
bottom: Radius.circular(6.0),
),
),
padding: const EdgeInsets.all(14.0),
@ -466,7 +495,15 @@ class TimetableLessonPopup extends StatelessWidget {
height: 8.0,
),
Text(
lesson.teacher.name,
((lesson.substituteTeacher == null ||
lesson.substituteTeacher!.name == "")
? (lesson.teacher.isRenamed
? lesson.teacher.renamedTo
: lesson.teacher.name)
: (lesson.substituteTeacher!.isRenamed
? lesson.substituteTeacher!.renamedTo
: lesson.substituteTeacher!.name)) ??
'',
style: TextStyle(
color: AppColors.of(context).text.withOpacity(0.9),
fontSize: 14.0,
@ -487,7 +524,7 @@ class TimetableLessonPopup extends StatelessWidget {
color: Theme.of(context).colorScheme.background,
borderRadius: const BorderRadius.vertical(
top: Radius.circular(6.0),
bottom: Radius.circular(12.0),
bottom: Radius.circular(6.0),
),
),
padding: const EdgeInsets.all(14.0),
@ -506,6 +543,94 @@ class TimetableLessonPopup extends StatelessWidget {
],
),
),
const SizedBox(
height: 6.0,
),
Container(
width: double.infinity,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background,
borderRadius: BorderRadius.vertical(
top: const Radius.circular(6.0),
bottom: lesson.exam != ''
? const Radius.circular(6.0)
: const Radius.circular(12.0),
),
),
padding: const EdgeInsets.all(14.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${'year_index'.i18n}: ${lesson.lessonYearIndex ?? '?'}',
style: TextStyle(
color: AppColors.of(context).text.withOpacity(0.9),
fontSize: 14.0,
fontWeight: FontWeight.w600,
),
),
],
),
),
if (lesson.exam != '')
const SizedBox(
height: 6.0,
),
if (lesson.exam != '' && lessonExam != null)
Container(
width: double.infinity,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background,
borderRadius: const BorderRadius.vertical(
top: Radius.circular(6.0),
bottom: Radius.circular(12.0)),
),
padding: const EdgeInsets.all(14.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
const Icon(
FeatherIcons.file,
size: 20.0,
),
const SizedBox(
width: 10.0,
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.5,
child: Text(
lessonExam.description.capital(),
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: AppColors.of(context)
.text
.withOpacity(0.9),
fontSize: 16.0,
fontWeight: FontWeight.w600,
),
),
),
],
),
Flexible(
child: Text(
lessonExam.mode?.description ?? 'Dolgozat',
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: AppColors.of(context)
.text
.withOpacity(0.85),
fontSize: 14.0,
fontWeight: FontWeight.w500,
),
),
),
],
),
),
// const SizedBox(
// height: 24.0,
// ),

View File

@ -4,7 +4,9 @@ import 'dart:math';
import 'package:animations/animations.dart';
import 'package:collection/collection.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:refilc/api/providers/update_provider.dart';
import 'package:refilc/models/settings.dart';
import 'package:refilc/theme/colors/utils.dart';
import 'package:refilc/ui/date_widget.dart';
import 'package:refilc_kreta_api/models/absence.dart';
@ -169,6 +171,7 @@ class AbsencesPageState extends State<AbsencesPage>
badge: updateProvider.available,
role: user.role,
profilePictureString: user.picture,
gradeStreak: (user.gradeStreak ?? 0) > 1,
),
),
),
@ -179,10 +182,22 @@ class AbsencesPageState extends State<AbsencesPage>
padding: const EdgeInsets.only(left: 8.0),
child: Text(
"Absences".i18n,
style: TextStyle(
color: AppColors.of(context).text,
fontSize: 32.0,
fontWeight: FontWeight.bold),
style: Provider.of<SettingsProvider>(context).fontFamily !=
'' &&
Provider.of<SettingsProvider>(context).titleOnlyFont
? GoogleFonts.getFont(
Provider.of<SettingsProvider>(context).fontFamily,
textStyle: TextStyle(
color: AppColors.of(context).text,
fontSize: 32.0,
fontWeight: FontWeight.bold,
),
)
: TextStyle(
color: AppColors.of(context).text,
fontSize: 32.0,
fontWeight: FontWeight.bold,
),
),
),
bottom: FilterBar(
@ -504,7 +519,9 @@ class AbsencesPageState extends State<AbsencesPage>
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.w700,
color: AppColors.of(context).green,
color: value1 > 0
? AppColors.of(context).green
: AppColors.of(context).text,
),
),
Text(
@ -513,12 +530,14 @@ class AbsencesPageState extends State<AbsencesPage>
fontSize: 16.0,
fontWeight: FontWeight.w500,
height: 1.1,
color: ColorsUtils().fade(
context,
AppColors.of(context).green,
darkenAmount: 0.5,
lightenAmount: 0.4,
),
color: value1 > 0
? ColorsUtils().fade(
context,
AppColors.of(context).green,
darkenAmount: 0.5,
lightenAmount: 0.4,
)
: AppColors.of(context).text,
),
),
],
@ -535,7 +554,9 @@ class AbsencesPageState extends State<AbsencesPage>
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.w700,
color: AppColors.of(context).red,
color: value2 > 0
? AppColors.of(context).red
: AppColors.of(context).text,
),
),
Text(
@ -544,12 +565,14 @@ class AbsencesPageState extends State<AbsencesPage>
fontSize: 16.0,
fontWeight: FontWeight.w500,
height: 1.1,
color: ColorsUtils().fade(
context,
AppColors.of(context).red,
darkenAmount: 0.4,
lightenAmount: 0.2,
),
color: value2 > 0
? ColorsUtils().fade(
context,
AppColors.of(context).red,
darkenAmount: 0.4,
lightenAmount: 0.2,
)
: AppColors.of(context).text,
),
),
],
@ -597,8 +620,9 @@ class AbsencesPageState extends State<AbsencesPage>
children: [
Row(
children: [
const Icon(
Icon(
Icons.av_timer_rounded,
color: value3 > 0 ? Colors.orange : null,
),
const SizedBox(
width: 10.0,

View File

@ -34,7 +34,6 @@ import 'package:refilc_mobile_ui/pages/grades/calculator/grade_calculator_provid
import 'package:refilc_mobile_ui/pages/grades/grades_count.dart';
import 'package:refilc_mobile_ui/pages/grades/graph.dart';
import 'package:refilc_mobile_ui/pages/grades/subject_grades_container.dart';
import 'package:refilc_plus/ui/mobile/goal_planner/goal_planner_screen.dart';
// import 'package:refilc_plus/models/premium_scopes.dart';
// import 'package:refilc_plus/providers/plus_provider.dart';
import 'package:refilc_plus/ui/mobile/goal_planner/goal_state_screen.dart';
@ -44,6 +43,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_expandable_fab/flutter_expandable_fab.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:provider/provider.dart';
import 'package:refilc_plus/ui/mobile/goal_planner/goal_track_popup.dart';
import 'grades_page.i18n.dart';
// import 'package:refilc_plus/ui/mobile/goal_planner/new_goal.dart';
@ -425,9 +425,10 @@ class _GradeSubjectViewState extends State<GradeSubjectView>
// ScaffoldMessenger.of(context).showSnackBar(
// const SnackBar(content: Text("Hamarosan...")));
Navigator.of(context).push(CupertinoPageRoute(
builder: (context) =>
GoalPlannerScreen(subject: widget.subject)));
// Navigator.of(context).push(CupertinoPageRoute(
// builder: (context) =>
// GoalPlannerScreen(subject: widget.subject)));
GoalTrackPopup.show(context, subject: widget.subject);
},
child: const Icon(FeatherIcons.flag, size: 20.0),
),

View File

@ -4,6 +4,7 @@ import 'dart:math';
import 'package:auto_size_text/auto_size_text.dart';
import 'package:collection/collection.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:refilc/api/providers/update_provider.dart';
import 'package:refilc/models/settings.dart';
import 'package:refilc/ui/widgets/grade/grade_tile.dart';
@ -267,6 +268,12 @@ class GradesPageState extends State<GradesPage> {
),
),
),
if (hasHomework &&
nearestExam != null &&
Provider.of<SettingsProvider>(context).qSubjectsSubTiles)
const SizedBox(
height: 6.0,
),
if (nearestExam != null &&
Provider.of<SettingsProvider>(context).qSubjectsSubTiles)
Container(
@ -560,6 +567,7 @@ class GradesPageState extends State<GradesPage> {
badge: updateProvider.available,
role: user.role,
profilePictureString: user.picture,
gradeStreak: (user.gradeStreak ?? 0) > 1,
),
),
),
@ -568,11 +576,21 @@ class GradesPageState extends State<GradesPage> {
title: Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Text(
"Grades".i18n,
style: TextStyle(
color: AppColors.of(context).text,
fontSize: 32.0,
fontWeight: FontWeight.bold),
"page_title_grades".i18n,
style: Provider.of<SettingsProvider>(context).fontFamily !=
'' &&
Provider.of<SettingsProvider>(context).titleOnlyFont
? GoogleFonts.getFont(
Provider.of<SettingsProvider>(context).fontFamily,
textStyle: TextStyle(
color: AppColors.of(context).text,
fontSize: 32.0,
fontWeight: FontWeight.bold),
)
: TextStyle(
color: AppColors.of(context).text,
fontSize: 32.0,
fontWeight: FontWeight.bold),
),
),
shadowColor: Theme.of(context).shadowColor,
@ -667,6 +685,8 @@ class GradesPageState extends State<GradesPage> {
// SoonAlert.show(context: context);
gradeCalcTotal(context);
Navigator.of(context, rootNavigator: true).pop();
},
),
),

View File

@ -31,7 +31,8 @@ extension Localization on String {
"grade_calc": "Grade Calculator",
},
"hu_hu": {
"Grades": "Tantárgyak",
"Grades": "Jegyek",
"page_title_grades": "Tantárgyak",
"Ghost Grades": "Szellem jegyek",
"Subjects": "Tantárgyaid",
"Subjects_changes": "Tantárgyi változások",

View File

@ -248,11 +248,12 @@ class GradeGraphState extends State<GradeGraph> {
),
if (ghostData.isNotEmpty && ghostSpots.isNotEmpty)
LineChartBarData(
preventCurveOverShooting: true,
preventCurveOverShooting: false,
spots: ghostSpots,
isCurved: true,
colors: [AppColors.of(context).text],
barWidth: 8,
barWidth: 6,
curveSmoothness: 0.2,
isStrokeCapRound: true,
dotData: FlDotData(show: false),
belowBarData: BarAreaData(

View File

@ -3,6 +3,7 @@ import 'dart:math';
import 'package:flutter/cupertino.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:refilc/api/providers/live_card_provider.dart';
import 'package:refilc/theme/colors/colors.dart';
import 'package:refilc/ui/date_widget.dart';
@ -244,14 +245,34 @@ class HomePageState extends State<HomePage> with TickerProviderStateMixin {
greeting,
overflow: TextOverflow.fade,
textAlign: TextAlign.start,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18.0,
color: Theme.of(context)
.textTheme
.bodyMedium
?.color,
),
style:
Provider.of<SettingsProvider>(context)
.fontFamily !=
'' &&
Provider.of<SettingsProvider>(
context)
.titleOnlyFont
? GoogleFonts.getFont(
Provider.of<SettingsProvider>(
context)
.fontFamily,
textStyle: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18.0,
color: Theme.of(context)
.textTheme
.bodyMedium
?.color,
),
)
: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18.0,
color: Theme.of(context)
.textTheme
.bodyMedium
?.color,
),
),
Text(
DateFormat('EEEE, MMM d',
@ -317,6 +338,7 @@ class HomePageState extends State<HomePage> with TickerProviderStateMixin {
badge: updateProvider.available,
role: user.role,
profilePictureString: user.picture,
gradeStreak: (user.gradeStreak ?? 0) > 1,
),
),
),
@ -355,7 +377,7 @@ class HomePageState extends State<HomePage> with TickerProviderStateMixin {
LiveCardState.duringLesson ||
_liveCard.currentState ==
LiveCardState.duringBreak)
? 55.0
? 62.0
: 52.0),
),
child: Transform.scale(

View File

@ -404,7 +404,7 @@ class LiveCardStateA extends State<LiveCard> {
swapRoom: true,
currentLessonIndicator: false,
padding:
const EdgeInsets.only(top: 6.0, bottom: 4.0),
const EdgeInsets.only(top: 2.0, bottom: 4.0),
contentPadding: EdgeInsets.zero,
showSubTiles: false,
),
@ -911,6 +911,37 @@ class LiveCardStateA extends State<LiveCard> {
Row(
children: liveCard.nextLesson != null
? [
Container(
width: (liveCard.nextLesson?.room
.length ??
0) >
20
? 111
: null,
padding: const EdgeInsets.symmetric(
horizontal: 5.5, vertical: 3.0),
decoration: BoxDecoration(
color: Theme.of(context)
.colorScheme
.tertiary
.withOpacity(.15),
borderRadius:
BorderRadius.circular(10.0),
),
child: Text(
liveCard.nextLesson!.room,
overflow: TextOverflow.ellipsis,
style: TextStyle(
height: 1.1,
fontSize: 12.0,
fontWeight: FontWeight.w600,
color: Theme.of(context)
.colorScheme
.secondary
.withOpacity(.9),
),
),
),
const SizedBox(
width: 10,
),

View File

@ -1,6 +1,8 @@
import 'dart:math';
import 'package:google_fonts/google_fonts.dart';
import 'package:refilc/api/providers/update_provider.dart';
import 'package:refilc/models/settings.dart';
import 'package:refilc/ui/date_widget.dart';
import 'package:refilc_kreta_api/providers/message_provider.dart';
import 'package:refilc/api/providers/user_provider.dart';
@ -109,6 +111,7 @@ class MessagesPageState extends State<MessagesPage>
badge: updateProvider.available,
role: user.role,
profilePictureString: user.picture,
gradeStreak: (user.gradeStreak ?? 0) > 1,
),
),
),
@ -128,10 +131,22 @@ class MessagesPageState extends State<MessagesPage>
),
Text(
"Messages".i18n,
style: TextStyle(
color: AppColors.of(context).text,
fontSize: 32.0,
fontWeight: FontWeight.bold),
style: Provider.of<SettingsProvider>(context)
.fontFamily !=
'' &&
Provider.of<SettingsProvider>(context)
.titleOnlyFont
? GoogleFonts.getFont(
Provider.of<SettingsProvider>(context).fontFamily,
textStyle: TextStyle(
color: AppColors.of(context).text,
fontSize: 32.0,
fontWeight: FontWeight.bold),
)
: TextStyle(
color: AppColors.of(context).text,
fontSize: 32.0,
fontWeight: FontWeight.bold),
),
],
),

View File

@ -5,6 +5,7 @@ import 'dart:math';
import 'package:flutter/cupertino.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:refilc/api/providers/database_provider.dart';
import 'package:refilc/api/providers/self_note_provider.dart';
import 'package:refilc/api/providers/update_provider.dart';
@ -35,6 +36,7 @@ import 'package:refilc_plus/models/premium_scopes.dart';
import 'package:refilc_plus/providers/plus_provider.dart';
import 'package:refilc_plus/ui/mobile/plus/premium_inline.dart';
import 'package:refilc_plus/ui/mobile/plus/upsell.dart';
import 'package:uuid/uuid.dart';
import 'notes_page.i18n.dart';
enum AbsenceFilter { absences, delays, misses }
@ -65,9 +67,14 @@ class NotesPageState extends State<NotesPage> with TickerProviderStateMixin {
Map<String, bool> doneItems = {};
List<Widget> noteTiles = [];
List<TodoItem> todoItems = [];
final TextEditingController _taskName = TextEditingController();
final TextEditingController _taskContent = TextEditingController();
void generateTiles() async {
doneItems = await databaseProvider.userQuery.toDoItems(userId: user.id!);
todoItems = await databaseProvider.userQuery.getTodoItems(userId: user.id!);
List<Widget> tiles = [];
@ -82,7 +89,7 @@ class NotesPageState extends State<NotesPage> with TickerProviderStateMixin {
List<Widget> toDoTiles = [];
if (hw.isNotEmpty &&
!Provider.of<PlusProvider>(context, listen: false)
Provider.of<PlusProvider>(context, listen: false)
.hasScope(PremiumScopes.unlimitedSelfNotes)) {
toDoTiles.addAll(hw.map((e) => TickTile(
padding: EdgeInsets.zero,
@ -102,6 +109,21 @@ class NotesPageState extends State<NotesPage> with TickerProviderStateMixin {
)));
}
if (selfNoteProvider.todos.isNotEmpty) {
toDoTiles.addAll(selfNoteProvider.todos.map((e) => TickTile(
padding: EdgeInsets.zero,
title: e.title,
description: e.content,
isTicked: e.done,
onTap: (p0) async {
todoItems.firstWhere((element) => element.id == e.id).done = p0;
await databaseProvider.userStore
.storeSelfTodoItems(todoItems, userId: user.id!);
},
)));
}
if (toDoTiles.isNotEmpty) {
tiles.add(const SizedBox(
height: 10.0,
@ -128,26 +150,31 @@ class NotesPageState extends State<NotesPage> with TickerProviderStateMixin {
CupertinoPageRoute(
builder: (context) => NoteViewScreen(note: e))),
)
: Container(
height: MediaQuery.of(context).size.width / 2.42,
width: MediaQuery.of(context).size.width / 2.42,
decoration: BoxDecoration(
boxShadow: [
if (Provider.of<SettingsProvider>(context, listen: false)
.shadowEffect)
BoxShadow(
offset: const Offset(0, 21),
blurRadius: 23.0,
color: Theme.of(context).shadowColor,
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(16.0),
child: Image.memory(
const Base64Decoder().convert(e.content),
fit: BoxFit.cover,
gaplessPlayback: true,
: GestureDetector(
onTap: () => Navigator.of(context, rootNavigator: true).push(
CupertinoPageRoute(
builder: (context) => NoteViewScreen(note: e))),
child: Container(
height: MediaQuery.of(context).size.width / 2.42,
width: MediaQuery.of(context).size.width / 2.42,
decoration: BoxDecoration(
boxShadow: [
if (Provider.of<SettingsProvider>(context, listen: false)
.shadowEffect)
BoxShadow(
offset: const Offset(0, 21),
blurRadius: 23.0,
color: Theme.of(context).shadowColor,
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(16.0),
child: Image.memory(
const Base64Decoder().convert(e.content),
fit: BoxFit.cover,
gaplessPlayback: true,
),
),
),
),
@ -289,6 +316,7 @@ class NotesPageState extends State<NotesPage> with TickerProviderStateMixin {
badge: updateProvider.available,
role: user.role,
profilePictureString: user.picture,
gradeStreak: (user.gradeStreak ?? 0) > 1,
),
),
),
@ -299,10 +327,20 @@ class NotesPageState extends State<NotesPage> with TickerProviderStateMixin {
padding: const EdgeInsets.only(left: 8.0),
child: Text(
"notes".i18n,
style: TextStyle(
color: AppColors.of(context).text,
fontSize: 32.0,
fontWeight: FontWeight.bold),
style: Provider.of<SettingsProvider>(context).fontFamily !=
'' &&
Provider.of<SettingsProvider>(context).titleOnlyFont
? GoogleFonts.getFont(
Provider.of<SettingsProvider>(context).fontFamily,
textStyle: TextStyle(
color: AppColors.of(context).text,
fontSize: 32.0,
fontWeight: FontWeight.bold),
)
: TextStyle(
color: AppColors.of(context).text,
fontSize: 32.0,
fontWeight: FontWeight.bold),
),
),
),
@ -313,6 +351,8 @@ class NotesPageState extends State<NotesPage> with TickerProviderStateMixin {
.fetch(
from: DateTime.now().subtract(const Duration(days: 30)));
Provider.of<SelfNoteProvider>(context, listen: false).restore();
Provider.of<SelfNoteProvider>(context, listen: false)
.restoreTodo();
generateTiles();
@ -410,7 +450,159 @@ class NotesPageState extends State<NotesPage> with TickerProviderStateMixin {
},
),
),
const SizedBox(
height: 10.0,
),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12.0),
color: Theme.of(context).colorScheme.background),
child: ListTile(
title: Row(
children: [
const Icon(Icons.task_outlined),
const SizedBox(
width: 10.0,
),
Text('new_task'.i18n),
],
),
onTap: () {
if (!Provider.of<PlusProvider>(context, listen: false)
.hasScope(PremiumScopes.unlimitedSelfNotes)) {
PlusLockedFeaturePopup.show(
context: context, feature: PremiumFeature.selfNotes);
return;
}
showTaskCreation(context);
},
),
),
]),
);
}
void showTaskCreation(context) {
showDialog(
context: context,
builder: (context) => AlertDialog(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(14.0))),
contentPadding: const EdgeInsets.only(top: 10.0),
title: Text("new_task".i18n),
content: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 10.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: _taskName,
decoration: InputDecoration(
border: OutlineInputBorder(
borderSide:
const BorderSide(color: Colors.grey, width: 1.5),
borderRadius: BorderRadius.circular(12.0),
),
focusedBorder: OutlineInputBorder(
borderSide:
const BorderSide(color: Colors.grey, width: 1.5),
borderRadius: BorderRadius.circular(12.0),
),
contentPadding: const EdgeInsets.symmetric(horizontal: 12.0),
hintText: "task_name".i18n,
suffixIcon: IconButton(
icon: const Icon(
FeatherIcons.x,
color: Colors.grey,
),
onPressed: () {
setState(() {
_taskName.text = "";
});
},
),
),
),
const SizedBox(
height: 10.0,
),
TextField(
controller: _taskContent,
decoration: InputDecoration(
border: OutlineInputBorder(
borderSide:
const BorderSide(color: Colors.grey, width: 1.5),
borderRadius: BorderRadius.circular(12.0),
),
focusedBorder: OutlineInputBorder(
borderSide:
const BorderSide(color: Colors.grey, width: 1.5),
borderRadius: BorderRadius.circular(12.0),
),
contentPadding: const EdgeInsets.symmetric(horizontal: 12.0),
hintText: "task_content".i18n,
suffixIcon: IconButton(
icon: const Icon(
FeatherIcons.x,
color: Colors.grey,
),
onPressed: () {
setState(() {
_taskContent.text = "";
});
},
),
),
),
],
),
),
actions: [
TextButton(
child: Text(
"cancel".i18n,
style: const TextStyle(fontWeight: FontWeight.w500),
),
onPressed: () {
Navigator.of(context).maybePop();
},
),
TextButton(
child: Text(
"next".i18n,
style: const TextStyle(fontWeight: FontWeight.w500),
),
onPressed: () async {
todoItems.add(TodoItem.fromJson({
'id': const Uuid().v4(),
'title': _taskName.text.replaceAll(' ', '') != ""
? _taskName.text
: 'no_title'.i18n,
'content': _taskContent.text,
'done': false,
}));
await databaseProvider.userStore
.storeSelfTodoItems(todoItems, userId: user.id!);
setState(() {
_taskName.text = "";
_taskContent.text = "";
});
Provider.of<SelfNoteProvider>(context, listen: false).restore();
Provider.of<SelfNoteProvider>(context, listen: false)
.restoreTodo();
generateTiles();
Navigator.of(context).pop(true);
},
),
],
),
);
}
}

View File

@ -14,6 +14,10 @@ extension ScreensLocalization on String {
"hint_t": "Note title...",
"your_notes": "Your Notes",
"new_image": "New Image",
"no_title": "No title",
"task_content": "Task content...",
"task_name": "Task title...",
"new_task": "New Task",
},
"hu_hu": {
"notes": "Füzet",
@ -26,6 +30,10 @@ extension ScreensLocalization on String {
"hint_t": "Jegyzet címe...",
"your_notes": "Jegyzeteid",
"new_image": "Új kép",
"no_title": "Nincs cím",
"task_content": "Feladat tartalma...",
"task_name": "Feladat címe...",
"new_task": "Új feladat",
},
"de_de": {
"notes": "Broschüre",
@ -38,6 +46,10 @@ extension ScreensLocalization on String {
"hint_t": "Titel notieren...",
"your_notes": "Deine Noten",
"new_image": "Neues Bild",
"no_title": "Kein Titel",
"task_content": "Aufgabeninhalt...",
"task_name": "Aufgabentitel...",
"new_task": "Neue Aufgabe",
},
};

View File

@ -257,6 +257,8 @@ class AddNoteScreenState extends State<AddNoteScreen> {
style: const TextStyle(fontSize: 16.0),
),
),
if (MediaQuery.of(context).viewInsets.bottom != 0)
const SizedBox(height: 60),
],
),
),

View File

@ -6,6 +6,7 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:image_crop/image_crop.dart';
import 'package:image_picker/image_picker.dart';
import 'package:provider/provider.dart';
@ -27,6 +28,8 @@ class ImageNoteEditor extends StatefulWidget {
}
class _ImageNoteEditorState extends State<ImageNoteEditor> {
final _title = TextEditingController();
final cropKey = GlobalKey<CropState>();
File? _file;
File? _sample;
@ -62,7 +65,7 @@ class _ImageNoteEditorState extends State<ImageNoteEditor> {
child: Crop.file(
_sample!,
key: cropKey,
aspectRatio: 1.0,
// aspectRatio: 1.0,
),
);
}
@ -135,7 +138,8 @@ class _ImageNoteEditorState extends State<ImageNoteEditor> {
selfNotes.add(SelfNote.fromJson({
'id': const Uuid().v4(),
'content': base64Image,
'note_type': 'image'
'note_type': 'image',
'title': _title.text,
}));
await Provider.of<DatabaseProvider>(context, listen: false)
@ -143,6 +147,7 @@ class _ImageNoteEditorState extends State<ImageNoteEditor> {
.storeSelfNotes(selfNotes, userId: widget.u.id);
Provider.of<SelfNoteProvider>(context, listen: false).restore();
Provider.of<SelfNoteProvider>(context, listen: false).restoreTodo();
debugPrint('$file');
}
@ -170,6 +175,37 @@ class _ImageNoteEditorState extends State<ImageNoteEditor> {
const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0),
child: _sample == null ? openImageWidget() : cropImageWidget(),
),
Padding(
padding:
const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0),
child: TextField(
controller: _title,
onEditingComplete: () async {},
decoration: InputDecoration(
border: OutlineInputBorder(
borderSide: const BorderSide(color: Colors.grey, width: 1.5),
borderRadius: BorderRadius.circular(12.0),
),
focusedBorder: OutlineInputBorder(
borderSide: const BorderSide(color: Colors.grey, width: 1.5),
borderRadius: BorderRadius.circular(12.0),
),
contentPadding: const EdgeInsets.symmetric(horizontal: 12.0),
hintText: 'title'.i18n,
suffixIcon: IconButton(
icon: const Icon(
FeatherIcons.x,
color: Colors.grey,
),
onPressed: () {
setState(() {
_title.text = '';
});
},
),
),
),
),
// if (widget.u.picture != "")
// TextButton(
// child: Text(

View File

@ -1,3 +1,5 @@
import 'dart:convert';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:refilc/api/providers/self_note_provider.dart';
import 'package:refilc/models/self_note.dart';
@ -8,6 +10,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:provider/provider.dart';
import 'package:markdown/markdown.dart' as md;
import 'notes_screen.i18n.dart';
class NoteViewScreen extends StatefulWidget {
const NoteViewScreen({super.key, required this.note});
@ -30,7 +33,9 @@ class NoteViewScreenState extends State<NoteViewScreen> {
surfaceTintColor: Theme.of(context).scaffoldBackgroundColor,
leading: BackButton(color: AppColors.of(context).text),
title: Text(
widget.note.title ?? '${widget.note.content.split(' ')[0]}...',
widget.note.noteType == NoteType.text
? (widget.note.title ?? '${widget.note.content.split(' ')[0]}...')
: 'image_note'.i18n,
style: TextStyle(
color: AppColors.of(context).text,
fontSize: 26.0,
@ -38,52 +43,55 @@ class NoteViewScreenState extends State<NoteViewScreen> {
),
),
actions: [
ClipRRect(
borderRadius: BorderRadius.circular(10.1),
child: GestureDetector(
onTap: () {
// handle tap
Navigator.of(context, rootNavigator: true).push(
CupertinoPageRoute(
builder: (context) =>
AddNoteScreen(initialNote: widget.note)));
},
child: Container(
color: Theme.of(context).colorScheme.secondary.withOpacity(0.2),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Stack(
children: [
IconTheme(
data: IconThemeData(
color: Theme.of(context).colorScheme.secondary,
if (widget.note.noteType == NoteType.text)
ClipRRect(
borderRadius: BorderRadius.circular(10.1),
child: GestureDetector(
onTap: () {
// handle tap
Navigator.of(context, rootNavigator: true).push(
CupertinoPageRoute(
builder: (context) =>
AddNoteScreen(initialNote: widget.note)));
},
child: Container(
color:
Theme.of(context).colorScheme.secondary.withOpacity(0.2),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Stack(
children: [
IconTheme(
data: IconThemeData(
color: Theme.of(context).colorScheme.secondary,
),
child: const Icon(
FeatherIcons.edit,
size: 20.0,
),
),
child: const Icon(
FeatherIcons.edit,
size: 20.0,
IconTheme(
data: IconThemeData(
color:
Theme.of(context).brightness == Brightness.light
? Colors.black.withOpacity(.5)
: Colors.white.withOpacity(.3),
),
child: const Icon(
FeatherIcons.edit,
size: 20.0,
),
),
),
IconTheme(
data: IconThemeData(
color:
Theme.of(context).brightness == Brightness.light
? Colors.black.withOpacity(.5)
: Colors.white.withOpacity(.3),
),
child: const Icon(
FeatherIcons.edit,
size: 20.0,
),
),
],
],
),
),
),
),
),
),
const SizedBox(
width: 10,
),
if (widget.note.noteType == NoteType.text)
const SizedBox(
width: 10,
),
ClipRRect(
borderRadius: BorderRadius.circular(10.1),
child: GestureDetector(
@ -140,21 +148,30 @@ class NoteViewScreenState extends State<NoteViewScreen> {
child: Column(
children: [
Expanded(
child: MarkdownBody(
data: widget.note.content,
extensionSet: md.ExtensionSet(
md.ExtensionSet.gitHubFlavored.blockSyntaxes,
<md.InlineSyntax>[
md.EmojiSyntax(),
...md.ExtensionSet.gitHubFlavored.inlineSyntaxes
],
),
styleSheet: MarkdownStyleSheet(
p: const TextStyle(
fontSize: 15.0,
),
),
),
child: widget.note.noteType == NoteType.text
? MarkdownBody(
data: widget.note.content,
extensionSet: md.ExtensionSet(
md.ExtensionSet.gitHubFlavored.blockSyntaxes,
<md.InlineSyntax>[
md.EmojiSyntax(),
...md.ExtensionSet.gitHubFlavored.inlineSyntaxes
],
),
styleSheet: MarkdownStyleSheet(
p: const TextStyle(
fontSize: 15.0,
),
),
)
: ClipRRect(
borderRadius: BorderRadius.circular(10.0),
child: Image.memory(
const Base64Decoder().convert(widget.note.content),
fit: BoxFit.contain,
gaplessPlayback: true,
),
),
),
// Expanded(
// child: Text(

View File

@ -268,6 +268,7 @@ class NotesScreenState extends State<NotesScreen> {
Provider.of<HomeworkProvider>(context, listen: false)
.fetch(from: DateTime.now().subtract(const Duration(days: 30)));
Provider.of<SelfNoteProvider>(context, listen: false).restore();
Provider.of<SelfNoteProvider>(context, listen: false).restoreTodo();
return Future(() => null);
},

View File

@ -18,6 +18,8 @@ extension SettingsLocalization on String {
"click_here": "Click here",
"select_image": "to select an image",
"new_image": "New Image",
"image_note": "Image",
"title": "Image title...",
},
"hu_hu": {
"notes": "Füzet",
@ -34,6 +36,8 @@ extension SettingsLocalization on String {
"click_here": "Kattints ide",
"select_image": "kép kiválasztásához",
"new_image": "Új kép",
"image_note": "Kép",
"title": "Kép címe...",
},
"de_de": {
"notes": "Broschüre",
@ -50,6 +54,8 @@ extension SettingsLocalization on String {
"click_here": "Klicken Sie hier",
"select_image": "um ein Bild auszuwählen",
"new_image": "Neues Bild",
"image_note": "Bild",
"title": "Bildtitel...",
},
};

View File

@ -1,3 +1,6 @@
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart';
import 'package:refilc/models/settings.dart';
import 'package:refilc/theme/colors/colors.dart';
import 'package:flutter/material.dart';
import 'package:refilc/utils/format.dart';
@ -50,11 +53,26 @@ class _DayTitleState extends State<DayTitle> {
width: MediaQuery.of(context).size.width / 1.5,
child: Text(
widget.dayTitle(index).capital(),
style: TextStyle(
color:
AppColors.of(context).text.withOpacity(opacity),
fontSize: 32.0,
fontWeight: FontWeight.bold),
style: Provider.of<SettingsProvider>(context)
.fontFamily !=
'' &&
Provider.of<SettingsProvider>(context)
.titleOnlyFont
? GoogleFonts.getFont(
Provider.of<SettingsProvider>(context).fontFamily,
textStyle: TextStyle(
color: AppColors.of(context)
.text
.withOpacity(opacity),
fontSize: 32.0,
fontWeight: FontWeight.bold),
)
: TextStyle(
color: AppColors.of(context)
.text
.withOpacity(opacity),
fontSize: 32.0,
fontWeight: FontWeight.bold),
),
);
},

View File

@ -1,5 +1,6 @@
import 'dart:math';
import 'package:animations/animations.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:i18n_extension/i18n_extension.dart';
import 'package:refilc/api/providers/database_provider.dart';
import 'package:refilc/api/providers/update_provider.dart';
@ -19,7 +20,7 @@ import 'package:refilc_mobile_ui/common/empty.dart';
import 'package:refilc_mobile_ui/common/profile_image/profile_button.dart';
import 'package:refilc_mobile_ui/common/profile_image/profile_image.dart';
import 'package:refilc_mobile_ui/common/system_chrome.dart';
import 'package:refilc_mobile_ui/common/widgets/lesson/lesson_view.dart';
// import 'package:refilc_mobile_ui/common/widgets/lesson/lesson_view.dart';
import 'package:refilc_kreta_api/controllers/timetable_controller.dart';
import 'package:refilc_mobile_ui/common/widgets/lesson/lesson_viewable.dart';
import 'package:refilc_mobile_ui/pages/timetable/day_title.dart';
@ -60,7 +61,11 @@ class TimetablePage extends StatefulWidget {
NavigationScreen.of(context)?.setPage("timetable");
// Show initial Lesson
if (lesson != null) LessonView.show(lesson, context: context);
// if (lesson != null) LessonView.show(lesson, context: context);
// changed to new popup
if (lesson != null) {
TimetableLessonPopup.show(context: context, lesson: lesson);
}
}
@override
@ -316,6 +321,7 @@ class TimetablePageState extends State<TimetablePage>
badge: updateProvider.available,
role: user.role,
profilePictureString: user.picture,
gradeStreak: (user.gradeStreak ?? 0) > 1,
),
),
),
@ -374,11 +380,25 @@ class TimetablePageState extends State<TimetablePage>
} else {
return Text(
"timetable".i18n,
style: TextStyle(
fontSize: 32.0,
fontWeight: FontWeight.bold,
color: AppColors.of(context).text,
),
style: Provider.of<SettingsProvider>(context)
.fontFamily !=
'' &&
Provider.of<SettingsProvider>(context)
.titleOnlyFont
? GoogleFonts.getFont(
Provider.of<SettingsProvider>(context)
.fontFamily,
textStyle: TextStyle(
fontSize: 32.0,
fontWeight: FontWeight.bold,
color: AppColors.of(context).text,
),
)
: TextStyle(
fontSize: 32.0,
fontWeight: FontWeight.bold,
color: AppColors.of(context).text,
),
);
}
}(),

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:refilc/models/settings.dart';
import 'package:refilc_plus/providers/plus_provider.dart';
import 'package:refilc_plus/ui/mobile/plus/activation_view/activation_view.dart';
import 'package:refilc_mobile_ui/plus/plus_screen.i18n.dart';
@ -50,6 +51,20 @@ class PlusPlanCard extends StatelessWidget {
return;
}
if (Provider.of<SettingsProvider>(context, listen: false).xFilcId ==
"none") {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text(
"Be kell kapcsolnod a Névtelen Analitikát a beállítások főoldalán, mielőtt reFilc+ előfizetést vásárolnál!",
style:
TextStyle(color: Colors.black, fontWeight: FontWeight.bold),
),
backgroundColor: Colors.white,
));
return;
}
if (Provider.of<PlusProvider>(context, listen: false).hasPremium) {
if (!active) {
launchUrl(

View File

@ -225,6 +225,7 @@ class PlusScreenState extends State<PlusScreen> {
['🎓', 'rfp_6'.i18n],
['👕', 'rfp_14'.i18n],
['👑', 'rfp_15'.i18n],
['📩', 'rfp_17'.i18n],
['🔜', 'more_soon'.i18n],
],
docsAccepted: docsAccepted,

View File

@ -45,6 +45,7 @@ extension SettingsLocalization on String {
"rfp_14": "Discount in reFilc Shop (soon)",
"rfp_15": "Subscriber role in our Discord community",
"rfp_16": "Private leaks and informations about upcoming features",
"rfp_17": "Grade exporting",
// other
"and": " and ",
"every": "Every ",
@ -96,6 +97,7 @@ extension SettingsLocalization on String {
"rfp_14": "Kedvezmény a reFilc Shop-ban (hamarosan)",
"rfp_15": "Előfizetői rang a Discord szerverünkön",
"rfp_16": "Privát betekintések és információk közelgő újításokról",
"rfp_17": "Jegy exportálás",
// other
"and": " és ",
"every": "Minden ",
@ -133,30 +135,32 @@ extension SettingsLocalization on String {
"Der Preis wird in Euro angegeben im Bezug zum aktuellen Wechselkurs. 1 EUR ≈ 390 HUF",
"active": "Aktiv",
// benefits
"rfp_1": "Előzetes hozzáférés új verziókhoz",
"rfp_2": "2 fiók használata egyszerre",
"rfp_3": "Egyedi üdvözlő üzenet",
"rfp_4": "Korlátlan saját jegyzet és feladat a füzet oldalon",
"rfp_5": "Egyedi jegy ritkaságok",
"rfp_6": "Összesített átlagszámoló",
"rfp_7": "Órarend jegyzetek",
"rfp_8": "Egyedi betütípusok",
"rfp_9": "Korlátlan fiók használata egyszerre",
"rfp_10": "Alkalmazás ikonjának megváltoztatása (v5.1-től)",
"rfp_11": "Live Activity szín",
"rfp_12": "Fejlettebb cél kitűzés",
"rfp_13": "Naptár szinkronizálás",
"rfp_14": "Kedvezmény a reFilc Shop-ban (hamarosan)",
"rfp_15": "Előfizetői rang a Discord szerverünkön",
"rfp_16": "Privát betekintések és információk közelgő újításokról",
"rfp_1": "Frühzeitiger Zugriff auf Updates",
"rfp_2": "Verwendung von zwei Konten gleichzeitig",
"rfp_3": "Individuelle Begrüßungsnachricht",
"rfp_4":
"Unbegrenzte eigene Notizen und Aufgaben auf der Notizbuchseite",
"rfp_5": "Individuelle Notenraritäten",
"rfp_6": "Gesamtdurchschnittsberechner",
"rfp_7": "Stundenplan-Notizen",
"rfp_8": "Individuelle Schriftarten",
"rfp_9": "Unbegrenzte Konten",
"rfp_10": "Anpassung des App-Symbols (ab v5.1)",
"rfp_11": "Änderung der Live-Aktivitätsfarbe",
"rfp_12": "Verbesserter Zielplaner",
"rfp_13": "Importieren Sie Ihren Stundenplan in Ihre Kalender-App",
"rfp_14": "Rabatt im reFilc Shop (bald)",
"rfp_15": "Abonnentenrolle in unserer Discord-Community",
"rfp_16": "Private Leaks und Informationen über kommende Funktionen",
"rfp_17": "Notenexport",
// other
"and": " és ",
"every": "Minden ",
"benefit": " előny",
"and": " und ",
"every": "Jeder ",
"benefit": " Vorteil",
"show_lifetime": "Für immer Pakete",
"more_soon": "Mehr folgt bald...",
"faq_dc":
"Az előnyök beváltásához írj nekünk Discord-on privát üzenetet!",
"Um Ihre Vorteile einzulösen, schreiben Sie uns eine private Nachricht auf Discord!",
"reactivate": "Bestehendes Abonnement reaktivieren",
},
};

View File

@ -0,0 +1,179 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:refilc_kreta_api/client/api.dart';
import 'package:refilc_kreta_api/client/client.dart';
import 'package:webview_flutter/webview_flutter.dart';
class KretenLoginScreen extends StatefulWidget {
const KretenLoginScreen({super.key});
@override
State<KretenLoginScreen> createState() => _KretenLoginScreenState();
}
class _KretenLoginScreenState extends State<KretenLoginScreen> {
late final WebViewController controller;
var loadingPercentage = 0;
var currentUrl = '';
@override
void initState() {
super.initState();
controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setNavigationDelegate(NavigationDelegate(
onPageStarted: (url) async {
setState(() {
loadingPercentage = 0;
currentUrl = url;
});
List<String> requiredThings = url
.replaceAll(
'https://mobil.e-kreta.hu/ellenorzo-student/prod/oauthredirect?code=',
'')
.replaceAll(
'&scope=openid email offline_access kreta-ellenorzo-webapi.public kreta-eugyintezes-webapi.public kreta-fileservice-webapi.public kreta-mobile-global-webapi.public kreta-dkt-webapi.public kreta-ier-webapi.public&state=refilc_student_mobile&session_state=',
':')
.split(':');
String code = requiredThings[0];
// String sessionState = requiredThings[1];
debugPrint('url: $url');
// actual login (token grant) logic
Map<String, String> headers = {
"content-type": "application/x-www-form-urlencoded",
"accept": "*/*",
"user-agent":
"eKretaStudent/264745 CFNetwork/1494.0.7 Darwin/23.4.0",
"code_verifier": "THDUSddKOOndwCkqBtVHvRjh2LK0V2kMyLP2QirqVWQ",
};
Map? res = await Provider.of<KretaClient>(context, listen: false)
.postAPI(KretaAPI.login, headers: headers, body: {
"code": code,
"redirect_uri":
"https://mobil.e-kreta.hu/ellenorzo-student/prod/oauthredirect",
"client_id": "kreta-ellenorzo-student-mobile-ios",
"grant_type": "authorization_code",
});
if (res != null) {
print(res);
// if (res.containsKey("error")) {
// if (res["error"] == "invalid_grant") {
// print("ERROR: invalid_grant");
// return;
// }
// } else {
// if (res.containsKey("access_token")) {
// try {
// Provider.of<KretaClient>(context, listen: false).accessToken =
// res["access_token"];
// Map? studentJson =
// await Provider.of<KretaClient>(context, listen: false)
// .getAPI(KretaAPI.student(instituteCode));
// Student student = Student.fromJson(studentJson!);
// var user = User(
// username: username,
// password: password,
// instituteCode: instituteCode,
// name: student.name,
// student: student,
// role: JwtUtils.getRoleFromJWT(res["access_token"])!,
// );
// if (onLogin != null) onLogin(user);
// // Store User in the database
// await Provider.of<DatabaseProvider>(context, listen: false)
// .store
// .storeUser(user);
// Provider.of<UserProvider>(context, listen: false)
// .addUser(user);
// Provider.of<UserProvider>(context, listen: false)
// .setUser(user.id);
// // Get user data
// try {
// await Future.wait([
// Provider.of<GradeProvider>(context, listen: false)
// .fetch(),
// Provider.of<TimetableProvider>(context, listen: false)
// .fetch(week: Week.current()),
// Provider.of<ExamProvider>(context, listen: false).fetch(),
// Provider.of<HomeworkProvider>(context, listen: false)
// .fetch(),
// Provider.of<MessageProvider>(context, listen: false)
// .fetchAll(),
// Provider.of<MessageProvider>(context, listen: false)
// .fetchAllRecipients(),
// Provider.of<NoteProvider>(context, listen: false).fetch(),
// Provider.of<EventProvider>(context, listen: false)
// .fetch(),
// Provider.of<AbsenceProvider>(context, listen: false)
// .fetch(),
// ]);
// } catch (error) {
// print("WARNING: failed to fetch user data: $error");
// }
// if (onSuccess != null) onSuccess();
// return LoginState.success;
// } catch (error) {
// print("ERROR: loginAPI: $error");
// // maybe check debug mode
// // ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("ERROR: $error")));
// return LoginState.failed;
// }
// }
// }
}
},
onProgress: (progress) {
setState(() {
loadingPercentage = progress;
});
},
onPageFinished: (url) {
setState(() {
loadingPercentage = 100;
});
},
))
..loadRequest(
Uri.parse(
'https://idp.e-kreta.hu/connect/authorize?prompt=login&nonce=refilc&response_type=code&code_challenge_method=S256&scope=openid%20email%20offline_access%20kreta-ellenorzo-webapi.public%20kreta-eugyintezes-webapi.public%20kreta-fileservice-webapi.public%20kreta-mobile-global-webapi.public%20kreta-dkt-webapi.public%20kreta-ier-webapi.public&code_challenge=Oj_aVMRJHYsv00mrtGJY72NJa7HY54lVnU2Cb4CWbWw&redirect_uri=https://mobil.e-kreta.hu/ellenorzo-student/prod/oauthredirect&client_id=kreta-ellenorzo-student-mobile-ios&state=refilc_student_mobile'),
);
}
// Future<void> loadLoginUrl() async {
// String nonceStr = await Provider.of<KretaClient>(context, listen: false)
// .getAPI(KretaAPI.nonce, json: false);
// Nonce nonce = getNonce(nonceStr, );
// }
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: const BackButton(),
title: const Text('e-KRÉTA Bejelentkezés'),
),
body: Stack(
children: [
WebViewWidget(
controller: controller,
),
if (loadingPercentage < 100)
LinearProgressIndicator(
value: loadingPercentage / 100.0,
),
],
),
);
}
}

View File

@ -56,6 +56,13 @@ class AccountView extends StatelessWidget {
Detail(
title: "parents".plural(user.student.parents.length),
description: user.student.parents.join(", ")),
const SizedBox(
height: 10.0,
),
// Detail(
// title: "parents".i18n,
// description: user.student.parents.join(", ")),
Detail(title: "school".i18n, description: user.student.school.name),
],
),
);

View File

@ -9,6 +9,7 @@ extension Localization on String {
"class": "Class",
"address": "Home address",
"parents": "Parents".one("Parent"),
"parents_phone": "Parents' phone number: ".one("Parent"),
},
"hu_hu": {
"birthdate": "Születési dátum",

View File

@ -37,6 +37,9 @@ import 'package:flutter_material_color_picker/flutter_material_color_picker.dart
import 'package:refilc/models/icon_pack.dart';
import 'package:refilc/utils/format.dart';
import 'package:refilc_mobile_ui/screens/settings/theme_screen.dart';
import 'package:refilc_plus/models/premium_scopes.dart';
import 'package:refilc_plus/providers/plus_provider.dart';
import 'package:refilc_plus/ui/mobile/plus/upsell.dart';
// import 'package:refilc_plus/models/premium_scopes.dart';
// import 'package:refilc_plus/providers/plus_provider.dart';
// import 'package:refilc_plus/ui/mobile/plus/upsell.dart';
@ -900,6 +903,15 @@ class _LiveActivityColorSettingState extends State<LiveActivityColorSetting> {
allowShades: false,
selectedColor: settings.liveActivityColor,
onMainColorChange: (k) {
if (!Provider.of<PlusProvider>(context, listen: false)
.hasScope(PremiumScopes.liveActivityColor)) {
PlusLockedFeaturePopup.show(
context: context,
feature: PremiumFeature.liveActivity,
);
return;
}
setState(() {
currentColor = k as Color;
settings.update(

View File

@ -27,6 +27,7 @@ import 'package:refilc_mobile_ui/common/bottom_sheet_menu/bottom_sheet_menu.dart
import 'package:refilc_mobile_ui/common/panel/panel.dart';
import 'package:refilc_mobile_ui/common/panel/panel_button.dart';
import 'package:refilc_mobile_ui/common/profile_image/profile_image.dart';
import 'package:refilc_mobile_ui/common/soon_alert/soon_alert.dart';
// import 'package:refilc_mobile_ui/common/soon_alert/soon_alert.dart';
import 'package:refilc_mobile_ui/common/splitted_panel/splitted_panel.dart';
// import 'package:refilc_mobile_ui/common/system_chrome.dart';
@ -320,6 +321,7 @@ class SettingsScreenState extends State<SettingsScreen>
badge: updateProvider.available,
role: user.role,
profilePictureString: user.picture,
gradeStreak: (user.gradeStreak ?? 0) > 1,
backgroundColor: Theme.of(context)
.colorScheme
.tertiary, //!settings.presentationMode
@ -702,6 +704,46 @@ class SettingsScreenState extends State<SettingsScreen>
],
),
if ((user.gradeStreak ?? 0) > 1)
SplittedPanel(
padding: const EdgeInsets.only(
bottom: 12.0, left: 24.0, right: 24.0),
children: [
GestureDetector(
onTap: () {
SoonAlert.show(context: context);
},
child: ListTile(
title: Text(
"grade_streak".i18n,
style: TextStyle(
color: AppColors.of(context).text.withOpacity(0.95),
fontWeight: FontWeight.w500,
),
),
subtitle: Text(
"grade_streak_subtitle".i18n,
style: TextStyle(
color: AppColors.of(context).text.withOpacity(0.75),
),
),
leading: const Text(
"🔥",
style: TextStyle(fontSize: 22.0),
),
trailing: Text(
"${user.gradeStreak}",
style: TextStyle(
color: AppColors.of(context).text.withOpacity(0.95),
fontWeight: FontWeight.w500,
fontSize: 18.0,
),
),
),
),
],
),
// plus subscribe inline
const PlusSettingsInline(),

View File

@ -116,6 +116,14 @@ extension SettingsLocalization on String {
"exp_settings": "Export Settings",
"manage_subs": "Manage Subscription",
"copy_plus_id": "Copy reFilc+ ID",
// grade streak
"grade_streak": "Grade 5 Streak",
"grade_streak_subtitle": "So many 5s in a row?!",
// other
"only_ch_title_font": "Font Only for Titles",
"new_popups": "New Popups",
"export_method": "Export Method",
"grade_exporting": "Grade Exporting",
},
"hu_hu": {
"personal_details": "Személyes információk",
@ -230,6 +238,14 @@ extension SettingsLocalization on String {
"exp_settings": "Beállítások exportálása",
"manage_subs": "Előfizetés kezelése",
"copy_plus_id": "reFilc+ ID másolása",
// grade streak
"grade_streak": "5-ös sorozat",
"grade_streak_subtitle": "Egymás után ennyi 5-ös?!",
// other
"only_ch_title_font": "Betűtípus csak címekre",
"new_popups": "Új felugró ablakok",
"export_method": "Exportálási mód",
"grade_exporting": "Jegy exportálás",
},
"de_de": {
"personal_details": "Persönliche Angaben",
@ -344,6 +360,14 @@ extension SettingsLocalization on String {
"exp_settings": "Einstellungen exportieren",
"manage_subs": "Abonnement verwalten",
"copy_plus_id": "reFilc+ ID kopieren",
// grade streak
"grade_streak": "5er-Streak",
"grade_streak_subtitle": "So viele 5er in Folge?!",
// other
"only_ch_title_font": "Schriftart nur für Titel",
"new_popups": "Neue Popups",
"export_method": "Exportmethode",
"grade_exporting": "Noten exportieren",
},
};

View File

@ -10,6 +10,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:provider/provider.dart';
import 'package:refilc_plus/ui/mobile/settings/submenu/calendar_sync.dart';
import 'package:refilc_plus/ui/mobile/settings/submenu/grade_exporting.dart';
import 'package:refilc_plus/models/premium_scopes.dart';
import 'package:refilc_plus/providers/plus_provider.dart';
import 'package:refilc_plus/ui/mobile/plus/upsell.dart';
@ -158,6 +159,16 @@ class ExtrasSettingsScreenState extends State<ExtrasSettingsScreen> {
),
],
),
SplittedPanel(
padding: const EdgeInsets.only(top: 9.0),
cardPadding: const EdgeInsets.all(4.0),
isSeparated: true,
children: [
MenuGradeExporting(
borderRadius: BorderRadius.circular(12.0),
),
],
),
],
),
),

View File

@ -0,0 +1,381 @@
import 'package:refilc/models/settings.dart';
import 'package:refilc/theme/colors/colors.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:refilc/ui/flutter_colorpicker/colorpicker.dart';
import 'package:refilc/ui/widgets/grade/grade_tile.dart';
import 'package:refilc_kreta_api/models/grade.dart';
import 'package:refilc_mobile_ui/screens/settings/theme_screen.dart';
import 'submenu_screen.i18n.dart';
enum SelectedGrade { one, two, three, four, five }
class GradeColorsSettingsScreen extends StatefulWidget {
const GradeColorsSettingsScreen({super.key});
@override
GradeColorsSettingsScreenState createState() =>
GradeColorsSettingsScreenState();
}
class GradeColorsSettingsScreenState extends State<GradeColorsSettingsScreen> {
late SettingsProvider settingsProvider;
SelectedGrade currentEditGrade = SelectedGrade.one;
@override
Widget build(BuildContext context) {
SettingsProvider settingsProvider = Provider.of<SettingsProvider>(context);
return Scaffold(
appBar: AppBar(
surfaceTintColor: Theme.of(context).scaffoldBackgroundColor,
leading: BackButton(
color: AppColors.of(context).text,
onPressed: () {
setState(() {
// made this cuz else it will be ugly
currentEditGrade = SelectedGrade.one;
});
Navigator.of(context).pop();
},
),
title: Text(
"grade_colors".i18n,
style: TextStyle(color: AppColors.of(context).text),
),
actions: [
IconButton(
onPressed: () {
List<Color> colors = List.castFrom(settingsProvider.gradeColors);
var defaultColors =
SettingsProvider.defaultSettings().gradeColors;
colors[currentEditGrade.index] =
defaultColors[currentEditGrade.index];
settingsProvider.update(gradeColors: colors);
},
icon: const Icon(
Icons.restore,
size: 26.0,
),
),
const SizedBox(
width: 10.0,
),
],
),
body: SizedBox(
width: double.infinity,
height: double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
children: [
SizedBox(
height: MediaQuery.of(context).size.height * 0.14,
),
Stack(
children: [
Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background,
borderRadius: BorderRadius.circular(75.0),
boxShadow: [
BoxShadow(
color: Theme.of(context)
.colorScheme
.shadow
.withOpacity(.1),
blurRadius: 10.0,
offset: const Offset(0, 5),
),
],
),
padding: const EdgeInsets.all(6.0),
child: GradeValueWidget(
GradeValue(currentEditGrade.index + 1, '', '', 100),
fill: true,
size: 75.0,
// color:
// settingsProvider.gradeColors[currentEditGrade.index],
),
),
// before grades
if (currentEditGrade.index > 0)
Transform.translate(
offset: const Offset(-110, 16.5),
child: GradeValueWidget(
GradeValue(currentEditGrade.index, '', '', 100),
fill: true,
size: 60.0,
// color:
// settingsProvider.gradeColors[currentEditGrade.index],
),
),
if (currentEditGrade.index > 1)
Transform.translate(
offset: const Offset(-200, 23),
child: GradeValueWidget(
GradeValue(currentEditGrade.index - 1, '', '', 100),
fill: true,
size: 50.0,
// color:
// settingsProvider.gradeColors[currentEditGrade.index],
),
),
// after grades
if (currentEditGrade.index < 4)
Transform.translate(
offset: const Offset(142, 16.5),
child: GradeValueWidget(
GradeValue(currentEditGrade.index + 2, '', '', 100),
fill: true,
size: 60.0,
// color:
// settingsProvider.gradeColors[currentEditGrade.index],
),
),
if (currentEditGrade.index < 3)
Transform.translate(
offset: const Offset(245, 23),
child: GradeValueWidget(
GradeValue(currentEditGrade.index + 3, '', '', 100),
fill: true,
size: 50.0,
// color:
// settingsProvider.gradeColors[currentEditGrade.index],
),
),
],
),
SizedBox(
height: MediaQuery.of(context).size.height * 0.14,
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: SafeArea(
child: FilcColorPicker(
colorMode: CustomColorMode.grade,
pickerColor:
settingsProvider.gradeColors[currentEditGrade.index],
onColorChanged: (c) {
setState(() {
// update grade color
settingsProvider.update(
gradeColors: settingsProvider.gradeColors
..[currentEditGrade.index] = c);
});
},
onColorChangeEnd: (c, {adaptive}) {
// update grade color
},
onThemeIdProvided: (t) {},
),
),
),
Padding(
padding: const EdgeInsets.only(bottom: 20.0, top: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
GestureDetector(
onTap: () => setState(() {
currentEditGrade = SelectedGrade.one;
}),
child: Container(
width: 45.0,
height: 45.0,
decoration: BoxDecoration(
border: Border.all(
color: currentEditGrade == SelectedGrade.one
? Theme.of(context).colorScheme.secondary
: Theme.of(context)
.colorScheme
.secondary
.withOpacity(.2),
width: 1.0,
),
borderRadius: BorderRadius.circular(50.0),
color: currentEditGrade == SelectedGrade.one
? Theme.of(context).colorScheme.secondary
: null,
),
child: Center(
child: Text(
'1',
style: TextStyle(
fontSize: 14.0,
fontWeight: FontWeight.w600,
color: currentEditGrade == SelectedGrade.one
? Colors.white
: null,
),
),
),
),
),
const SizedBox(width: 10.0),
GestureDetector(
onTap: () => setState(() {
currentEditGrade = SelectedGrade.two;
}),
child: Container(
width: 45.0,
height: 45.0,
decoration: BoxDecoration(
border: Border.all(
color: currentEditGrade == SelectedGrade.two
? Theme.of(context).colorScheme.secondary
: Theme.of(context)
.colorScheme
.secondary
.withOpacity(.2),
width: 1.0,
),
borderRadius: BorderRadius.circular(50.0),
color: currentEditGrade == SelectedGrade.two
? Theme.of(context).colorScheme.secondary
: null,
),
child: Center(
child: Text(
'2',
style: TextStyle(
fontSize: 14.0,
fontWeight: FontWeight.w600,
color: currentEditGrade == SelectedGrade.two
? Colors.white
: null,
),
),
),
),
),
const SizedBox(width: 10.0),
GestureDetector(
onTap: () => setState(() {
currentEditGrade = SelectedGrade.three;
}),
child: Container(
width: 45.0,
height: 45.0,
decoration: BoxDecoration(
border: Border.all(
color: currentEditGrade == SelectedGrade.three
? Theme.of(context).colorScheme.secondary
: Theme.of(context)
.colorScheme
.secondary
.withOpacity(.2),
width: 1.0,
),
borderRadius: BorderRadius.circular(50.0),
color: currentEditGrade == SelectedGrade.three
? Theme.of(context).colorScheme.secondary
: null,
),
child: Center(
child: Text(
'3',
style: TextStyle(
fontSize: 14.0,
fontWeight: FontWeight.w600,
color: currentEditGrade == SelectedGrade.three
? Colors.white
: null,
),
),
),
),
),
const SizedBox(width: 10.0),
GestureDetector(
onTap: () => setState(() {
currentEditGrade = SelectedGrade.four;
}),
child: Container(
width: 45.0,
height: 45.0,
decoration: BoxDecoration(
border: Border.all(
color: currentEditGrade == SelectedGrade.four
? Theme.of(context).colorScheme.secondary
: Theme.of(context)
.colorScheme
.secondary
.withOpacity(.2),
width: 1.0,
),
borderRadius: BorderRadius.circular(50.0),
color: currentEditGrade == SelectedGrade.four
? Theme.of(context).colorScheme.secondary
: null,
),
child: Center(
child: Text(
'4',
style: TextStyle(
fontSize: 14.0,
fontWeight: FontWeight.w600,
color: currentEditGrade == SelectedGrade.four
? Colors.white
: null,
),
),
),
),
),
const SizedBox(width: 10.0),
GestureDetector(
onTap: () => setState(() {
currentEditGrade = SelectedGrade.five;
}),
child: Container(
width: 45.0,
height: 45.0,
decoration: BoxDecoration(
border: Border.all(
color: currentEditGrade == SelectedGrade.five
? Theme.of(context).colorScheme.secondary
: Theme.of(context)
.colorScheme
.secondary
.withOpacity(.2),
width: 1.0,
),
borderRadius: BorderRadius.circular(50.0),
color: currentEditGrade == SelectedGrade.five
? Theme.of(context).colorScheme.secondary
: null,
),
child: Center(
child: Text(
'5',
style: TextStyle(
fontSize: 14.0,
fontWeight: FontWeight.w600,
color: currentEditGrade == SelectedGrade.five
? Colors.white
: null,
),
),
),
),
),
],
),
),
],
),
],
),
),
);
}
}

View File

@ -16,6 +16,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:provider/provider.dart';
import 'package:refilc_mobile_ui/screens/settings/settings_screen.i18n.dart';
import 'package:refilc_mobile_ui/screens/settings/submenu/share_theme_popup.dart';
import 'package:share_plus/share_plus.dart';
class MenuPaintList extends StatelessWidget {
@ -251,18 +252,7 @@ class PaintListScreenState extends State<PaintListScreen>
subject: 'share_subj_theme'.i18n,
);
} else {
SharedGradeColors gradeColors = await shareProvider
.shareCurrentGradeColors(context);
SharedTheme theme =
await shareProvider.shareCurrentTheme(
context,
gradeColors: gradeColors,
);
Share.share(
theme.id,
subject: 'share_subj_theme'.i18n,
);
ShareThemeDialog.show(context);
}
},
longPressInstead: true,

View File

@ -30,6 +30,8 @@ import 'package:refilc_plus/providers/plus_provider.dart';
import 'package:refilc_plus/ui/mobile/plus/upsell.dart';
import 'package:google_fonts/google_fonts.dart';
import 'grade_colors.dart';
class MenuPersonalizeSettings extends StatelessWidget {
const MenuPersonalizeSettings({
super.key,
@ -459,6 +461,49 @@ class PersonalizeSettingsScreenState extends State<PersonalizeSettingsScreen>
),
],
),
// new popup toggle
SplittedPanel(
padding: const EdgeInsets.only(top: 9.0),
cardPadding: const EdgeInsets.all(4.0),
isSeparated: true,
children: [
PanelButton(
padding: const EdgeInsets.only(left: 14.0, right: 6.0),
onPressed: () async {
settingsProvider.update(
newPopups: !settingsProvider.newPopups);
setState(() {});
},
title: Text(
"new_popups".i18n,
style: TextStyle(
color: AppColors.of(context).text.withOpacity(
settingsProvider.newPopups ? .95 : .25),
),
),
leading: Icon(
FeatherIcons.alertOctagon,
size: 22.0,
color: AppColors.of(context).text.withOpacity(
settingsProvider.newPopups ? .95 : .25),
),
trailing: Switch(
onChanged: (v) async {
settingsProvider.update(newPopups: v);
setState(() {});
},
value: settingsProvider.newPopups,
activeColor: Theme.of(context).colorScheme.secondary,
),
borderRadius: const BorderRadius.vertical(
top: Radius.circular(12.0),
bottom: Radius.circular(12.0),
),
),
],
),
// change subject icons
// SplittedPanel(
// padding: const EdgeInsets.only(top: 9.0),
@ -499,8 +544,14 @@ class PersonalizeSettingsScreenState extends State<PersonalizeSettingsScreen>
children: [
PanelButton(
onPressed: () {
SettingsHelper.gradeColors(context);
setState(() {});
// SettingsHelper.gradeColors(context);
// setState(() {});
Navigator.of(context, rootNavigator: true).push(
CupertinoPageRoute(
builder: (context) =>
const GradeColorsSettingsScreen(),
),
);
},
title: Text(
"grade_colors".i18n,
@ -670,16 +721,6 @@ class PersonalizeSettingsScreenState extends State<PersonalizeSettingsScreen>
children: [
PanelButton(
onPressed: () {
if (!Provider.of<PlusProvider>(context,
listen: false)
.hasScope(PremiumScopes.liveActivityColor)) {
PlusLockedFeaturePopup.show(
context: context,
feature: PremiumFeature.liveActivity,
);
return;
}
SettingsHelper.liveActivityColor(context);
setState(() {});
},
@ -882,7 +923,7 @@ class PersonalizeSettingsScreenState extends State<PersonalizeSettingsScreen>
title: Text('fonts'.i18n),
padding: EdgeInsets.zero,
cardPadding: const EdgeInsets.all(4.0),
isSeparated: true,
isSeparated: false,
children: [
PanelButton(
onPressed: () {
@ -920,11 +961,72 @@ class PersonalizeSettingsScreenState extends State<PersonalizeSettingsScreen>
),
borderRadius: const BorderRadius.vertical(
top: Radius.circular(12.0),
bottom: Radius.circular(6.0),
),
),
PanelButton(
padding: const EdgeInsets.only(left: 14.0, right: 6.0),
onPressed: () async {
if (!Provider.of<PlusProvider>(context, listen: false)
.hasScope(PremiumScopes.customFont)) {
PlusLockedFeaturePopup.show(
context: context,
feature: PremiumFeature.fontChange);
return;
}
settingsProvider.update(
titleOnlyFont: !settingsProvider.titleOnlyFont);
Provider.of<ThemeModeObserver>(context, listen: false)
.changeTheme(settingsProvider.theme,
updateNavbarColor: false);
setState(() {});
},
title: Text(
"only_ch_title_font".i18n,
style: TextStyle(
color: AppColors.of(context).text.withOpacity(
settingsProvider.titleOnlyFont ? .95 : .25),
),
),
leading: Icon(
Icons.text_increase_rounded,
size: 22.0,
color: AppColors.of(context).text.withOpacity(
settingsProvider.titleOnlyFont ? .95 : .25),
),
trailing: Switch(
onChanged: (v) async {
if (!Provider.of<PlusProvider>(context,
listen: false)
.hasScope(PremiumScopes.customFont)) {
PlusLockedFeaturePopup.show(
context: context,
feature: PremiumFeature.fontChange);
return;
}
settingsProvider.update(titleOnlyFont: v);
Provider.of<ThemeModeObserver>(context,
listen: false)
.changeTheme(settingsProvider.theme,
updateNavbarColor: false);
setState(() {});
},
value: settingsProvider.titleOnlyFont,
activeColor: Theme.of(context).colorScheme.secondary,
),
borderRadius: const BorderRadius.vertical(
top: Radius.circular(4.0),
bottom: Radius.circular(12.0),
),
),
],
),
// bottom padding
const SizedBox(
height: 20.0,
),
],
),
),

View File

@ -0,0 +1,160 @@
// ignore_for_file: use_build_context_synchronously, deprecated_member_use
import 'package:flutter/material.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:provider/provider.dart';
// import 'package:refilc/models/settings.dart';
import 'package:refilc/models/shared_theme.dart';
import 'package:refilc_kreta_api/providers/share_provider.dart';
import 'package:refilc_mobile_ui/common/action_button.dart';
import 'package:refilc_mobile_ui/common/splitted_panel/splitted_panel.dart';
import 'package:share_plus/share_plus.dart';
import 'submenu_screen.i18n.dart';
class ShareThemeDialog extends StatefulWidget {
const ShareThemeDialog({super.key});
static void show(BuildContext context) {
showDialog(
context: context,
builder: (context) => AlertDialog(
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
title: Text("attention".i18n),
content: Text("share_disclaimer".i18n),
actions: [
ActionButton(
label: "understand".i18n,
onTap: () async {
Navigator.of(context).pop();
showDialog(
context: context,
builder: (context) => const ShareThemeDialog());
},
),
],
),
);
}
@override
ShareThemeDialogState createState() => ShareThemeDialogState();
}
class ShareThemeDialogState extends State<ShareThemeDialog> {
final _title = TextEditingController();
bool isPublic = false;
late ShareProvider shareProvider;
@override
void initState() {
super.initState();
shareProvider = Provider.of<ShareProvider>(context, listen: false);
}
@override
Widget build(BuildContext context) {
return AlertDialog(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(14.0))),
contentPadding: const EdgeInsets.only(top: 10.0),
title: Text("share_theme".i18n),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding:
const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0),
child: TextField(
controller: _title,
onEditingComplete: () async {},
decoration: InputDecoration(
border: OutlineInputBorder(
borderSide: const BorderSide(color: Colors.grey, width: 1.5),
borderRadius: BorderRadius.circular(12.0),
),
focusedBorder: OutlineInputBorder(
borderSide: const BorderSide(color: Colors.grey, width: 1.5),
borderRadius: BorderRadius.circular(12.0),
),
contentPadding: const EdgeInsets.symmetric(horizontal: 12.0),
hintText: 'paint_title'.i18n,
suffixIcon: IconButton(
icon: const Icon(
FeatherIcons.x,
color: Colors.grey,
),
onPressed: () {
setState(() {
_title.text = '';
});
},
),
),
),
),
SplittedPanel(
children: [
SwitchListTile(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.0),
),
value: isPublic,
onChanged: (value) {
setState(() {
isPublic = value;
});
},
title: Text("is_public".i18n),
contentPadding: const EdgeInsets.only(left: 15.0, right: 10.0),
),
],
),
],
),
actions: [
TextButton(
child: Text(
"cancel".i18n,
style: const TextStyle(fontWeight: FontWeight.w500),
),
onPressed: () {
Navigator.of(context).maybePop();
},
),
TextButton(
child: Text(
"share_it".i18n,
style: const TextStyle(fontWeight: FontWeight.w500),
),
onPressed: () async {
// share the fucking theme
SharedGradeColors gradeColors =
await shareProvider.shareCurrentGradeColors(context);
SharedTheme theme = await shareProvider.shareCurrentTheme(
context,
gradeColors: gradeColors,
isPublic: isPublic,
displayName: _title.text,
);
// save theme id in settings
// Provider.of<SettingsProvider>(context, listen: false)
// .update(currentThemeId: theme.id);
// close this popup shit
Navigator.of(context).pop(true);
// show the share popup
Share.share(
theme.id,
subject: 'share_subj_theme'.i18n,
);
},
),
],
);
}
}

View File

@ -17,6 +17,17 @@ extension SettingsLocalization on String {
"rare": "Rare",
"epic": "Epic",
"legendary": "Legendary",
// grade colors
"grade_colors": "Grade Colors",
// theme share popup
"share_theme": "Share Paint",
"paint_title": "Paint title...",
"share_it": "Share it!",
"is_public": "Public Paint",
"attention": "Attention!",
"share_disclaimer":
"By sharing the theme, you agree that the nickname you set and all settings of the theme will be shared publicly.",
"understand": "I understand",
},
"hu_hu": {
"general": "Általános",
@ -32,6 +43,17 @@ extension SettingsLocalization on String {
"rare": "Ritka",
"epic": "Epikus",
"legendary": "Legendás",
// grade colors
"grade_colors": "Jegyek színei",
// theme share popup
"share_theme": "Téma megosztása",
"paint_title": "Téma neve...",
"share_it": "Megosztás!",
"is_public": "Nyilvános téma",
"attention": "Figyelem!",
"share_disclaimer":
"A téma megosztásával elfogadod, hogy az általad beállított becenév és a téma minden beállítása nyilvánosan megosztásra kerüljön.",
"understand": "Értem",
},
"de_de": {
"general": "Allgemeine",
@ -47,6 +69,17 @@ extension SettingsLocalization on String {
"rare": "Selten",
"epic": "Episch",
"legendary": "Legendär",
// grade colors
"grade_colors": "Notenfarben",
// theme share popup
"share_theme": "Thema teilen",
"paint_title": "Thema Titel...",
"share_it": "Teilen!",
"is_public": "Öffentliches Thema",
"attention": "Achtung!",
"share_disclaimer":
"Durch das Teilen des Themes erklären Sie sich damit einverstanden, dass der von Ihnen festgelegte Spitzname und alle Einstellungen des Themes öffentlich geteilt werden.",
"understand": "Ich verstehe",
},
};

View File

@ -1,7 +1,7 @@
// ignore_for_file: use_build_context_synchronously, deprecated_member_use
import 'package:refilc/models/settings.dart';
import 'package:refilc/models/shared_theme.dart';
// import 'package:refilc/models/shared_theme.dart';
import 'package:refilc/theme/colors/accent.dart';
import 'package:refilc/theme/colors/colors.dart';
import 'package:refilc/theme/observer.dart';
@ -10,7 +10,7 @@ import 'package:refilc/ui/widgets/message/message_tile.dart';
import 'package:refilc_kreta_api/models/grade.dart';
import 'package:refilc_kreta_api/models/homework.dart';
import 'package:refilc_kreta_api/models/message.dart';
import 'package:refilc_mobile_ui/common/action_button.dart';
// import 'package:refilc_mobile_ui/common/action_button.dart';
import 'package:refilc_mobile_ui/common/filter_bar.dart';
import 'package:refilc_mobile_ui/common/panel/panel.dart';
import 'package:refilc_mobile_ui/common/widgets/grade/new_grades.dart';
@ -24,6 +24,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:provider/provider.dart';
import 'package:refilc_mobile_ui/screens/settings/submenu/share_theme_popup.dart';
import 'theme_screen.i18n.dart';
import 'package:share_plus/share_plus.dart';
@ -44,6 +45,7 @@ enum CustomColorMode {
text,
icon,
enterId,
grade,
}
class _PremiumCustomAccentColorSettingState
@ -158,6 +160,9 @@ class _PremiumCustomAccentColorSettingState
case CustomColorMode.enterId:
// do nothing here lol
break;
case CustomColorMode.grade:
// do nothing here as well
break;
}
}
@ -218,6 +223,9 @@ class _PremiumCustomAccentColorSettingState
settings.update(customAccentColor: accent, store: store);
settings.update(customIconColor: icon, store: store);
break;
case CustomColorMode.grade:
// do nothing
break;
}
}
@ -297,40 +305,48 @@ class _PremiumCustomAccentColorSettingState
// ),
// ),
// );
showDialog(
context: context,
builder: (context) => WillPopScope(
onWillPop: () async => false,
child: AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0)),
title: Text("attention".i18n),
content: Text("share_disclaimer".i18n),
actions: [
ActionButton(
label: "understand".i18n,
onTap: () async {
Navigator.of(context).pop();
// showDialog(
// context: context,
// builder: (context) => WillPopScope(
// onWillPop: () async => false,
// child: AlertDialog(
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(12.0)),
// title: Text("attention".i18n),
// content: Text("share_disclaimer".i18n),
// actions: [
// ActionButton(
// label: "understand".i18n,
// onTap: () async {
// Navigator.of(context).pop();
SharedGradeColors gradeColors =
await shareProvider
.shareCurrentGradeColors(context);
SharedTheme theme =
await shareProvider.shareCurrentTheme(
context,
gradeColors: gradeColors,
);
// SharedGradeColors gradeColors =
// await shareProvider
// .shareCurrentGradeColors(context);
// SharedTheme theme =
// await shareProvider.shareCurrentTheme(
// context,
// gradeColors: gradeColors,
// );
Share.share(
theme.id,
subject: 'share_subj_theme'.i18n,
);
},
),
],
),
),
);
// Share.share(
// theme.id,
// subject: 'share_subj_theme'.i18n,
// );
// },
// ),
// ],
// ),
// ),
// );
if (settings.currentThemeId != '') {
Share.share(
settings.currentThemeId,
subject: 'share_subj_theme'.i18n,
);
} else {
ShareThemeDialog.show(context);
}
},
icon: const Icon(
FeatherIcons.share2,

View File

@ -71,6 +71,7 @@ dependencies:
markdown: ^7.2.2
carousel_slider: ^4.2.1
flutter_portal: ^1.1.4
webview_flutter: ^4.8.0
dev_dependencies:
flutter_lints: ^3.0.1

@ -1 +1 @@
Subproject commit 1f5cca7b8e2ac896155a6c494e79fb057628379e
Subproject commit 9bd46b81f230cd094787e1436cd5e8cdee7b5529